之前做的一个项目,想要给软件做一个注册码功能。当软件发布之后,不想给所有人用,这时就可以通过注册机给软件生成授权码来软件加密。整个过程实现分为两大步骤,一是在自己的软件打开时,增加一段判断逻辑;二是写一个软件注册机,对于给定的机器码生成授权码,并且设置授权到期时间。这里也说明一下,本文实现软件注册码的功能主要是基于注册表。
大体实现思路是获取到机器的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)保证密钥是相同的,否则会在授权判断时失败。
写在最后,有任何问题欢迎与我沟通交流(><)