背景:
因项目需要,要做一个调用短信接口发送短信的功能。需要实现一个功能是,给定一个字符串,给定一个密钥对。实现RSA公钥加密,Base64加密,然后将加密后的字符串发给服务器。服务器返回通过私钥和Base64加密后的字符串,然后我这边要实现Base64解密后,RSA公钥解密。
1.RSA加密的原理,网上都有很多介绍,在这就不介绍了,需要注意的概念如下:
rsa加解密分两种,第一:公钥加密私钥解密。第二:私钥加密公钥解密。 需要注意的是,公加私解得到的密文是变化的,而私加公解的得到的密文是固定的。
生成密匙对需要设置一个长度,常用的设置为1024,或者2048。注意,不同长度的密匙,能够加密的明文最长度是有限制的。说明如下:
1024的情况:
加密时,明文最大长度: 1024/8 - 11 = 117 ,因此需要对明文做117字节长度的分片加密,再拼接。
解密时,密文最大长度:1024/8 = 128, 因此需要对密文做128字节长度的分片解密,再拼接。
2048的情况:
加密时,明文最大长度: 2048/8 - 11 = 245 ,因此需要对明文做245字节长度的分片加密,再拼接。
解密时,密文最大长度:2048/8 = 256, 因此需要对密文做256字节长度的分片解密,再拼接。
C++调用openssl库生成的密匙对是pkcs#1格式的。java调用库生成的密匙对是pkcs#8格式的。
pkcs#1
-----BEGIN RSA PUBLIC KEY-----
......公钥
-----END RSA PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
......私钥
-----END RSA PRIVATE KEY-----
pkcs#8
-----BEGIN PUBLIC KEY-----
......公钥
-----END PUBLIC KEY-----
-----BEGIN PRIVATE KEY-----
......私钥
-----END PRIVATE KEY-----
2.先上代码
```
RSACrypt.h
#include <openssl/rsa.h>
#include <string>
class RSACrypt {
public:
static RSACrypt *GetInstance();
//RSA公钥加密 + 分片
std::string RSA_Base64_pub_split117_encrypt(const std::string &clearText);
// 公钥解密 + 分片
std::string RSA_Base64_pub_split128_decrypt(const std::string &clearText);
private:
//RSA公钥加密
std::string RSA_pub_encrypt(const std::string &clearText);
// 公钥解密
std::string RSA_pub_decrypt(const std::string &clearText);
static std::string Init_public_key();
std::string pubKey;
};
```
```
RSACrypt.cpp
#include "RSACrypt.h"
#include <openssl/pem.h>
#include <cstring>
#include <iostream>
#include "Base64.h"
using std::cout;
static RSACrypt instance;
RSACrypt* RSACrypt::GetInstance() {
if(instance.pubKey.empty()){
instance.pubKey = instance.Init_public_key();
}
return &instance;
}
std::string BaseEncode(std::string code){
unsigned char *str = (unsigned char*)(code.c_str());
std::string encoded;
int len = code.length();
Base64 *base = new Base64();
encoded = base ->Encode(str,len);
delete base;
return encoded;
}
std::string BaseDecode(std::string encoded){
std::string normal;
Base64 *base = new Base64();
int len = encoded.length();
const char * str2 = encoded.c_str();
normal = base->Decode(str2,len);
delete base;
return normal;
}
//RSA公钥加密
std::string RSACrypt::Init_public_key(){
std::string strPublicKey = "MIGfMB0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCCFCPUaNuen2ctFEri2gG0kLzFGq7hxkP16UHQg2z9xPJh86RpCbLunKgzI64y4A8zo2plECHZxW1R9ltzbN4pfoU6geSl2+Yl247KOqa0IkxkjEawiSYaufWZ1HZbhaKdjugF/GukOkHp+l7LaYXV9ja9Gam98/WoxWJXS1tOVQIDAQAB";
int nPublicKeyLen = strPublicKey.size();//strPublicKey为base64编码的公钥字符串
for(int i = 64; i < nPublicKeyLen; i+=64) {
if (strPublicKey[i] != '\n') {
strPublicKey.insert(i, "\n");
}
i++;
}
strPublicKey.insert(0, "-----BEGIN PUBLIC KEY-----\n");
strPublicKey.append("\n-----END PUBLIC KEY-----\n");
return strPublicKey;
}
std::string RSACrypt::RSA_pub_encrypt(const std::string &clearText)
{
std::string strRet;
BIO *keybio = BIO_new_mem_buf((unsigned char *)pubKey.c_str(), -1);
//keybio = BIO_new_mem_buf((unsigned char *)strPublicKey.c_str(), -1);
// 此处有三种方法
// 1, 读取内存里生成的密钥对,再从内存生成rsa
// 2, 读取磁盘里生成的密钥对文本文件,在从内存生成rsa
// 3,直接从读取文件指针生成rsa
//RSA* pRSAPublicKey = RSA_new();
RSA* rsa = RSA_new();
rsa = PEM_read_bio_RSA_PUBKEY(keybio,NULL,NULL,NULL);
if (!rsa)
{
BIO_free_all(keybio);
return std::string("");
}
int len = RSA_size(rsa);
//int len = 1028;
char *encryptedText = (char *)malloc(len + 1);
memset(encryptedText, 0, len + 1);
// 加密函数
int ret = RSA_public_encrypt(clearText.length(), (const unsigned char*)clearText.c_str(), (unsigned char*)encryptedText, rsa, RSA_PKCS1_PADDING);
if (ret >= 0)
strRet = std::string(encryptedText, ret);
// 释放内存
free(encryptedText);
BIO_free_all(keybio);
RSA_free(rsa);
return strRet;
}
//公钥加密 + 分片
std::string RSACrypt::RSA_Base64_pub_split117_encrypt(const std::string &clearText)
{
std::string result;
std::string input;
result.clear();
for(int i = 0 ; i< clearText.length()/117; i++)
{
input.clear();
input.assign(clearText.begin() + i*117, clearText.begin() + i*117 + 117);
result = result + RSA_pub_encrypt(input);
}
if(clearText.length()%117 != 0)
{
int tem1 = clearText.length()/117 * 117;
input.clear();
input.assign(clearText.begin()+ tem1, clearText.end());
std::string encode = RSA_pub_encrypt(input);
result = result + encode;
}
std::string encode_str = BaseEncode(result);
return encode_str;
}
// 公钥解密
std::string RSACrypt::RSA_pub_decrypt(const std::string &clearText)
{
std::string strRet;
BIO *keybio = BIO_new_mem_buf((unsigned char *)pubKey.c_str(), -1);
RSA* rsa = RSA_new();
rsa = PEM_read_bio_RSA_PUBKEY(keybio, NULL, NULL, NULL);
if (!rsa)
{
BIO_free_all(keybio);
return std::string("");
}
int len = RSA_size(rsa);
//int len = 1028;
char *encryptedText = (char *)malloc(len + 1);
memset(encryptedText, 0, len + 1);
//解密
int ret = RSA_public_decrypt(clearText.length(), (const unsigned char*)clearText.c_str(), (unsigned char*)encryptedText, rsa, RSA_PKCS1_PADDING);
if (ret >= 0)
strRet = std::string(encryptedText, ret);
// 释放内存
free(encryptedText);
BIO_free_all(keybio);
RSA_free(rsa);
return strRet;
}
//公钥解密 + 分片
std::string RSACrypt::RSA_Base64_pub_split128_decrypt(const std::string &text)
{
std::string clearText = BaseDecode(text);
std::string result;
std::string input;
result.clear();
for(int i = 0 ; i< clearText.length()/128; i++)
{
input.clear();
input.assign(clearText.begin() + i*128, clearText.begin() + i*128 + 128);
result = result + RSA_pub_decrypt(input);
}
if(clearText.length()%128 != 0)
{
int tem1 = clearText.length()/128 * 128;
input.clear();
input.assign(clearText.begin()+ tem1, clearText.end());
result = result + RSA_pub_decrypt(input);
}
return result;
}
```
##走过的坑
1.网上很多帖子,GitHub上的RSA,c++实现也很多都是init,自动生成秘钥对。但是业务场景是给定的公钥去加密解密,不需要随时变更。
2.OpenSSL库,PEM_read_bio_RSA_PUBKEY对应的密钥对是pkcs#8类型。pem方法跟密钥对的类型一定要匹配,不然RSA得到的就是NULL,无法加密解密。
3.开始PEM_read_bio_RSA_PUBKEY的参数 keybio,纠结是传什么值进去,秘钥的string还是pem的路径。最后发现是拼接好的pkcs#8类型的字符串即可。
##参考的帖子
https://www.cnblogs.com/azbane/p/10178729.html