公司开发了一款软件,需要配合加密芯片进行激活验证,我就想着我也搞一个。
RSA是目前最有影响力和最常用的加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。这是一种非对称密码算法,所谓非对称,就是指该算法需要一对公密钥,使用其中一个加密,则需要用另一个才能解密。密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全。
公私秘钥是成对使用的,公钥加密私钥解密,私钥加密公钥解密。既然是加密,那肯定是不希望别人知道我的消息,所以只有我才能解密,所以可得出公钥负责加密,私钥负责解密;同理,既然是签名,那肯定是不希望有人冒充我发消息,只有我才能发布这个签名,所以可得出私钥负责签名,公钥负责验证。一般使用公钥加密,私钥解密(本文使用此种方法)。
本文采用的是512位的RSA秘钥。
OpenSSL库
首先要准备OpenSSL的动态链接库,Linux下由软件仓库提供,Windows下可以自己编译,也可以使用别人编译好的。
1 2 3
| openssl genrsa -out rsa_private_key.pem 512 openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -out pkcs8_rsa_private_key.epm -nocrypt
|
如果是在openssl命令中,就不需要openssl前缀
Qt
创建一个空白Widget工程。
将OpenSSL安装后的头文件和库文件复制到工程目录下,然后添加
1 2 3
| INCLUDEPATH += $$PWD/include LIBS += -L$$PWD/lib LIBS += -lcrypto-1_1-x64 -lssl-1_1-x64
|
测试代码
头文件
1 2 3 4 5 6 7
| #include <openssl/rsa.h> #include <openssl/pem.h> #include <openssl/bn.h> #include <openssl/bio.h> #include <openssl/evp.h> #include <openssl/ssl.h> #include <openssl/err.h>
|
公钥加密函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| QString MainWindow::rsa_pub_encrypt_base64(const QString &strClearData) { std::string public_key =ui->textPublicKey->toPlainText().toStdString(); if(public_key.empty()){ qDebug()<<"Must have a public_key for encryption."; return ""; }
BIO* pKeyBio = BIO_new_mem_buf(public_key.data(), public_key.size()); if (pKeyBio == NULL){ qDebug()<<"Public encryption BIO is empty."; return ""; } RSA* pRsa = RSA_new(); pRsa = PEM_read_bio_RSA_PUBKEY(pKeyBio, &pRsa, NULL, NULL); if ( pRsa == NULL ){ qDebug()<<"Public encryption read bio rsa error."; BIO_free_all(pKeyBio); return ""; } int nLen = RSA_size(pRsa); char* pEncryptBuf = new char[nLen]; memset(pEncryptBuf, 0, nLen); QByteArray clearDataArry = strClearData.toUtf8(); int nClearDataLen = clearDataArry.length(); uchar* pClearData = (uchar*)clearDataArry.data(); int nSize = RSA_public_encrypt(nClearDataLen, pClearData, (uchar*)pEncryptBuf, pRsa, RSA_PKCS1_PADDING);
QString strEncryptData = ""; if ( nSize >= 0 ){ QByteArray arry(pEncryptBuf, nSize); strEncryptData = arry.toBase64(); } delete[] pEncryptBuf; BIO_free_all(pKeyBio); RSA_free(pRsa); return strEncryptData; }
|
私钥解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| QString MainWindow::rsa_pri_decrypt_base64(const QString &strDecryptData) { std::string private_key = ui->textPrivateKey->toPlainText().toStdString(); if(private_key.empty()){ qDebug()<<"Must have private_key for decryption."; return ""; }
BIO* pKeyBio = BIO_new_mem_buf(private_key.data(), private_key.size()); if (pKeyBio == NULL){ qDebug()<<"Private decryption BIO is empty."; return ""; }
RSA* pRsa = RSA_new(); pRsa = PEM_read_bio_RSAPrivateKey(pKeyBio, &pRsa, NULL, NULL); if ( pRsa == NULL ){ qDebug()<<"Private decryption read bio rsa error."; BIO_free_all(pKeyBio); return ""; }
int nLen = RSA_size(pRsa); char* pClearBuf = new char[nLen]; memset(pClearBuf, 0, nLen); QByteArray decryptDataArry = strDecryptData.toUtf8(); decryptDataArry = QByteArray::fromBase64(decryptDataArry); int nDecryptDataLen = decryptDataArry.length(); uchar* pDecryptData = (uchar*)decryptDataArry.data(); int nSize = RSA_private_decrypt(nDecryptDataLen, pDecryptData, (uchar*)pClearBuf, pRsa, RSA_PKCS1_PADDING); QString strClearData = ""; if ( nSize >= 0 ){ strClearData = QByteArray(pClearBuf, nSize); }
delete[] pClearBuf; BIO_free_all(pKeyBio); RSA_free(pRsa); return strClearData; }
|
测试代码
1 2 3 4
| QString encrypt_str = rsa_pub_encrypt_base64("123abc"); qDebug()<<"加密数据:"<<encrypt_str; QString decrypt_str = rsa_pri_decrypt_base64(encrypt_str); qDebug()<<"解密数据:"<<decrypt_str;
|
输出为:
1 2
| 加密数据: "SDrjl8qmW//wB55Tb8uoJGUhdb+cvhHWpXHslmVh35QkzZK5inA3JJLWg/UOb8HG97bMNFzA5M6rko66aUBfew==" 解密数据: "123abc"
|
注册机制
经过测试,可以加密,可以解密。
我设计一个界面。
- 不同版本对应不同的公私秘钥。
- 绑定MAC地址
- 绑定CPU信息
- 绑定硬盘信息
- 授权到期时间
界面如下
以MAC和到期时间作为激活信息,然后把激活信息进行加密,得到激活秘钥。把私钥(其实公钥好一点)写入软件中,每次启动时验证信息。
还可以添加额外字符串,如果此字符串所在的密钥泄露,就直接跳过。
效果为:
操作流程就是将私钥直接写入到软件中,软件用户在使用时提供MAC/有效期等等向开发者申请注册码,软件用户将注册码填入激活窗口,软件使用私钥解密信息,判断MAC是否与本机相同,判断是否在有效期内等等,然后软件正常启动。
可以看到不论加密方式有多安全,注册流程有多复杂,只要有比较是否有效这一步,就是破解的关键,主要是是否值得破解。