ccy
6 天以前 5e1e4a4b57e298806dba97175fa1fbd5c771c7cd
新的加密方式
5个文件已修改
309 ■■■■■ 已修改文件
licensedata.cpp 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
licensedata.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mainwindow.cpp 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
shortcrypto.cpp 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
shortcrypto.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
licensedata.cpp
@@ -1,5 +1,7 @@
#include "licensedata.h"
#include <QDataStream>
#include <QCryptographicHash>
#include <QRandomGenerator>
#include <QtDebug>
LicenseData::LicenseData()
@@ -105,6 +107,116 @@
        .arg(activeState);
}
// ===== V3 紧凑格式(20位许可密钥用)=====
QByteArray LicenseData::machineFingerprint(const QByteArray &uuid, const QByteArray &cpu, const QByteArray &key)
{
    // 与toBinary相同的定长规整,保证授权端和验证端对同一台机器算出同一指纹
    QByteArray u = uuid.left(16);
    u.append(QByteArray(16 - u.size(), '\0'));
    QByteArray c = cpu.left(8);
    c.append(QByteArray(8 - c.size(), '\0'));
    QCryptographicHash hash(QCryptographicHash::Sha256);
    hash.addData(key);
    hash.addData(u);
    hash.addData(c);
    return hash.result().left(8);
}
quint16 LicenseData::crc16(const QByteArray &data)
{
    // CRC16-CCITT (0xFFFF 初值,0x1021 多项式)
    quint16 crc = 0xFFFF;
    for (int i = 0; i < data.size(); ++i) {
        crc ^= static_cast<quint16>(static_cast<unsigned char>(data.at(i))) << 8;
        for (int b = 0; b < 8; ++b) {
            crc = (crc & 0x8000) ? static_cast<quint16>((crc << 1) ^ 0x1021)
                                 : static_cast<quint16>(crc << 1);
        }
    }
    return crc;
}
QByteArray LicenseData::toBinaryV3(const QByteArray &key) const
{
    QByteArray data(16, '\0');
    // [0..7] 机器指纹
    QByteArray fp = machineFingerprint(boardUuid, cpuSerial, key);
    memcpy(data.data(), fp.constData(), 8);
    // [8..9] 失效日期(与V2同一2000-01-01基准)
    quint16 days = dateToDays(expireDate);
    data[8]  = static_cast<char>((days >> 8) & 0xFF);
    data[9]  = static_cast<char>(days & 0xFF);
    // [10] 产品号
    data[10] = static_cast<char>(productId);
    // [11] 版本:高4位主版本 | 低4位次版本
    data[11] = static_cast<char>(((verMajor & 0x0F) << 4) | (verMinor & 0x0F));
    // [12] 标志:高4位格式版本(1),低4位激活状态
    data[12] = static_cast<char>(0x10 | (activeState & 0x0F));
    // [13..14] CRC16(覆盖[0..12],随机盐不参与)
    quint16 crc = crc16(data.left(13));
    data[13] = static_cast<char>((crc >> 8) & 0xFF);
    data[14] = static_cast<char>(crc & 0xFF);
    // [15] 随机盐
    data[15] = static_cast<char>(QRandomGenerator::global()->generate() & 0xFF);
    return data;
}
bool LicenseData::fromBinaryV3(const QByteArray &data)
{
    if (data.size() != 16) {
        return false;
    }
    quint8 flags = static_cast<quint8>(data.at(12));
    if ((flags >> 4) != 0x01) {
        qWarning() << "LicenseData::fromBinaryV3: not a V3 payload, format version ="
                   << (flags >> 4);
        return false;
    }
    quint16 expectCrc = static_cast<quint16>((static_cast<quint8>(data.at(13)) << 8)
                                             | static_cast<quint8>(data.at(14)));
    if (crc16(data.left(13)) != expectCrc) {
        qWarning() << "LicenseData::fromBinaryV3: CRC check failed, wrong key or corrupted data";
        return false;
    }
    machineFp = data.left(8);
    quint16 days = static_cast<quint16>((static_cast<quint8>(data.at(8)) << 8)
                                        | static_cast<quint8>(data.at(9)));
    expireDate = daysToDate(days);
    productId = static_cast<quint8>(data.at(10));
    quint8 ver = static_cast<quint8>(data.at(11));
    verMajor = ver >> 4;
    verMinor = ver & 0x0F;
    activeState = flags & 0x0F;
    return true;
}
QString LicenseData::toDebugStringV3() const
{
    return QString("{FP:%1, Expire:%2, Prod:%3, Ver:%4.%5, State:%6}")
        .arg(binaryToHexString(machineFp))
        .arg(expireDate.toString("yyyy-MM-dd"))
        .arg(productId)
        .arg(verMajor).arg(verMinor)
        .arg(activeState);
}
quint16 LicenseData::dateToDays(const QDate &date) const
{
    QDate base(BASE_YEAR, BASE_MONTH, BASE_DAY);
licensedata.h
@@ -38,6 +38,31 @@
    bool fromBinary(const QByteArray &data);
    QString toDebugString() const;
    /**
     * @brief V3 紧凑格式(28位纯大写许可密钥用,恰好16字节)
     *
     * 二进制布局(大端序):
     *  [0..7]    8B  机器指纹 = SHA256(密钥‖UUID(16B)‖CPU(8B)) 截断前8字节
     *  [8..9]    2B  失效日期(距2000-01-01的天数,uint16,与V2同基准)
     *  [10]      1B  产品号
     *  [11]      1B  版本号(高4位主版本 | 低4位次版本)
     *  [12]      1B  标志(高4位格式版本=1,低4位激活状态)
     *  [13..14]  2B  CRC16-CCITT(覆盖[0..12])
     *  [15]      1B  随机盐(避免同机同日期密文完全相同)
     *
     * 与V2的区别:UUID/CPU不再明文随密钥携带,只带不可逆指纹;
     * 设备ID仍用V2格式(需要还原机器信息),仅许可密钥用V3。
     */
    QByteArray toBinaryV3(const QByteArray &key) const;
    bool fromBinaryV3(const QByteArray &data);
    QString toDebugStringV3() const;
    QByteArray machineFp;    // V3解出的机器指纹(8字节);V2中为空
    // 机器指纹:授权端与验证端必须用同一算法、同一密钥
    static QByteArray machineFingerprint(const QByteArray &uuid, const QByteArray &cpu, const QByteArray &key);
    static quint16 crc16(const QByteArray &data);
    // 辅助转换
    static QByteArray uuidStringToBinary(const QString &uuidStr);
    static QString    uuidBinaryToString(const QByteArray &bin);
mainwindow.cpp
@@ -279,7 +279,6 @@
    QString encryptlocalid = ui->textEdit_localid->toPlainText();
    // 一键解密
    lic2.fromBinary(ShortCrypto::decrypt(encryptlocalid, key));
    qDebug()<<"on_pushButton_decrypt_clicked"<<lic2.toDebugString();
    //与之前的加密对应,且去掉冗余信息
    localinfo.Baseboard_Uuid = LicenseData::uuidBinaryToString(lic2.boardUuid);
    localinfo.CPU_Processorid = LicenseData::binaryToHexString(lic2.cpuSerial);
@@ -536,13 +535,18 @@
    lic2.expireDate = QDate::fromString(licensedatestr,"yyyy-MM-dd");
//    lic2.expireDate = lic2.expireDate.addMonths(time);
    lic2.activeState = 1;
    QString cipher = ShortCrypto::encrypt(lic2.toBinary(), key);
    // V3紧凑格式(28位纯大写):机器信息以指纹形式绑定
    QString cipher = ShortCrypto::encryptRaw(lic2.toBinaryV3(key), key);
    ui->textEdit_licensekey->clear();
    ui->textEdit_licensekey->setPlainText(cipher);
    // 一键解密
    // 加密后自检
    LicenseData lic3;
    lic3.fromBinary(ShortCrypto::decrypt(cipher, key));
    qDebug()<<"change"<<cipher<<lic3.toDebugString()<<cipher.size();
    if (lic3.fromBinaryV3(ShortCrypto::decryptRaw(cipher, key))) {
        qDebug() << "V3自检通过:" << lic3.toDebugStringV3();
    } else {
        qWarning() << "V3自检失败";
    }
    //更新licenseinfo,只需要更新LicensedDuration_end、LicensedDuration_start、ActiveState
    licenseinfo.LicensedDuration_end = newlicensedate.toString("yyyy-MM-dd");
    licenseinfo.LicensedDuration_start = licensedate.toString("yyyy-MM-dd");
@@ -791,7 +795,7 @@
//    ui->textEdit_licensekey->setPlainText(getEncrypt(QString(testid),key));
    lic2.productId = ui->lineEdit_setproid->text().toUInt();
    QString cipher = ShortCrypto::encrypt(lic2.toBinary(), key);
    QString cipher = ShortCrypto::encryptRaw(lic2.toBinaryV3(key), key);
    ui->textEdit_licensekey->setPlainText(cipher);
//    setLicenseinfo(licensekey_Obj);
@@ -817,7 +821,7 @@
    QString version = ui->lineEdit_setversion->text();
    lic2.verMajor = version.split(".").at(0).toUInt();
    lic2.verMinor = version.split(".").at(1).toUInt();
    QString cipher = ShortCrypto::encrypt(lic2.toBinary(), key);
    QString cipher = ShortCrypto::encryptRaw(lic2.toBinaryV3(key), key);
    ui->textEdit_licensekey->setPlainText(cipher);
    //這裡也只需要更新Versionid
    licenseinfo.Versionid = ui->lineEdit_setversion->text();
shortcrypto.cpp
@@ -1,10 +1,16 @@
#include "shortcrypto.h"
#include "tinyaes.h"
#include <QtDebug>
#include <QtMath>
#include <algorithm>
// Base85 字符集:85个可打印字符,避开容易在URL/JSON里出问题的字符
const char *ShortCrypto::BASE85_CHARS =
    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
// V3 字符集:纯大写字母(26字符),许可密钥只含 A-Z,28位
const char *ShortCrypto::V3_CHARS =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
namespace {
    QByteArray pkcs7Pad(const QByteArray &data, int blockSize) {
@@ -105,6 +111,129 @@
    return pkcs7Unpad(decrypted);
}
QString ShortCrypto::encryptRaw(const QByteArray &plaintext, const QByteArray &key)
{
    if (key.size() != 16) {
        qWarning() << "ShortCrypto::encryptRaw: key must be 16 bytes for AES-128";
        return QString();
    }
    if (plaintext.isEmpty() || plaintext.size() % 16 != 0) {
        qWarning() << "ShortCrypto::encryptRaw: plaintext size must be a non-zero multiple of 16, got"
                   << plaintext.size();
        return QString();
    }
    // 单块时全零IV的CBC等价于ECB;多块时为CBC链接
    QByteArray adjustedIv(16, '\0');
    QByteArray encrypted = plaintext;
    struct AES_ctx ctx;
    AES_init_ctx_iv(&ctx, reinterpret_cast<const uint8_t*>(key.constData()),
                    reinterpret_cast<const uint8_t*>(adjustedIv.constData()));
    AES_CBC_encrypt_buffer(&ctx, reinterpret_cast<uint8_t*>(encrypted.data()), encrypted.size());
    // 纯大写字母编码:16字节密文 → 28字符(若允许数字换36字符集,长度降到25)
    QByteArray encoded = baseNEncode(encrypted, V3_CHARS, 26);
    return QString::fromLatin1(encoded);
}
QByteArray ShortCrypto::decryptRaw(const QString &ciphertext, const QByteArray &key)
{
    if (key.size() != 16) {
        qWarning() << "ShortCrypto::decryptRaw: key must be 16 bytes for AES-128";
        return QByteArray();
    }
    // 优先按纯字母表解码;若含表外字符(早期Base85版V3密钥)则回退Base85
    QByteArray encrypted = baseNDecode(ciphertext, V3_CHARS, 26);
    if (encrypted.isEmpty()) {
        encrypted = base85Decode(ciphertext);
    }
    if (encrypted.isEmpty()) {
        qWarning() << "ShortCrypto::decryptRaw: decode failed";
        return QByteArray();
    }
    if (encrypted.size() % 16 != 0) {
        qWarning() << "ShortCrypto::decryptRaw: decoded data size not aligned to block size:"
                   << encrypted.size();
        return QByteArray();
    }
    QByteArray adjustedIv(16, '\0');
    QByteArray decrypted = encrypted;
    struct AES_ctx ctx;
    AES_init_ctx_iv(&ctx, reinterpret_cast<const uint8_t*>(key.constData()),
                    reinterpret_cast<const uint8_t*>(adjustedIv.constData()));
    AES_CBC_decrypt_buffer(&ctx, reinterpret_cast<uint8_t*>(decrypted.data()), decrypted.size());
    return decrypted;
}
QByteArray ShortCrypto::baseNEncode(const QByteArray &data, const char *alphabet, int base)
{
    // 把整段大端字节序当作一个大整数,逐次除以base取余数
    QByteArray num = data;
    QByteArray remainders;
    bool isZero = false;
    while (!isZero) {
        isZero = true;
        unsigned int carry = 0;
        for (int i = 0; i < num.size(); ++i) {
            unsigned int cur = (carry << 8) | static_cast<unsigned char>(num.at(i));
            num[i] = static_cast<char>(cur / static_cast<unsigned int>(base));
            carry = cur % static_cast<unsigned int>(base);
            if (num.at(i) != 0) isZero = false;
        }
        if (isZero && remainders.isEmpty()) break; // 全零输入
        remainders.append(alphabet[static_cast<int>(carry)]);
        if (isZero) break;
    }
    if (remainders.isEmpty()) remainders.append(alphabet[0]);
    // 还原为高位在前,并用alphabet[0]补齐固定位数(前导零)
    QByteArray result = remainders;
    std::reverse(result.begin(), result.end());
    int minimalLen = static_cast<int>(qCeil(data.size() * 8.0 * qLn(2.0) / qLn(static_cast<double>(base))));
    if (result.size() < minimalLen) {
        result.prepend(QByteArray(minimalLen - result.size(), alphabet[0]));
    }
    return result;
}
QByteArray ShortCrypto::baseNDecode(const QString &text, const char *alphabet, int base)
{
    // 建立字符索引表
    int charToVal[256];
    memset(charToVal, -1, sizeof(charToVal));
    for (int i = 0; i < base; ++i) {
        charToVal[static_cast<unsigned char>(alphabet[i])] = i;
    }
    // Horner法则:num = num * base + v(大端字节序)
    QByteArray num(16, '\0');
    QByteArray input = text.toLatin1();
    for (int i = 0; i < input.size(); ++i) {
        int v = charToVal[static_cast<unsigned char>(input.at(i))];
        if (v < 0) {
            qWarning() << "ShortCrypto::baseNDecode: invalid character:" << QChar(input.at(i));
            return QByteArray();
        }
        unsigned int carry = static_cast<unsigned int>(v);
        for (int b = num.size() - 1; b >= 0; --b) {
            unsigned int cur = (static_cast<unsigned int>(static_cast<unsigned char>(num.at(b))) * static_cast<unsigned int>(base)) + carry;
            num[b] = static_cast<char>(cur & 0xFF);
            carry = cur >> 8;
        }
        if (carry != 0) {
            qWarning() << "ShortCrypto::baseNDecode: value overflows 128 bits";
            return QByteArray();
        }
    }
    // 保持16字节定宽(前导零有意义),调用方按固定块长处理
    return num;
}
QByteArray ShortCrypto::base85Encode(const QByteArray &data)
{
    QByteArray result;
shortcrypto.h
@@ -29,12 +29,37 @@
     */
    static QByteArray decrypt(const QString &ciphertext, const QByteArray &key, const QByteArray &iv = QByteArray());
    /**
     * @brief 无填充加密(V3专用)
     * @param plaintext 原始明文,长度必须是16的倍数(16字节明文 → 16字节密文)
     * @param key 16字节密钥(AES-128)
     * @return 纯大写字母编码的密文字符串(A-Z,26字符集),16字节明文恰好输出28字符
     * @note 不做PKCS7填充:16字节明文若走填充会扩成32字节
     * @note 若想缩短到25字符,把V3_CHARS换成"A-Z0-9"并把base改为36即可
     */
    static QString encryptRaw(const QByteArray &plaintext, const QByteArray &key);
    /**
     * @brief 无填充解密(V3专用)
     * @param ciphertext 纯大写字母编码的密文(兼容早期Base85/纯字母版V3密文,自动回退)
     * @param key 16字节密钥(AES-128)
     * @return 原始明文(长度与密文一致),完整性由上层CRC校验保证
     */
    static QByteArray decryptRaw(const QString &ciphertext, const QByteArray &key);
private:
    // Base85 字符集(RFC 1924 变体,不含引号/逗号/句点/斜杠,避免JSON/XML转义问题)
    static const char *BASE85_CHARS;
    // V3 密文字符集:纯大写字母,不含数字/小写/特殊字符(26字符集,16字节→28字符)
    static const char *V3_CHARS;
    static QByteArray base85Encode(const QByteArray &data);
    static QByteArray base85Decode(const QString &text);
    // 任意进制的大数编解码(整段字节序作为大整数,前导零以alphabet[0]补齐固定位数)
    static QByteArray baseNEncode(const QByteArray &data, const char *alphabet, int base);
    static QByteArray baseNDecode(const QString &text, const char *alphabet, int base);
};
#endif // SHORTCRYPTO_H