You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt5与mkpasswd生成的MD5哈希不一致,如何生成匹配的哈希?

嘿,我来帮你搞清楚这个问题~

你现在遇到的核心问题是:Qt里用QCryptographicHash::Md5生成的是纯MD5哈希,而mkpasswd -s -5生成的是MD5crypt格式的加盐哈希,这俩根本不是同一种东西,所以结果自然不一样。

先说说两者的区别

  • 你用Qt写的代码,是直接对字符串"test"做MD5运算,得到的是原始MD5的十六进制结果,没有加盐,也没有遵循特定的哈希格式。
  • 而mkpasswd -5生成的是Unix系统常用的MD5crypt密码哈希,格式是$1$<salt>$<hashed_value>:
    • $1$是MD5crypt的标识
    • <salt>是随机生成的8位盐(你的例子里是xCs.somY)
    • <hashed_value>是密码加盐后经过MD5变种算法运算得到的结果,还使用了自定义的Base64编码格式

怎么在Qt里生成和mkpasswd一致的结果?

Qt没有直接提供MD5crypt的API,所以我们需要自己实现这个算法。下面是完整的实现代码,你可以直接用:

#include <QCryptographicHash>
#include <QRandomGenerator>
#include <QString>

QString md5Crypt(const QString& password, const QString& salt = QString()) {
    // MD5crypt专用的字符编码集,和标准Base64不同
    const QByteArray charSet = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    const int maxSaltLen = 8;

    // 处理盐:如果没传就随机生成8位,传了就取前8位
    QByteArray usedSalt = salt.toLatin1();
    if (usedSalt.isEmpty()) {
        usedSalt.reserve(maxSaltLen);
        for (int i = 0; i < maxSaltLen; ++i) {
            usedSalt.append(charSet.at(QRandomGenerator::global()->bounded(charSet.size())));
        }
    } else {
        usedSalt = usedSalt.left(maxSaltLen);
    }

    QByteArray passBytes = password.toLatin1();

    // 第一步:计算 MD5(password + "$" + salt)
    QCryptographicHash hash1(QCryptographicHash::Md5);
    hash1.addData(passBytes);
    hash1.addData("$");
    hash1.addData(usedSalt);
    QByteArray hash1Res = hash1.result();

    // 第二步:计算 MD5(password + salt + password)
    QCryptographicHash hash2(QCryptographicHash::Md5);
    hash2.addData(passBytes);
    hash2.addData(usedSalt);
    hash2.addData(passBytes);
    QByteArray hash2Res = hash2.result();

    // 第三步:生成混合初始数据
    QByteArray temp;
    temp.reserve(passBytes.size());
    for (int i = 0; i < passBytes.size(); ++i) {
        temp.append(hash1Res.at(i % 16));
    }
    while (temp.size() < passBytes.size()) {
        temp.append('\0');
    }

    // 第四步:循环混合密码和初始数据
    QByteArray hash3Input = passBytes;
    int len = passBytes.size();
    while (len > 0) {
        hash3Input.append((len & 1) ? temp : passBytes);
        len >>= 1;
    }
    QCryptographicHash hash3(QCryptographicHash::Md5);
    hash3.addData(hash3Input);
    QByteArray hash3Res = hash3.result();

    // 第五步:重复1000次混合运算(MD5crypt的核心步骤)
    for (int i = 0; i < 1000; ++i) {
        QCryptographicHash roundHash(QCryptographicHash::Md5);
        // 根据奇偶次选择输入内容
        if (i & 1) {
            roundHash.addData(passBytes);
        } else {
            roundHash.addData(hash3Res);
        }
        // 根据模3、模7决定是否加盐或密码
        if (i % 3) roundHash.addData(usedSalt);
        if (i % 7) roundHash.addData(passBytes);
        // 再次根据奇偶选择输入
        if (i & 1) {
            roundHash.addData(hash3Res);
        } else {
            roundHash.addData(passBytes);
        }
        hash3Res = roundHash.result();
    }

    // 第六步:将哈希结果转换成MD5crypt的编码格式
    QString result = "$1$" + QString(usedSalt) + "$";
    // MD5crypt指定的字节提取顺序
    const int posOrder[] = {0, 6, 12, 1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 5, 11};

    // 每3个字节一组,编码成4个字符
    for (int i = 0; i < 16; ) {
        int val = (hash3Res.at(posOrder[i]) & 0xff) << 16 
                | (hash3Res.at(posOrder[i+1]) & 0xff) << 8 
                | (hash3Res.at(posOrder[i+2]) & 0xff);
        i += 3;
        for (int j = 0; j < 4; ++j) {
            result.append(charSet.at(val & 0x3f));
            val >>= 6;
        }
    }
    // 处理最后剩下的1个字节
    int finalVal = hash3Res.at(posOrder[15]) & 0xff;
    result.append(charSet.at(finalVal & 0x3f));
    result.append(charSet.at((finalVal >> 6) & 0x3f));

    // 截断到标准MD5crypt长度($1$ + 盐 + $ + 22个字符)
    result = result.left(3 + usedSalt.size() + 1 + 22);
    return result;
}

调用示例(匹配你的mkpasswd结果)

要得到和echo test | mkpasswd -s -5完全一致的结果,你需要指定相同的盐xCs.somY:

QString pass = "test";
QString hash = md5Crypt(pass, "xCs.somY");
qDebug() << hash; // 输出:$1$xCs.somY$b/VXeBeO9DZbqCR/31iPT1

如果不指定盐,函数会随机生成一个8位盐,每次生成的哈希盐部分不同,但都是合法的MD5crypt格式。

内容的提问来源于stack exchange,提问作者Šerg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:48:01