QT设置软件使用期限

一、设计思路

程序启动

每次启动软件时会读取注册文件,若不存在则提示输入激活码,进行注册。若文件存在,则解析文件验证文件合法性,计算剩余使用期限。

程序激活

为了使每个激活码仅对应一个用户(机器),我们的激活码应当绑定设备,因此应包含MAC地址信息。
激活界面,会显示当前的机器码,机器码根据本机MAC地址通过相应算法生成,因此每个用户的机器码都不同。用户将机器码发送给厂商,厂商根据机器码和使用期限生成相应的激活码,发送给用户,用户输入激活码完成注册。注册后会生成注册文件,文件为二进制加密文件,存放注册日期,使用期限等信息,用户无法直接篡改,且若文件缺失需要重新注册。

二、激活码生成端(厂家端)

生成端界面如下

QT设置软件使用期限_第1张图片
由一个机器码输入框、一个有效期输入框、一个激活码生成框以及生成按钮组成。
厂家需要输入用户给出的机器码,输入要赋予的有效期限,点击按钮就能生成激活码。
机器码的获取在 验证端(客户端) 中会提及。

(1)密码表

下方为用随机数生成的密码表,建议大家自己重新生成密码表。

uint8_t pwd[256]={23,95,39,51,15,49,18,20,78,45,24,4,74,113,100,123,
                      0,37,34,121,127,108,25,37,37,56,20,75,34,8,112,87,
                      19,117,101,4,24,35,82,107,93,51,95,108,28,33,88,6,
                      23,26,25,65,84,56,62,33,120,1,65,87,47,51,39,43,
                      78,23,41,43,95,26,76,50,121,96,75,8,13,24,23,115,
                      40,99,123,72,52,87,5,81,117,115,42,38,59,26,38,94,
                      105,28,6,87,18,75,93,107,101,62,19,18,7,37,89,73,
                      23,92,12,59,46,48,59,10,70,7,81,59,43,77,95,107,
                      72,66,67,59,48,106,126,120,31,80,105,99,127,9,96,72,
                      70,27,47,67,32,80,33,64,112,56,13,27,10,71,63,63,
                      100,59,91,8,1,65,9,35,14,90,71,90,123,37,21,47,
                      16,62,110,69,39,55,75,30,113,122,53,73,22,29,111,29,
                      114,89,27,85,76,84,52,85,76,62,26,60,109,90,44,21,
                      20,46,115,108,19,35,58,55,55,78,27,49,12,69,105,74,
                      109,91,90,108,127,95,76,53,53,112,74,72,79,26,109,102,
                      21,10,25,68,67,17,29,22,59,80,39,97,120,104,0,109};

(2)生成激活码

QString getActivationCode(QString machineCode,ing yxq)//machineCode机器码,yxq有效期:天
{
	QString sj=QDate::currentDate().toString("yyMMdd");//当前日期,由此也说明了激活码仅当天有效
	//中间为激活码生成算法,不必深究
    QString Activation;
    for(int j=0;j<6;j++)
    {
        int tempCode;
        tempCode=pwd[machineCode.mid(j*2,2).toInt(nullptr,16)]<<sj.mid(j,1).toInt();
        Activation.append(tenToHexL(tempCode));
    }
    QString Activation2;
    for(int k=0;k<5;k++)
    {
        int tempCode2;
        tempCode2=Activation.mid(4*k+4,4).toInt(nullptr,16)>>tenToBin(sj.mid(4,2).toInt()).mid(k,1).toInt();
        Activation2.append(tenToHexL(tempCode2));
    }
    Activation2.append(Activation.mid(0,4));
    Activation2.remove("00");
	//激活码生成算法

	//此处开始为激活码带有有效期信息的部分
    QString str_yxq =tenToHexL(yxq+pwd[sj.mid(4,2).toInt()]).remove(0,1);
    int jy = yxq ^ pwd[sj.mid(4,2).toInt()];
    str_yxq.append(tenToHexL(jy).remove(0,1));
    Activation2.append(str_yxq);
    //有效期信息部分
    return Activation2
}

三、验证端(客户端)

(1)获取机器码

机器码生成方法如下,其中pwd密码表应与生成端相同。

QString getMachineCode()
{
    QString text;
    //获取非00-00-00-00-00-00的mac地址
    QRegExp regmac("00.00.00.00.00.00");
    foreach(QNetworkInterface interface,QNetworkInterface::allInterfaces()){
        text = interface.hardwareAddress();
        if(text.indexOf(regmac)>=0)
            continue;
        else
            break;
    }
    //以mac地址为序号获取密码表相应值
    QString machineCode;
    for(int i=0;i<6;i++){
        machineCode.append(text.mid(0,2));
        text.remove(0,3);
    }
    return machineCode;
}

(2)首次激活

用户首次启动应用时为未激活状态(即未读取到注册文件),此时需要输入厂家提供的激活码进行注册。

2.1 首次激活/生成注册文件

void Widget::on_pushButton_2_clicked()//激活按钮
{
    QString code = ui->lineEdit->text();
    QString tail = code.mid(code.length()-6,6);//code:QString激活码  tail:激活码后六位,包含有效期信息
	//验证激活码有效性
	QString machineCode = getMachineCode();
    QString sj=QDate::currentDate.toString("yyMMdd");
    QString Activation;
    for(int j=0;j<6;j++)
    {
        int tempCode;
        tempCode=pwd[machineCode.mid(j*2,2).toInt(nullptr,16)]<<sj.mid(j,1).toInt();
        Activation.append(tenToHexL(tempCode));
    }
    QString Activation2;
    for(int k=0;k<5;k++)
    {
        int tempCode2;
        tempCode2=Activation.mid(4*k+4,4).toInt(nullptr,16)>>tenToBin(sj.mid(4,2).toInt()).mid(k,1).toInt();
        Activation2.append(tenToHexL(tempCode2));
    }
    Activation2.append(Activation.mid(0,4));
    Activation2.remove("00");
    if(code.mid(0,code.length()-10)!=Activation2)
        return ;//激活码无效,可以根据需求设定函数返回值,返回激活失败原因,我这里偷懒就直接在按钮里了
	//验证有效期部分
    QString tail = code.mid(code.length()-3,3);
    int h = tail.toInt(nullptr,16);
    int iyxq = h ^ pwd[sj.mid(4,2).toInt()];//逆解出有效期
    QString str_yxq =tenToHexL(iyxq+pwd[sj.mid(4,2).toInt()]).remove(0,1);
    if(str_yxq!=code.mid(code.length()-6,3))
    	return ;//激活码无效,有效期部分校验失败
    
    //写注册文件
    QFileInfo fileinfo("PassCode.dat");
    if(!fileinfo.exists()){
        QFile file("PassCode.dat");
        file.open(QIODevice::WriteOnly);
        QDataStream out(&file);
        QByteArray ba;
        ba.resize(15);
        for(int i=0;i<12;i++){
            ba[i]=code.mid(2*i,2).toInt(nullptr,16);
        }
        ba[12]=tail.mid(0,2).toInt(nullptr,16);
        ba[13]=tail.mid(2,2).toInt(nullptr,16);
        ba[14]=tail.mid(4,2).toInt(nullptr,16);
        out<<(QByteArray)ba;
        out<<(QDate)QDate::currentDate();//date:QDate激活日期
        file.flush();
        file.close();
    }
}

(3)后续启动检验

程序每次启动都会检验有无注册文件,若无则执行上面的首次激活步骤,若有,则对文件数据进行校对

3.1 校对注册文件

int Widget::checkHasActivate()
{
    QFileInfo fileinfo("PassCode.dat");
    if(!fileinfo.exists()){
        return 0;//无注册文件,返回有效期0天,执行首次激活流程
    }
    else{
        QFile file("PassCode.dat");
        file.open(QIODevice::ReadOnly);
        QDataStream in(&file);
        QByteArray ba;
        QDate date;
        QString code;
        in >> ba;
        in >> date;
        for(int i=0;i<12;i++){
            int a=ba[i];
            code.append(tenToHexS(a));//tenToHexS()方法为十进制转为两个字符的十六进制
        }
        code.remove("00");
        code.append(tenToHexS(ba[12]));
        code.append(tenToHexS(ba[13]));
        code.append(tenToHexS(ba[14]));
        int yxq = checkActivationCode(code,date);//checkActivationCode为上述的验证激活码算法,返回有效期
        if(yxq==3840)//永久
            return 3840;
        yxq = yxq-date.daysTo(QDate::currentDate());
        if(yxq<=0||date.daysTo(QDate::currentDate())<0){
            file.remove();//若有效期到期,或者检测到用户通过更改当前日期来延长有效期,则删除注册文件
            			  //再次启动后需要执行首次激活
            return 0;
        }
        return yxq;
    }
}

3.2 激活码验证算法 checkActivationCode

int checkActivationCode(QString code, QDate date)
{
    QString machineCode = getMachineCode();
    QString sj=date.toString("yyMMdd");
    QString Activation;
    for(int j=0;j<6;j++)
    {
        int tempCode;
        tempCode=pwd[machineCode.mid(j*2,2).toInt(nullptr,16)]<<sj.mid(j,1).toInt();
        Activation.append(tenToHexL(tempCode));
    }
    QString Activation2;
    for(int k=0;k<5;k++)
    {
        int tempCode2;
        tempCode2=Activation.mid(4*k+4,4).toInt(nullptr,16)>>tenToBin(sj.mid(4,2).toInt()).mid(k,1).toInt();
        Activation2.append(tenToHexL(tempCode2));
    }
    Activation2.append(Activation.mid(0,4));
    Activation2.remove("00");
    if(code.mid(0,code.length()-10)!=Activation2)
        return 0;

    QString tail = code.mid(code.length()-3,3);
    int h = tail.toInt(nullptr,16);
    int iyxq = h ^ pwd[sj.mid(4,2).toInt()];

    QString str_yxq =tenToHexL(iyxq+pwd[sj.mid(4,2).toInt()]).remove(0,1);
    if(str_yxq!=code.mid(code.length()-6,3))
        return 0;
    else
        return iyxq;
}

四、补充上述代码中缺少的一些函数

QString Widget::tenToHexL(int ten)
{
    QString a="0123456789ABCDEF";
    QString res;
    if(ten>255)
    {
        res=a.at(ten/4096);
        res.append(a.at(ten/256%16));
        res.append(a.at(ten/16%16));
        res.append(a.at(ten%16));
    }
    else
    {
        res="00";
        res.append(a.at(ten/16));
        res.append(a.at(ten%16));
    }
    return res;
}

QString Widget::tenToHexS(int ten)
{
    QString s = "0123456789ABCDEF";
    QString res;
    res.append(s.at(ten/16));
    res.append(s.at(ten%16));
    return res;
}

QString Widget::tenToBin(int ten)
{
    QString res;
    for(int i=0;i<5;i++)
    {
        res.prepend(QString::number(ten%2));
        ten=ten/2;
    }
    return res;
}

五、源代码下载

源码下载

你可能感兴趣的:(QT,qt,开发语言)