赞
踩
RSA是一种非对称加密。
RSA秘钥:私钥和公钥,一对私钥和公钥就像夫妻一样是唯一的,用私钥加密后必须用对应的公钥才能解密,用公钥加密后必须用对应的私钥才能解密。
加密和解密方式:公钥加密-私钥解密,私钥加密-公钥解密
1) 调用OpenSSL库生成秘钥(非必要步骤,如果已经有秘钥对了,就不需要进行这步了)
2) 调用OpenSSL库对明文进行加密
2) 对加密后密文进行BASE64转码(非必要步骤,一般开发过程中,为了传输or存贮方便,都会对密文进行BASE64编码)
注意:OpenSSL的RSA加密接口,每次加密数据的最大长度是有限制的,所以对“较大数据”进行加密,需要循环对“较大数据”分段加密
1)对BASE64内容进行BASE64解码
2) 调用OpenSSL库对密文进行解密
注意:OpenSSL的RSA解密接口,每次解密数据的最大长度是有限制的,所以对“较大数据”进行解密,需要循环对“较大数据”分段解密
说明: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);
- }
- /*
- @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;
- }
- /*
- @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;
- 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 = 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 = RsaPubEncrypt(src_text, pub_key);
- printf("encrypt: len=%d\n", encrypt_text.length());
- decrypt_text = RsaPriDecrypt(encrypt_text, pri_key);
- printf("decrypt: len=%d\n", decrypt_text.length());
- printf("decrypt: %s\n", decrypt_text.c_str());
-
- return 0;
- }
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 64
-
- std::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;
- }
- /* 公钥 */
- -----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-----
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。