1. RSA加密和解密基礎概念
? ? RSA是一種非對稱加密。
????RSA秘鑰:私鑰和公鑰,一對私鑰和公鑰就像夫妻一樣是唯一的,用私鑰加密后必須用對應的公鑰才能解密,用公鑰加密后必須用對應的私鑰才能解密。
????加密和解密方式:公鑰加密-私鑰解密,私鑰加密-公鑰解密
2. 使用OpenSSL庫進行RSA加密和解密的基礎過程
加密基礎過程 ???????1) 調用OpenSSL庫生成秘鑰(非必要步驟,如果已經有秘鑰對了,就不需要進行這步了)
???????2) 調用OpenSSL庫對明文進行加密
???????2) 對加密后密文進行BASE64轉碼(非必要步驟,一般開發過程中,為了傳輸or存貯方便,都會對密文進行BASE64編碼)
???????注意:OpenSSL的RSA加密接口,每次加密數據的最大長度是有限制的,所以對“較大數據”進行加密,需要循環對“較大數據”分段加密
解密基礎過程 ???????1)對BASE64內容進行BASE64解碼
???????2) 調用OpenSSL庫對密文進行解密
???????注意:OpenSSL的RSA解密接口,每次解密數據的最大長度是有限制的,所以對“較大數據”進行解密,需要循環對“較大數據”分段解密
3. 直接上代碼
私鑰和公鑰格式 ???說明:C++ OpenSSL中RSA秘鑰(公鑰和私鑰)是有起止標識的,并且每64個字節會有一個換行符(\n),這個可能和其他編程語言(例如Java)是不同的。據我所知Java中秘鑰是沒有起止標識,只有秘鑰內容的,也沒有換行符(\n)(個人開發中遇到的情形,也不排除有其他情形的)
???1)私鑰格式
????????(據我所知)私鑰的起止標識只有一種。
????????起始標識:-----BEGIN RSA PRIVATE KEY----- ????????結束標識:-----END RSA PRIVATE KEY-----
???2)·公鑰格式
?????(據我所知)公鑰的起止標識有兩種。
第一種 ????????起始標識:-----BEGIN RSA PUBLIC KEY----- ????????結束標識:-----END RSA PUBLIC KEY-----
第二種 ????????起始標識:-----BEGIN PUBLIC KEY----- ????????結束標識:-----END PUBLIC KEY-----
生成秘鑰對(公鑰和私鑰)
#include <fstream>
#include "openssl/rsa.h"#define KEY_LENGTH ?2048 ? ? ? ? ? ? // 密鑰長度
#define PUB_KEY_FILE "pubkey.pem" ? ?// 公鑰路徑
#define PRI_KEY_FILE "prikey.pem" ? ?// 私鑰路徑/*
制造密鑰對:私鑰和公鑰
**/
void GenerateRSAKey(std::string & out_pub_key, std::string & out_pri_key)
{size_t pri_len = 0; // 私鑰長度size_t pub_len = 0; // 公鑰長度char *pri_key = nullptr; // 私鑰char *pub_key = nullptr; // 公鑰// 生成密鑰對RSA *keypair = RSA_generate_key(KEY_LENGTH, RSA_3, NULL, NULL);BIO *pri = BIO_new(BIO_s_mem());BIO *pub = BIO_new(BIO_s_mem());// 生成私鑰PEM_write_bio_RSAPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL);// 注意------生成第1種格式的公鑰//PEM_write_bio_RSAPublicKey(pub, keypair);// 注意------生成第2種格式的公鑰(此處代碼中使用這種)PEM_write_bio_RSA_PUBKEY(pub, keypair);// 獲取長度 ?pri_len = BIO_pending(pri);pub_len = BIO_pending(pub);// 密鑰對讀取到字符串 ?pri_key = (char *)malloc(pri_len + 1);pub_key = (char *)malloc(pub_len + 1);BIO_read(pri, pri_key, pri_len);BIO_read(pub, pub_key, pub_len);pri_key[pri_len] = '\0';pub_key[pub_len] = '\0';out_pub_key = pub_key;out_pri_key = pri_key;// 將公鑰寫入文件std::ofstream pub_file(PUB_KEY_FILE, std::ios::out);if (!pub_file.is_open()){perror("pub key file open fail:");return;}pub_file << pub_key;pub_file.close();// 將私鑰寫入文件std::ofstream pri_file(PRI_KEY_FILE, std::ios::out);if (!pri_file.is_open()){perror("pri key file open fail:");return;}pri_file << pri_key;pri_file.close();// 釋放內存RSA_free(keypair);BIO_free_all(pub);BIO_free_all(pri);free(pri_key);free(pub_key);
}
私鑰加密-公鑰解密 ???1)對長度較短的數據加密和解密(數據長度小于RSA單次處理的最大長度)
/*
@brief : 私鑰加密
@para ?: clear_text ?-[i] 需要進行加密的明文pri_key ? ? -[i] 私鑰
@return: 加密后的數據
**/
std::string RsaPriEncrypt(const std::string &clear_text, std::string &pri_key)
{std::string encrypt_text;BIO *keybio = BIO_new_mem_buf((unsigned char *)pri_key.c_str(), -1);RSA* rsa = RSA_new();rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);if (!rsa){BIO_free_all(keybio);return std::string("");}// 獲取RSA單次可以處理的數據的最大長度int len = RSA_size(rsa);// 申請內存:存貯加密后的密文數據char *text = new char[len + 1];memset(text, 0, len + 1);// 對數據進行私鑰加密(返回值是加密后數據的長度)int ret = RSA_private_encrypt(clear_text.length(), (const unsigned char*)clear_text.c_str(), (unsigned char*)text, rsa, RSA_PKCS1_PADDING);if (ret >= 0) {encrypt_text = std::string(text, ret);}// 釋放內存 ?free(text);BIO_free_all(keybio);RSA_free(rsa);return encrypt_text;
}
/*
@brief : 公鑰解密
@para ?: cipher_text -[i] 加密的密文pub_key ? ? -[i] 公鑰
@return: 解密后的數據
**/
std::string RsaPubDecrypt(const std::string & cipher_text, const std::string & pub_key)
{std::string decrypt_text;BIO *keybio = BIO_new_mem_buf((unsigned char *)pub_key.c_str(), -1);RSA *rsa = RSA_new();// 注意--------使用第1種格式的公鑰進行解密//rsa = PEM_read_bio_RSAPublicKey(keybio, &rsa, NULL, NULL);// 注意--------使用第2種格式的公鑰進行解密(我們使用這種格式作為示例)rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);if (!rsa){unsigned long err= ERR_get_error(); //獲取錯誤號char err_msg[1024] = { 0 };ERR_error_string(err, err_msg); // 格式:error:errId:庫:函數:原因printf("err msg: err:%ld, msg:%s\n", err, err_msg);BIO_free_all(keybio);return decrypt_text;}int len = RSA_size(rsa);char *text = new char[len + 1];memset(text, 0, len + 1);// 對密文進行解密int ret = RSA_public_decrypt(cipher_text.length(), (const unsigned char*)cipher_text.c_str(), (unsigned char*)text, rsa, RSA_PKCS1_PADDING);if (ret >= 0) {decrypt_text.append(std::string(text, ret));}// 釋放內存 ?delete text;BIO_free_all(keybio);RSA_free(rsa);return decrypt_text;
}
?2)對長度較長的數據加密和解密(數據長度大于RSA單次處理數據塊的最大長度)
/*
@brief : 私鑰加密
@para ?: clear_text ?-[i] 需要進行加密的明文pri_key ? ? -[i] 私鑰
@return: 加密后的數據
**/
std::string RsaPriEncrypt(const std::string &clear_text, std::string &pri_key)
{std::string encrypt_text;BIO *keybio = BIO_new_mem_buf((unsigned char *)pri_key.c_str(), -1);RSA* rsa = RSA_new();rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);if (!rsa){BIO_free_all(keybio);return std::string("");}// 獲取RSA單次可以處理的數據塊的最大長度int key_len = RSA_size(rsa);int block_len = key_len - 11; ? ?// 因為填充方式為RSA_PKCS1_PADDING, 所以要在key_len基礎上減去11// 申請內存:存貯加密后的密文數據char *sub_text = new char[key_len + 1];memset(sub_text, 0, key_len + 1);int ret = 0;int pos = 0;std::string sub_str;// 對數據進行分段加密(返回值是加密后數據的長度)while (pos < clear_text.length()) {sub_str = clear_text.substr(pos, block_len);memset(sub_text, 0, key_len + 1);ret = RSA_private_encrypt(sub_str.length(), (const unsigned char*)sub_str.c_str(), (unsigned char*)sub_text, rsa, RSA_PKCS1_PADDING);if (ret >= 0) {encrypt_text.append(std::string(sub_text, ret));}pos += block_len;}// 釋放內存 ?delete sub_text;BIO_free_all(keybio);RSA_free(rsa);return encrypt_text;
}
/*
@brief : 公鑰解密
@para ?: cipher_text -[i] 加密的密文pub_key ? ? -[i] 公鑰
@return: 解密后的數據
**/
std::string RsaPubDecrypt(const std::string & cipher_text, const std::string & pub_key)
{std::string decrypt_text;BIO *keybio = BIO_new_mem_buf((unsigned char *)pub_key.c_str(), -1);RSA* rsa = RSA_new();// 注意-------使用第1種格式的公鑰進行解密//rsa = PEM_read_bio_RSAPublicKey(keybio, &rsa, NULL, NULL);// 注意-------使用第2種格式的公鑰進行解密(我們使用這種格式作為示例)rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);if (!rsa){unsigned long err = ERR_get_error(); //獲取錯誤號char err_msg[1024] = { 0 };ERR_error_string(ulErr, szErrMsg); // 格式:error:errId:庫:函數:原因printf("err msg: err:%ld, msg:%s\n", err , err_msg);BIO_free_all(keybio);return decrypt_text;}// 獲取RSA單次處理的最大長度int len = RSA_size(rsa);char *sub_text = new char[len + 1];memset(sub_text, 0, len + 1);int ret = 0;std::string sub_str;int pos = 0;// 對密文進行分段解密while (pos < cipher_text.length()) {sub_str = cipher_text.substr(pos, len);memset(sub_text, 0, len + 1);ret = RSA_public_decrypt(sub_str.length(), (const unsigned char*)sub_str.c_str(), (unsigned char*)sub_text, rsa, RSA_PKCS1_PADDING);if (ret >= 0) {decrypt_text.append(std::string(sub_text, ret));printf("pos:%d, sub: %s\n", pos, sub_text);pos += len;}}// 釋放內存 ?delete sub_text;BIO_free_all(keybio);RSA_free(rsa);return decrypt_text;
}
公鑰加密-私鑰解密 ???該部分只對數據塊較長的數據的處理為例
/*
@brief : 公鑰加密
@para ?: clear_text ?-[i] 需要進行加密的明文pri_key ? ? -[i] 私鑰
@return: 加密后的數據
**/
std::string RsaPubEncrypt(const std::string &clear_text, const std::string &pub_key)
{std::string encrypt_text;BIO *keybio = BIO_new_mem_buf((unsigned char *)pub_key.c_str(), -1);RSA* rsa = RSA_new();// 注意-----第1種格式的公鑰//rsa = PEM_read_bio_RSAPublicKey(keybio, &rsa, NULL, NULL);// 注意-----第2種格式的公鑰(這里以第二種格式為例)rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);// 獲取RSA單次可以處理的數據塊的最大長度int key_len = RSA_size(rsa);int block_len = key_len - 11; ? ?// 因為填充方式為RSA_PKCS1_PADDING, 所以要在key_len基礎上減去11// 申請內存:存貯加密后的密文數據char *sub_text = new char[key_len + 1];memset(sub_text, 0, key_len + 1);int ret = 0;int pos = 0;std::string sub_str;// 對數據進行分段加密(返回值是加密后數據的長度)while (pos < clear_text.length()) {sub_str = clear_text.substr(pos, block_len);memset(sub_text, 0, key_len + 1);ret = RSA_public_encrypt(sub_str.length(), (const unsigned char*)sub_str.c_str(), (unsigned char*)sub_text, rsa, RSA_PKCS1_PADDING);if (ret >= 0) {encrypt_text.append(std::string(sub_text, ret));}pos += block_len;}// 釋放內存 ?BIO_free_all(keybio);RSA_free(rsa);delete[] sub_text;return encrypt_text;
}
/*
@brief : 私鑰解密
@para ?: cipher_text -[i] 加密的密文pub_key ? ? -[i] 公鑰
@return: 解密后的數據
**/
std::string RsaPriDecrypt(const std::string &cipher_text, const std::string &pri_key)
{std::string decrypt_text;RSA *rsa = RSA_new();BIO *keybio;keybio = BIO_new_mem_buf((unsigned char *)pri_key.c_str(), -1);rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);if (rsa == nullptr) {unsigned long err = ERR_get_error(); //獲取錯誤號char err_msg[1024] = { 0 };ERR_error_string(err, err_msg); // 格式:error:errId:庫:函數:原因printf("err msg: err:%ld, msg:%s\n", err, err_msg);return std::string();}// 獲取RSA單次處理的最大長度int key_len = RSA_size(rsa);char *sub_text = new char[key_len + 1];memset(sub_text, 0, key_len + 1);int ret = 0;std::string sub_str;int pos = 0;// 對密文進行分段解密while (pos < cipher_text.length()) {sub_str = cipher_text.substr(pos, key_len);memset(sub_text, 0, key_len + 1);ret = RSA_private_decrypt(sub_str.length(), (const unsigned char*)sub_str.c_str(), (unsigned char*)sub_text, rsa, RSA_PKCS1_PADDING);if (ret >= 0) {decrypt_text.append(std::string(sub_text, ret));printf("pos:%d, sub: %s\n", pos, sub_text);pos += key_len;}}// 釋放內存 ?delete[] sub_text;BIO_free_all(keybio);RSA_free(rsa);return decrypt_text;
}
測試代碼
int main()
{// 原始明文 ?std::string src_text = "test begin\n this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! \ntest end";//src_text = "rsa test";std::string encrypt_text;std::string decrypt_text;// 生成密鑰對std::string pub_key;std::string pri_key;OpensslTool::GenerateRSAKey(pub_key, pri_key);printf("public key:\n");printf("%s\n", pub_key.c_str());printf("private key:\n");printf("%s\n", pri_key.c_str());// 私鑰加密-公鑰解密encrypt_text = OpensslTool::RsaPriEncrypt(src_text, pri_key);printf("encrypt: len=%d\n", encrypt_text.length());decrypt_text = OpensslTool::RsaPubDecrypt(encrypt_text, pub_key);printf("decrypt: len=%d\n", decrypt_text.length());printf("decrypt: %s\n", decrypt_text.c_str());// 公鑰加密-私鑰解密encrypt_text = OpensslTool::RsaPubEncrypt(src_text, pub_key);printf("encrypt: len=%d\n", encrypt_text.length());decrypt_text = OpensslTool::RsaPriDecrypt(encrypt_text, pri_key);printf("decrypt: len=%d\n", decrypt_text.length());printf("decrypt: %s\n", decrypt_text.c_str());return 0;
}
4. 總結 1)單次加密數據的最大長度(block_len),由RSA秘鑰模長RSA_size()和填充模式有關
??????A. 填充模式:RSA_PKCS1_PADDING,?block_len=RSA_size() - 11
??????B. 填充模式:RSA_PKCS1_OAEP_PADDING,block_len=RSA_size() - 41
??????C. 填充模式:RSA_NO_PADDING(不填充),block_len=RSA_size()
??????調用加密接口時,如果傳入的加密數據的長度大于block_len,則加密接口將返回錯誤
2)公鑰的使用(公鑰和公鑰加密解密接口),公鑰的類型 和 生成公鑰RSA對象指針的接口必須是一一對應的,否則將操作失敗
??????A. 第1種格式的公鑰(-----BEGIN RSA PUBLIC KEY----- /?-----END RSA PUBLIC KEY-----),對應的接口如下:
??????????生成公鑰:PEM_write_bio_RSAPublicKey()
??????????生成公鑰RSA對象指針:PEM_read_bio_RSAPublicKey()
??????B. 第2種格式的公鑰(-----BEGIN PUBLIC KEY----- /?-----END PUBLIC KEY-----),對應的接口如下:
??????????生成公鑰:PEM_write_bio_RSA_PUBKEY()
??????????生成公鑰RSA對象指針:PEM_read_bio_RSA_PUBKEY()
3) 在使用其他編程語言生成的RSA秘鑰對時,可能(同時)遇到以下問題(本人在使用java生成的公鑰時全部遇到了,血的經驗……):
??????A. 秘鑰對只有“秘鑰內容”,而沒有“秘鑰起止標識”;(公鑰+私鑰都可能遇到)
??????B. 秘鑰內容沒有換行符(\n);(公鑰可能會遇到)
??????遇到這個問題,請不要緊張,自己寫個函數對“秘鑰內容”包裝一下,加上“起止標識”和換行符(\n)。對于“起止標識”,一種格式不行就換另一種試試,本人遇到的就是需要使用第二種“起止標識”的,代碼請參考:
#define RSA_KEYSUB_LEN ? ?64std::string iifs::IIRSA::FormatPubKey(const std::string & key)
{std::string pub_key = "-----BEGIN PUBLIC KEY-----\n";auto pos = key.length();pos = 0;while (pos < key.length()) {pub_key.append(key.substr(pos, RSA_KEYSUB_LEN));pub_key.append("\n");pos += RSA_KEYSUB_LEN;}pub_key.append("-----END PUBLIC KEY-----");return pub_key;
}
5. 貼上一對秘鑰(起止標識結束位置有換行,每64字節有換行) /* 公鑰 */ -----BEGIN RSA PUBLIC KEY----- MIIBCAKCAQEAwDpa2EOEu7vCx88mVXzLHxdw1Yn0Hm7gkUEdnzdXzPenbL4NVLoj 6lxtX2UQ10wZLQfHuv5elaBn9jkCxpZgb9zkD3hKqmVLJI6HXRi2OECEnOp0Edus crCr3N/FXvf7ALT4i9LeUlfmUmTB+kIR7VpyPY2Pg88DvA5n7QXQRRqRu8d3NBXF ZhB9nZd8Zb8XyTStKLwwd11e7JV4vUTiA3heoNnSsBEeLN1DWdQNjCqq35AFP2yd M1pncM4Zjhbyv0z0l776vCJyS4/iS78qund4i1dxp2l4gDoDuTd4Nck6oN/HC9Ba nuIqrv/RP0Sw01Dij7g59nVxYA1aN8cvxQIBAw== -----END RSA PUBLIC KEY----- /* 私鑰 */ -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAwDpa2EOEu7vCx88mVXzLHxdw1Yn0Hm7gkUEdnzdXzPenbL4N VLoj6lxtX2UQ10wZLQfHuv5elaBn9jkCxpZgb9zkD3hKqmVLJI6HXRi2OECEnOp0 EduscrCr3N/FXvf7ALT4i9LeUlfmUmTB+kIR7VpyPY2Pg88DvA5n7QXQRRqRu8d3 NBXFZhB9nZd8Zb8XyTStKLwwd11e7JV4vUTiA3heoNnSsBEeLN1DWdQNjCqq35AF P2ydM1pncM4Zjhbyv0z0l776vCJyS4/iS78qund4i1dxp2l4gDoDuTd4Nck6oN/H C9BanuIqrv/RP0Sw01Dij7g59nVxYA1aN8cvxQIBAwKCAQEAgCbnOtet0n0shTTE OP3cv2T147FNaZ9AYNYTv3o6iKUaSH6zjdFtRuhI6kNgj4gQyK/afKmUY8BFTtCs hGRASpNCtPrccZjcwwmvk2XO0CsDE0b4C+fITHXH6JUuP0/8qyNQXTc+4Y/u4ZiB UYFhSOb207O1AooCfV7v81k1g2XkBlZqUGXBCYoo7ec5X1PoCImTHrlbwTLOaA6h GIr0HgrsvLMVjNNvzx8p7v18jBRzolkDa3Ch3dp61QHQ9Z3lehpXByTplLHxCNDp fa8KtmdmQVZSKsosjBoMvtxHtEEpAhhAAL+6HtlQ67Y4LHsfLtIDk/PKjMNKyeht mDKV8wKBgQDgWdxBbLw30cos87Tk98JlWYmkuo6YtVQL0qyj4J6jFnH2LTMSiJPP 2ETcUXt2rkd5Zs6NCNQEhHifgbmvIOILqD7txNGRIDksVf5UKRC5X3+90RbQff/x XLLzNqKIEhIrdgNQzptoTpJyj5Llzw5Sj1f0MtnKAomW4l3zmuf2BwKBgQDbWGmW TsDsBfcTRQfBXv7WYtyrwBeOID0dfdLjN9XQv/YFWJof1EAmnemoIdxcC8SEBTvz FW+l4hoPr5Gw/MgO3+aESDYLPN5caFgv5ifhSVyhWD8l6TpEUV/9ZEqElVVRp7gW PBVbIgm+vduXLX2vfb3o/vDAIMbqTtLCOJNY0wKBgQCVkT2A8yglNobIoniYpSxD kQZt0bRlzjgH4chtQGnCDvakHiIMWw01OtiS4Pz5yYT7md8IsI1YWFBqVnvKFewH xX9JLeELatDIOVQ4G2B7lP/T4LngU//2PcyiJGxatrbHpAI13xJFibb3CmHuigmM X4/4IeaGrFu57D6iZ0VOrwKBgQCSOvEO3ytIA/oM2K/WP1SO7JMdKrpewCi+U+Hs z+Pgf/lY5bwVOCrEaUZwFpLoB9hYA31MuPUZQWa1H7Z1/dq0lURYMCQHfemS8DrK mW/rhj3A5X9um3wti5VTmDGtuOOLxSVkKA48wVvUfpJkyP50/n6bVKCAFdnxieHW 0GI7NwKBgCNVncsGnj/sIwoTJ62udRTxKW5VxtmUmPcDlG05qfjCB/itrJmPC5nv Pmzq2doZXlu9SCqTN/tEgeyJ8PGBGJFDS03T42VnjuNu4Eravbmgm4AJYLdxip4O oCF6GkBGNYJaCCdcPHQnouW5cvTILlAvJVYn99w0Vei/VmajwCIZ -----END RSA PRIVATE KEY-----
其他鏈接:https://www.jianshu.com/p/011303dc9429
總結
以上是生活随笔 為你收集整理的使用OpenSSL进行RSA加密和解密(非对称) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。