【问题标题】:Serialize encrypted password to XML将加密密码序列化为 XML
【发布时间】:2017-03-04 18:07:40
【问题描述】:

我有一个包含用户库的应用程序,该用户库存储在 xml 文件中并在程序开始时加载。为此,我使用 QXmlStreamWriter/Reader。当我尝试序列化加密(散列?)形式的密码(为此使用 QCryptographicHash 和 Sha256)时,会出现问题。

QCryptographicHash返回QByteArray,可以转换成QString(使用QXmlStreamWriter/Reader需要)。相关代码如下。在序列化之前一切正常(我可以登录),但是当我从 xml 读取数据时,在找到散列密码后,该函数的行为就像它找到了 EOF,并且 QXmlStreamReader 仅将大约 2 个字符加载到 QString。

在代码中请忽略预订等(这是一个电影院面板),相关片段是密码,我提供完整的功能以防万一。

我希望我解释了问题所在,这是我的代码片段(注意:在添加散列之前一切正常)

注册函数(散列,pass是一个QString):

QString hash = QCryptographicHash::hash(pass.toUtf8(), QCryptographicHash::Sha256);

User* user_pointer;
user_pointer = new User(name, hash, admin);

编写函数:

QFile file("users/users.xml");
if(!file.open(QIODevice::WriteOnly))
    throw "Error podczas otwierania bazy użytkowników!";


    QXmlStreamWriter writer (&file);

    writer.setAutoFormatting(true);
    writer.writeStartDocument();
    writer.writeStartElement("USERS");
    int list_size = userList.size();

    for(int i = 0; i < list_size; i++)
    {
        writer.writeStartElement("USER");
        writer.writeTextElement("name", userList.at(i)->name);
        writer.writeTextElement("pass", userList.at(i)->password);
        writer.writeTextElement("admin", QString::number(userList.at(i)->is_admin));
        writer.writeStartElement("RESERVATIONS");
        for(int m = 0; m < userList.at(i)->reservList.size(); m++)
        {
            writer.writeStartElement("reservation");
            writer.writeTextElement("moviename", userList.at(i)->reservList.at(m)->movie_name);
            writer.writeTextElement("date", userList.at(i)->reservList.at(m)->date.toString("dd.MM.yyyy"));
            writer.writeTextElement("hour", (userList.at(i)->reservList.at(m)->hour).toString("hhmm"));
            writer.writeTextElement("paid", QString::number(userList.at(i)->reservList.at(m)->paid));
            for(int n = 0; n < userList.at(i)->reservList.at(m)->placeList.size(); n++)
                writer.writeTextElement("place", QString::number(userList.at(i)->reservList.at(m)->placeList.at(n)));
            writer.writeEndElement();
        }
        writer.writeEndElement();
        writer.writeEndElement();
    }
    writer.writeEndDocument();


  file.close();

}

阅读功能:

QFile file("users/users.xml");
if(!file.open(QIODevice::ReadOnly))
    throw "Brak bazy danych użytkowników lub błąd jej otworzenia!";

QXmlStreamReader reader;

reader.setDevice(&file);
reader.readNext();

QString user_name;
QString user_pass;
bool admin;
QString movie_name;
QTime hour;
QDate date;
bool paid;

User* user_pointer = NULL;
int user_counter = -1;
Reservation* reserv_pointer = NULL;
int reserv_counter = -1;

while(!reader.atEnd())
{
    if(reader.isStartElement())
    {
        if(reader.name() == "USER")
        {
            reserv_counter = -1;

        }

        if(reader.name() == "name")
            user_name = reader.readElementText();
        if(reader.name() == "pass")
            user_pass = reader.readElementText();
        if(reader.name() == "admin")
        {
            admin = reader.readElementText().toInt();
            user_pointer = new User(user_name, user_pass, admin);
            userList.append(user_pointer);

            user_counter++;
        }
        if(reader.name() == "reservation")
        {
            reserv_counter++;
        }
        if(reader.name() == "moviename")
            movie_name = reader.readElementText();
        if(reader.name() == "hour")
            hour = QTime::fromString(reader.readElementText(), "hhmm");
        if(reader.name() == "date")
            date = QDate::fromString(reader.readElementText(), "dd.MM.yyyy");
        if(reader.name() == "paid")
        {
            paid = reader.readElementText().toInt();
            reserv_pointer = new Reservation(movie_name, date, hour, paid);
            userList.at(user_counter)->reservList.append(reserv_pointer);
        }
        if(reader.name() == "place")
          {
            userList.at(user_counter)->reservList.at(reserv_counter)->placeList.append(reader.readElementText().toInt());

          }

        reader.readNextStartElement();
    }
    else
        reader.readNext();
}

file.close();
}

【问题讨论】:

    标签: c++ xml qt encryption


    【解决方案1】:

    哈希值不是字符串,它是任意字节值的序列,其中一些在将其解释为字符串时可能会出现问题。

    您有一个从 QByteArrayQString 的隐式转换,文档说明:

    使用 fromUtf8() 函数将字节数组转换为 Unicode。 此函数在找到的第一个 NUL 字符处停止转换,或者 ba 字节数组的结尾。

    例如,您可以使用指定长度的显式转换:

    QString::fromUtf8(byteArray.data(), length);
    

    正如 Frank Osterfeld 在 cmets 中指出的那样,使用 UTF8 并不是一个好主意,我已经针对我正在从事的一个项目从和到 Latin1 进行了广泛的测试,二进制数据是相同的,但是在文本中看起来“时髦”表单,可能无法很好地与 XML 读写配合,toHex() 将通过将字符集限制为 0-F 来解决这个问题:

    QByteArray b; // hash
    QString ss = QString::fromLatin1(b.toHex()); // to QString
    b = QByteArray::fromHex(ss.toLatin1()); // back to QByteArray
    

    【讨论】:

    • 如果二进制数据不是有效的 utf-8,这将不起作用,因为这里可能就是这种情况。我会使用 QString::fromLatin1(byteArray.toHex())。 toHex() 的替代方法是 toBase64()
    • 我还没有测试过 UTF8 的 from/to,它似乎可以与 Latin1 一起使用。我实际上会选择二进制格式而不是 XML。
    【解决方案2】:

    您的问题是 XML 是文本格式,但加密密码是二进制格式。两者不兼容。您需要有某种方法以文本友好的格式对二进制数据进行编码。

    正如@ddriver 所提到的,一种方法是使用QByteArray::toHex(),因为它将所有字节转换为人类可读的文本字符。然而,代价是大小增加了 100%(密码哈希的每个字节返回 2 个字符)

    另一种更普遍、更高效的以文本形式传输二进制数据的方法是使用QByteArray::toBase64()。虽然它还以文本形式返回二进制数据,但大小仅增加了 33 1/3%(每 3 个字节的密码哈希返回 4 个字节)。

    (您可能会认出这种编码,因为它通常以一两个 = 字符结尾的无意义文本,并且是用于在电子邮件中传输二进制数据的常用编码)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-09
      • 2013-06-27
      • 2017-07-24
      • 2011-06-24
      • 2011-02-18
      • 1970-01-01
      • 2023-04-07
      相关资源
      最近更新 更多