#include "licensemanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ==================== 静态成员初始化 ==================== // 加密密钥 const QByteArray LicenseManager::encryptKey = "GetonAgain2026SecretKey!@#$%"; // 客户专用密码 - 根据不同客户修改此值 const QString LicenseManager::customerPassword = "nuobo123"; // ==================== LicenseManager 实现 ==================== LicenseManager::LicenseManager(QObject *parent) : QObject(parent) { } QString LicenseManager::getMachineUUID() { QString uuid; #ifdef Q_OS_WIN // Windows: 通过WMI获取主板UUID QProcess process; process.start("wmic", QStringList() << "csproduct" << "get" << "UUID"); process.waitForFinished(5000); QString output = QString::fromLocal8Bit(process.readAllStandardOutput()); // 解析输出,获取UUID QStringList lines = output.split('\n', Qt::SkipEmptyParts); for (const QString &line : lines) { QString trimmed = line.trimmed(); if (!trimmed.isEmpty() && trimmed != "UUID" && trimmed.contains('-')) { uuid = trimmed; break; } } #else // Linux/Mac: 读取machine-id QFile file("/etc/machine-id"); if (file.open(QIODevice::ReadOnly)) { uuid = QString::fromUtf8(file.readAll()).trimmed(); file.close(); } #endif if (uuid.isEmpty()) { // 备用方案:使用机器名+用户名的哈希 QString fallback = QSysInfo::machineHostName() + QDir::homePath(); uuid = QString::fromLatin1(QCryptographicHash::hash(fallback.toUtf8(), QCryptographicHash::Md5).toHex()); } return uuid; } QString LicenseManager::getLicenseFilePath() { // 激活文件放在程序目录下 return QCoreApplication::applicationDirPath() + "/license.gal"; } QString LicenseManager::encrypt(const QString &plainText) { QByteArray data = plainText.toUtf8(); // 第一步:计算明文的SHA256哈希(用于完整性校验) QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Sha256); // 第二步:构造加密数据 = 哈希前8字节 + 原始数据 QByteArray toEncrypt; toEncrypt.append(hash.left(8)); // 8字节校验头 toEncrypt.append(data); // 第三步:多层XOR加密 QByteArray encrypted; for (int i = 0; i < toEncrypt.size(); ++i) { unsigned char c = static_cast(toEncrypt[i]); // 第一层:与主密钥XOR c = c ^ static_cast(encryptKey[i % encryptKey.size()]); // 第二层:字节置换(非线性变换) c = static_cast((c * 7 + 13) % 256); // 第三层:与位置相关的混淆 c = c ^ static_cast((i * 31 + 17) % 256); encrypted.append(static_cast(c)); } // 第四步:Base64编码 return QString::fromLatin1(encrypted.toBase64()); } QString LicenseManager::decrypt(const QString &encryptedText) { // Base64解码 QByteArray encrypted = QByteArray::fromBase64(encryptedText.toLatin1()); if (encrypted.size() < 9) // 至少8字节校验头 + 1字节数据 { return QString(); } // 逆向解密 QByteArray decrypted; for (int i = 0; i < encrypted.size(); ++i) { unsigned char c = static_cast(encrypted[i]); // 逆向第三层:位置混淆 c = c ^ static_cast((i * 31 + 17) % 256); // 逆向第二层:字节置换的逆运算 // 原式: c' = (c * 7 + 13) % 256 // 需要找到 7 在 mod 256 下的逆元 // 7 * 183 = 1281 = 5 * 256 + 1, 所以 7^(-1) ≡ 183 (mod 256) c = static_cast(((c + 256 - 13) * 183) % 256); // 逆向第一层:与主密钥XOR c = c ^ static_cast(encryptKey[i % encryptKey.size()]); decrypted.append(static_cast(c)); } // 提取校验头和原始数据 QByteArray storedHash = decrypted.left(8); QByteArray originalData = decrypted.mid(8); // 验证哈希 QByteArray computedHash = QCryptographicHash::hash(originalData, QCryptographicHash::Sha256); if (computedHash.left(8) != storedHash) { qDebug() << "License integrity check failed - data may be corrupted or tampered"; return QString(); // 返回空表示解密失败 } return QString::fromUtf8(originalData); } QString LicenseManager::generateActivationCode(const QString &uuid) { // 生成激活码格式: encrypt(UUID:password) // 这个方法供管理员/供应商使用,为特定设备生成激活码 QString plainText = uuid + ":" + customerPassword; return encrypt(plainText); } bool LicenseManager::readAndVerifyLicense() { QString filePath = getLicenseFilePath(); QFile file(filePath); if (!file.exists()) { qDebug() << "License file not found:" << filePath; return false; } if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug() << "Cannot open license file"; return false; } QTextStream in(&file); QString encryptedContent = in.readAll().trimmed(); file.close(); if (encryptedContent.isEmpty()) { qDebug() << "License file is empty"; return false; } // 解密 QString decrypted = decrypt(encryptedContent); qDebug() << "Decrypted license content (debug)"; // 解析格式: UUID:password // 使用 lastIndexOf 因为UUID中也可能包含冒号(虽然通常不会) int separatorIndex = decrypted.lastIndexOf(':'); if (separatorIndex == -1) { qDebug() << "Invalid license format: no separator"; return false; } QString licenseUUID = decrypted.left(separatorIndex); QString password = decrypted.mid(separatorIndex + 1); // 获取本机真实UUID QString currentUUID = getMachineUUID(); qDebug() << "License UUID:" << licenseUUID; qDebug() << "Current UUID:" << currentUUID; // 验证UUID是否匹配 if (licenseUUID != currentUUID) { qDebug() << "UUID mismatch - license not valid for this machine"; return false; } // 验证密码是否正确 if (password != customerPassword) { qDebug() << "Password mismatch"; return false; } qDebug() << "License verification successful"; return true; } bool LicenseManager::saveLicenseFile(const QString &activationCode) { QString filePath = getLicenseFilePath(); QFile file(filePath); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qDebug() << "Cannot save license file to:" << filePath; return false; } QTextStream out(&file); out << activationCode; file.close(); qDebug() << "License file saved to:" << filePath; return true; } bool LicenseManager::showActivationDialog(QWidget *parent) { ActivationDialog dialog(parent); while (true) { if (dialog.exec() != QDialog::Accepted) { return false; // 用户取消 } QString serverAddress = dialog.getServerAddress(); QString activationCode = dialog.getActivationCode(); // 解析服务器地址和端口 QString host; int port = 8080; if (serverAddress.contains(':')) { QStringList parts = serverAddress.split(':'); host = parts[0]; port = parts[1].toInt(); } else { host = serverAddress; } // 显示连接中提示 QMessageBox waitMsg(parent); waitMsg.setWindowTitle("请稍候"); waitMsg.setText("正在连接激活服务器..."); waitMsg.setStandardButtons(QMessageBox::NoButton); waitMsg.show(); QApplication::processEvents(); // 连接服务器 QTcpSocket socket; socket.connectToHost(host, port); bool connected = socket.waitForConnected(10000); // 10秒超时 if (!connected) { waitMsg.close(); QMessageBox::warning(parent, "连接失败", QString("无法连接到服务器 %1:%2\n请检查网络连接和服务器地址。").arg(host).arg(port)); continue; } // 构建激活请求 QJsonObject requestObj; requestObj["uuid"] = getMachineUUID(); requestObj["code"] = activationCode; requestObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate); QJsonDocument doc(requestObj); QByteArray requestData = doc.toJson(QJsonDocument::Compact); // 发送请求 socket.write(requestData); socket.write("\n"); socket.flush(); // 等待响应 if (!socket.waitForReadyRead(15000)) // 15秒超时 { waitMsg.close(); socket.close(); QMessageBox::warning(parent, "激活失败", "服务器响应超时,请重试。"); continue; } QByteArray response = socket.readAll(); socket.close(); waitMsg.close(); // 解析响应 QJsonDocument responseDoc = QJsonDocument::fromJson(response); if (responseDoc.isNull() || !responseDoc.isObject()) { QMessageBox::warning(parent, "激活失败", "服务器响应格式错误。"); continue; } QJsonObject responseObj = responseDoc.object(); QString status = responseObj["status"].toString(); if (status == "success") { QString license = responseObj["license"].toString(); // 保存激活文件 if (saveLicenseFile(license)) { // 验证保存的激活文件 if (readAndVerifyLicense()) { QMessageBox::information(parent, "成功", "软件激活成功!"); return true; } } QMessageBox::warning(parent, "错误", "保存激活文件失败!"); } else { QString message = responseObj["message"].toString(); if (message.isEmpty()) message = "激活失败,请检查激活码是否正确。"; QMessageBox::StandardButton retry = QMessageBox::warning( parent, "激活失败", message, QMessageBox::Retry | QMessageBox::Cancel); if (retry != QMessageBox::Retry) { return false; } } } } // ==================== ActivationDialog 实现 ==================== ActivationDialog::ActivationDialog(QWidget *parent) : QDialog(parent) { setWindowTitle("软件激活"); setFixedSize(420, 240); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // 确保对话框接收键盘输入 setAttribute(Qt::WA_InputMethodEnabled, true); setupUI(); } QString ActivationDialog::getServerAddress() const { return serverEdit->text().trimmed(); } QString ActivationDialog::getActivationCode() const { return codeEdit->text().trimmed(); } void ActivationDialog::setupUI() { QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(25, 20, 25, 20); mainLayout->setSpacing(12); // 标题 QLabel *titleLabel = new QLabel("软件激活"); titleLabel->setStyleSheet("font-size: 20px; font-weight: bold; color: #333;"); titleLabel->setAlignment(Qt::AlignCenter); mainLayout->addWidget(titleLabel); mainLayout->addSpacing(8); // 提示信息 QLabel *infoLabel = new QLabel("请输入激活服务器地址和激活码"); infoLabel->setStyleSheet("font-size: 12px; color: #666;"); infoLabel->setAlignment(Qt::AlignCenter); mainLayout->addWidget(infoLabel); mainLayout->addSpacing(8); // 服务器地址输入 - 使用 QFormLayout 风格 QHBoxLayout *serverLayout = new QHBoxLayout(); serverLayout->setSpacing(10); QLabel *serverLabel = new QLabel("服务器地址:"); serverLabel->setFixedWidth(80); serverLabel->setStyleSheet("font-size: 13px;"); serverEdit = new QLineEdit(); serverEdit->setMinimumHeight(32); serverEdit->setPlaceholderText("例如: 192.168.1.100:8080"); serverEdit->setStyleSheet("font-size: 13px; padding: 4px 8px;"); serverLayout->addWidget(serverLabel); serverLayout->addWidget(serverEdit, 1); mainLayout->addLayout(serverLayout); // 激活码输入 QHBoxLayout *codeLayout = new QHBoxLayout(); codeLayout->setSpacing(10); QLabel *codeLabel = new QLabel("激 活 码:"); codeLabel->setFixedWidth(80); codeLabel->setStyleSheet("font-size: 13px;"); codeEdit = new QLineEdit(); codeEdit->setMinimumHeight(32); codeEdit->setPlaceholderText("例如: RB2026-XXXX-XXXX-XXXX"); codeEdit->setStyleSheet("font-size: 13px; padding: 4px 8px;"); codeLayout->addWidget(codeLabel); codeLayout->addWidget(codeEdit, 1); mainLayout->addLayout(codeLayout); mainLayout->addSpacing(15); // 按钮 QHBoxLayout *btnLayout = new QHBoxLayout(); btnLayout->addStretch(); confirmBtn = new QPushButton("激 活"); confirmBtn->setFixedSize(100, 36); confirmBtn->setStyleSheet( "QPushButton { background-color: #4CAF50; color: white; font-size: 14px; font-weight: bold; border-radius: 4px; }" "QPushButton:hover { background-color: #45a049; }" "QPushButton:pressed { background-color: #3d8b40; }"); connect(confirmBtn, &QPushButton::clicked, this, &ActivationDialog::onConfirmClicked); cancelBtn = new QPushButton("退 出"); cancelBtn->setFixedSize(100, 36); cancelBtn->setStyleSheet( "QPushButton { background-color: #f44336; color: white; font-size: 14px; font-weight: bold; border-radius: 4px; }" "QPushButton:hover { background-color: #da3c30; }" "QPushButton:pressed { background-color: #c62828; }"); connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); btnLayout->addWidget(confirmBtn); btnLayout->addSpacing(20); btnLayout->addWidget(cancelBtn); btnLayout->addStretch(); mainLayout->addLayout(btnLayout); // 确保可以Tab切换 setTabOrder(serverEdit, codeEdit); setTabOrder(codeEdit, confirmBtn); setTabOrder(confirmBtn, cancelBtn); } void ActivationDialog::onConfirmClicked() { if (serverEdit->text().trimmed().isEmpty()) { QMessageBox::warning(this, "提示", "请输入服务器地址!"); serverEdit->setFocus(); return; } if (codeEdit->text().trimmed().isEmpty()) { QMessageBox::warning(this, "提示", "请输入激活码!"); codeEdit->setFocus(); return; } accept(); }