软件注册机 | QT给自己的桌面软件实现软件注册码功能

发布于:2024-12-07 ⋅ 阅读:(273) ⋅ 点赞:(0)

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


网站公告

今日签到

点亮在社区的每一天
去签到