之前做的一个项目,想要给软件做一个注册码功能。当软件发布之后,不想给所有人用,这时就可以通过注册机给软件生成授权码来软件加密。整个过程实现分为两大步骤,一是在自己的软件打开时,增加一段判断逻辑;二是写一个软件注册机,对于给定的机器码生成授权码,并且设置授权到期时间。这里也说明一下,本文实现软件注册码的功能主要是基于注册表。
大体实现思路是获取到机器的CPU信息、MAC信息生成独一无二的机器码,之后使用密钥对其做md5加密,最后为该机器设置一个过期时间,并对其进行编码,拼接在加密后的字符后,作为授权码。
1. 在自己的项目中增加判断逻辑
以下是一些工具函数
// 获取MAC地址信息
QString get_mac_id()
{QList<QNetworkInterface> nets = QNetworkInterface::allInterfaces();int nCnt = nets.count();QString strMacAddr = "";for(int i = 0; i < nCnt; i++){if(nets[i].flags().testFlag(QNetworkInterface::IsUp) &&nets[i].flags().testFlag(QNetworkInterface::IsRunning) &&!nets[i].flags().testFlag(QNetworkInterface::IsLoopBack)){for(int j = 0; j < nets[i].addressEntries().size(); j++){if(nets[i].addressEntries().at(j).ip() != QHostAddress::LocalHost &&nets[i].addressEntries().at(j).ip().protocol() == QAbstractSocket::IPv4Protocol){strMacAddr = nets[i].hardwareAddress();}}}}strMacAddr.remove(':');return strMacAddr;
}// 获取CPU信息
QString get_cpu_id(){QString strCpuId = "";unsigned int dwBuf[4] = { 0 };unsigned long long ret = 0;__cpuid((int*)(void*)dwBuf, 1);ret = dwBuf[3];ret = ret << 32;QString str0 = QString::number(dwBuf[3], 16).toUpper();QString str0_1 = str0.rightJustified(8, '0');QString str1 = QString::number(dwBuf[0], 16).toUpper();QString str1_1 = str1.rightJustified(8, '0');strCpuId = str0_1 + str1_1;return strCpuId;
}// 获得独一无二的机器码
QString get_machine_code()
{QString cpuid = get_cpu_id();QString macid = get_mac_id();QString UniqueCode = cpuid + macid;QString MachineCode = "";for(int i = 0; i < UniqueCode.size(); i++){MachineCode = MachineCode + UniqueCode[i];if((i+1)%4==0 && i+1!=UniqueCode.size()){MachineCode = MachineCode + "-";}}return MachineCode;}// 加入公钥
QString get_visible_key(){QString machinecode = get_machine_code();QString Authorkey1 = "key1";QString Authorkey2 = "key2";QString ciphertext = Authorkey1+ "-" + machinecode + "-" + Authorkey2;return ciphertext;
}// 获得加密后的密码
QString get_md5_key(){QString ciphertext = get_visible_key();QString md5Str = QCryptographicHash::hash(ciphertext.toLatin1(),QCryptographicHash::Md5).toHex();return md5Str;
}
1.1 判断注册表中是否有值,值是否过期
声明下面的函数,可以判断到当前机器是否已被授权
bool is_authorized(){QString md5pwd = get_md5_key();QSettings setting(registry_key, QSettings::NativeFormat);QString mypwd = setting.value("AuthKey", "").toString();mypwd = mypwd.split("-")[0];if(mypwd == md5pwd){return true;}else{return false;}
}
1.2 在main方法中循环调用授权窗口,判断授权码是否正确
int main(int argc, char *argv[])
{QApplication a(argc, argv);// 设置程序的字体大小QFont font = a.font();font.setPointSize(10);a.setFont(font);QString key = "AuthKey";AuthDialog authDialog; // 授权页authDialog.setMachineCode(get_machine_code());if(isExpire(key)){authDialog.changeTip();}while (is_authorized() == false) {if (authDialog.exec() == QDialog::Accepted) {if (authDialog.authorized) {break; // 授权成功,跳出循环} else {QMessageBox::warning(nullptr, "授权失败", "授权码验证失败,请重试");authDialog.clearPage(); // 界面恢复原始}} else {return 0; // 用户取消授权,退出程序}}MainWindow w;w.show();return a.exec();
}
1.3 授权窗口
授权窗口中针对“确定”按钮绑定了槽函数,点确定的时候,会调用验证函数,判断授权码是否正确,如果正确的话,会设置标志位位true,成功进入软件。否则返回false,继续弹窗。
void AuthDialog::on_buttonBox_accepted()
{if(validateKey()){authorized = true;save_authorization_info(ui->authCode->text());accept();}
}bool AuthDialog::validateKey(){QString authKey = ui->authCode->text();if(authKey.contains("-") && (authKey.split("-").length() >= 2)){QString encodedExpireTime = authKey.split("-")[1];QString expireTime = decode(encodedExpireTime); // 解码出时间 形式是20240616这样QDate expireDate = QDate::fromString(expireTime, "yyyyMMdd");QDate currentDate = QDate::currentDate();if(currentDate > expireDate){ // 授权码已经过期return false;}}QString authCode = authKey.split("-")[0];return authCode == get_md5_key(); // 时间没过期并且授权码经过验证 此时才能使用
}void AuthDialog::on_buttonBox_rejected()
{reject();
}void AuthDialog::setMachineCode(QString machineCode){ui->machineCode->setText(machineCode);
}
void AuthDialog::clearPage(){ui->authCode->setText("");
}// 加入公钥
QString AuthDialog::get_visible_key(){QString machinecode = ui->machineCode->text();// m_str_machine_code = machinecode;QString Authorkey1 = "key1";QString Authorkey2 = "key2";QString ciphertext = Authorkey1+ "-" + machinecode + "-" + Authorkey2;return ciphertext;
}// 获得加密后的密码
QString AuthDialog::get_md5_key(){QString ciphertext = get_visible_key();QString md5Str = QCryptographicHash::hash(ciphertext.toLatin1(),QCryptographicHash::Md5).toHex();return md5Str;
}void AuthDialog::save_authorization_info(QString md5_key)
{QSettings setting(registry_key, QSettings::NativeFormat);setting.setValue("AuthKey", md5_key);
}void AuthDialog::changeTip(){ui->textEdit->setText("您的授权码已过期,请重新将机器码发送给管理员进行授权");
}QString AuthDialog::encode(QByteArray data){return data.toBase64();
}QString AuthDialog::decode(QString encoded){return QString::fromUtf8((QByteArray::fromBase64(encoded.toUtf8())));
}
2. 软件注册机
2.1 对于传入的机器码,做合法性校验
bool MainWindow::validateMachineKey(){QString machineCode = ui->machineCode->text();if(machineCode == ""){return false;}QRegularExpression regex("^([A-F0-9]{4}-){6}[A-F0-9]{4}$");QRegularExpressionMatch match = regex.match(machineCode);return match.hasMatch();
}
2.2 对机器码加密钥,使用md5加密处理,将加密后的字符与过期时间做拼接
QString MainWindow::get_md5_key(){QString ciphertext = get_visible_key();QString md5Str = QCryptographicHash::hash(ciphertext.toLatin1(),QCryptographicHash::Md5).toHex();QString expireTime;if(ui->checkBox_forever->isChecked()){expireTime = "30240616";}else{expireTime = ui->dateEdit->date().toString("yyyyMMdd");}return md5Str + "-" + encode(expireTime.toUtf8());
}// 使用 Base64编码
QString MainWindow::encode(QByteArray data){return data.toBase64();
}
在经过上面的步骤,就可以实现给自己的软件加密了!
说明:
(1)步骤1放到一个项目中,步骤2放到一个项目中,最后呈现结果是两个软件。
(2)保证密钥是相同的,否则会在授权判断时失败。
写在最后,有任何问题欢迎与我沟通交流(><)