在工業軟件或其他領域中,經常會對軟件進行授權,軟件需要付費進行有期限的使用。以下是我用Qt設計的設置軟件使用期限的兩種方案。
主體思想:
1.軟件需要綁定機器,讓用戶無法通過復制在另一臺機器上運行。
2.由廠家提供激活碼供用戶激活,每個激活碼只對應指定的機器有效,且激活碼需包含使用期限。為保證激活碼不能重復使用,激活碼需包含當天的日期信息,令激活碼僅當天有效,軟件過期或許可證丟失后無法使用之前激活碼進行激活。
3.由于2的限制,因此需要在每臺機器上產生機器碼,供廠家用來生成激活碼。
4.需有校驗功能,校驗激活碼是否有效,校驗是否過期。為了避免用戶修改系統時間來延長使用期限,需要驗證許可證中最后使用日期是否大于當前日期,若是,則說明用戶修改了系統時間,校驗不通過,此時可以在代碼中刪除許可證,或直接退出軟件。
5.需要保存許可證或注冊表。許可證和注冊表信息存儲的是當前激活的密鑰和最后使用日期、有效期限。并在每次開啟和關閉軟件時對許可證內的最后使用日期進行更新。
一、獲取機器碼
要綁定機器,就要獲取到機器的唯一標識,網卡的MAC地址是個不錯的選擇,因此可以拿MAC地址當做機器碼。
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;elsebreak;}QString machineCode;for(int i=0;i<6;i++){machineCode.append(text.mid(0,2));text.remove(0,3);}return machineCode;
}
二、生成帶使用期限的激活碼
激活碼可以自己編寫一些算法,通過各種計算、移位、換位等操作來生成。下面是我的示例:
QString newFunction(QString mc, int day)//mc為機器碼,day為使用期限
{QString newcode;for(int i=0;i<6;i++){int index=mc.mid(i*2,2).toUInt(nullptr,16);newcode.append(QString::number(pwd[index],16));}
//激活碼中日期信息QDate date = QDate::currentDate();int y=date.year()-2000;int m=date.month();int d=date.day();QString dts = intToBin(y,7)+intToBin(m,4)+intToBin(d,5);qDebug()<<dts;int dt = dts.toInt(nullptr,2);QString dthex = QString::number(dt,16);QString copycode = newcode;copycode.replace(0,1,newcode[1]);copycode.replace(1,1,newcode[0]);copycode.replace(2,1,newcode[4]);copycode.replace(4,1,newcode[2]);copycode.replace(7,1,newcode[10]);copycode.replace(10,1,newcode[7]);QStringList binlst;binlst.append(hexToBin(copycode.mid(0,2)));binlst.append(hexToBin(copycode.mid(2,2)));binlst.append(hexToBin(copycode.mid(4,2)));binlst.append(hexToBin(copycode.mid(6,2)));binlst.append(hexToBin(copycode.mid(8,2)));binlst.append(hexToBin(copycode.mid(10,2)));int jy = 0;for(int i=7;i>=0;i--){int nums=0;for(int j=0;j<6;j++){if(binlst.at(j).at(i)=='1')nums++;}if(nums%2==1)jy += 1<<i;}qDebug()<<jy;jy += day;int local = day%6;copycode.insert(local*2,tenToHexL(jy));copycode.append(dthex);qDebug()<<copycode;return copycode;
}
三、校驗激活碼
校驗的過程其實就是生成激活碼(不帶期限),與帶期限的激活碼進行比對和運算,判斷其是否有效,若有效則計算使用期限。
int Widget::getValidity(QString code)
{QDate date = QDate::currentDate();int y=date.year()-2000;int m=date.month();int d=date.day();QString dts = intToBin(y,7)+intToBin(m,4)+intToBin(d,5);int dt = dts.toInt(nullptr,2);QString dthex = QString::number(dt,16);if(dthex!=code.mid(16,4)){//激活碼內日期與當前日期不符,激活碼過期qDebug()<<"code over time!";return 0;}code.remove(16,4);QString mc = getMachineCode();QString newcode;for(int i=0;i<6;i++){int index=mc.mid(i*2,2).toUInt(nullptr,16);newcode.append(QString::number(pwd[index],16));}QString copycode = newcode;copycode.replace(0,1,newcode[1]);copycode.replace(1,1,newcode[0]);copycode.replace(2,1,newcode[4]);copycode.replace(4,1,newcode[2]);copycode.replace(7,1,newcode[10]);copycode.replace(10,1,newcode[7]);QStringList binlst;binlst.append(hexToBin(copycode.mid(0,2)));binlst.append(hexToBin(copycode.mid(2,2)));binlst.append(hexToBin(copycode.mid(4,2)));binlst.append(hexToBin(copycode.mid(6,2)));binlst.append(hexToBin(copycode.mid(8,2)));binlst.append(hexToBin(copycode.mid(10,2)));int jy = 0;for(int i=7;i>=0;i--){int nums=0;for(int j=0;j<6;j++){if(binlst.at(j).at(i)=='1')nums++;}if(nums%2==1)jy += 1<<i;}int validity=0;for(int i=0;i<12;i+=2){if(copycode.mid(i,2)!=code.mid(i,2)){validity = code.mid(i,4).toInt(nullptr,16)-jy;code.remove(i,4);break;}}if(copycode==code){//激活碼有效,生成許可證writeLicense(copycode,dthex,validity);return validity;}elsereturn 0;
}
四、生成許可證
為了后續校驗方便,許可證內的激活碼可以省略最后幾個步驟。許可證內容由三部分組成:激活碼、最后使用日期、軟件有效期。
void writeLicense(QString code,QString dthex,int days)
{QDate yxq = QDate::currentDate().addDays(days);int y=yxq.year()-2000;int m=yxq.month();int d=yxq.day();QString dts = intToBin(y,7)+intToBin(m,4)+intToBin(d,5);int dt = dts.toInt(nullptr,2);QString yxqhex = QString::number(dt,16);QFile file("license.dat");file.open(QIODevice::WriteOnly);QDataStream ds(&file);QByteArray ba;ba.resize(10);for(int i=0;i<6;i++){ba[i]=uchar(code.mid(i*2,2).toUInt(nullptr,16));}ba[6]=uchar(dthex.mid(0,2).toUInt(nullptr,16));ba[7]=uchar(dthex.mid(2,2).toUInt(nullptr,16));ba[8]=uchar(yxqhex.mid(0,2).toUInt(nullptr,16));ba[9]=uchar(yxqhex.mid(2,2).toUInt(nullptr,16));ds<<(QByteArray)ba;file.flush();file.close();
}
五、軟件每次啟動時校驗許可證
void Widget::checkLicense()
{QFile file("license.dat");file.open(QIODevice::ReadOnly);QDataStream ds(&file);QByteArray ba;ds>>ba;QString code = ba.toHex();QString shortcode = code.mid(0,12);QString mc = getMachineCode();QString newcode;for(int i=0;i<6;i++){int index=mc.mid(i*2,2).toUInt(nullptr,16);newcode.append(QString::number(pwd[index],16));}QString copycode = newcode;copycode.replace(0,1,newcode[1]);copycode.replace(1,1,newcode[0]);copycode.replace(2,1,newcode[4]);copycode.replace(4,1,newcode[2]);copycode.replace(7,1,newcode[10]);copycode.replace(10,1,newcode[7]);if(shortcode!=copycode){//激活碼錯誤qDebug()<<"校驗不通過";return;}QDate lastDate;QString dts = code.mid(12,4);int idt = dts.toInt(nullptr,16);int y = idt>>9;int m = (idt>>5) & ((1<<4)-1);int d = idt & ((1<<5)-1);lastDate.setDate(y+2000,m,d);if(lastDate.daysTo(QDate::currentDate())<0){//用戶修改了系統時間qDebug()<<"時間非法";return;}QDate today = QDate::currentDate();y=today.year()-2000;m=today.month();d=today.day();QString s_today = intToBin(y,7)+intToBin(m,4)+intToBin(d,5);int dt = s_today.toInt(nullptr,2);QString s_yxq = code.mid(16,4);if(s_yxq.toInt(nullptr,16)<dt){//許可證已過期qDebug()<<"已過期";return;}
}
六、更新許可證
為防止用戶篡改系統時間以達到延長使用期限的目的,許可證中加入了最后使用日期進行校驗。在每次軟件啟動校驗成功后、軟件關閉時,更新許可證中的最后使用日期。
void updateLicense()
{QFile file("license.dat");file.open(QIODevice::ReadWrite);QDataStream ds(&file);QByteArray ba;ds>>ba;QDate today = QDate::currentDate();int y=today.year()-2000;int m=today.month();int d=today.day();QString s_today = intToBin(y,7)+intToBin(m,4)+intToBin(d,5);int dt = s_today.toInt(nullptr,2);QString hex = QString::number(dt,16);ba[6]=uchar(hex.mid(0,2).toUInt(nullptr,16));ba[7]=uchar(hex.mid(2,2).toUInt(nullptr,16));file.resize(0);file.seek(0);ds<<ba;file.flush();file.close();
}
以上內容為本地生成許可證的方式,也可以用寫注冊表的方式來代替,主體思想不變。