http://www.dtmao.cc/news_show_692109.shtml
本文不讨论RSA加密解密本身,只记录使用方法及遇到的坑,RSA原理及注意事项可在网上查找。
背景:公司的一个需求,要求对接客户的一个平台,通信方式为MQTT,数据包含Token及json内容,在通信过程中发送的MQTT信息必须带上Token,这个Token是要从客户平台获取,通过http协议,POST方法。
然后POST方法的body信息需要进行RSA加密(提供了公钥和私钥,公钥加密私钥解密),然后再Base64加密。Base64公司代码里已经有了,但RSA加密却没有,只能自己网上搜索。在使用RSA加密过程中遇到了以下几个问题:
1,用RSA加密时,如果密文长度是1024位(128字节)的话,其要加密的明文只能是117字节长度。我的POST方法数据不止117字节,无法满足我目前的需求。
2,Base64编码问题
3,公钥及私钥是客户提供的,但不是标准格式。
关于问题1、问题2:
找了很多网上的例子,实现的都是简单的几个字符的加密及解密,那些长串字符加密的直接拿过来又无法通过编译。最后找到一些拿过来修修改改,终于可以用,记录如下:
/**
** @brief 用公钥进行加密,明文长于117字节时需要进行分段处理
*/
std::string CRSAEnCrypt::rsaPublicKeyEncryptSplit(const std::string &clearText)
{
std::string result;
std::string input;
result.clear();
for(int i = 0 ; i< clearText.length() / SPLIT_LEN; i++)
{
input.clear();
input.assign(clearText.begin() + i * SPLIT_LEN, clearText.begin() + i * SPLIT_LEN + SPLIT_LEN);
result = result + rsaPublicKeyEncrypt(input);
}
if(clearText.length() % SPLIT_LEN != 0)
{
int32_t tmp = clearText.length() / SPLIT_LEN * SPLIT_LEN;
input.clear();
input.assign(clearText.begin() + tmp, clearText.end());
result = result + rsaPublicKeyEncrypt(input);
}
printf("before base64 len: %lu\n", result.length());
std::string encode_str = base64Encode((uint8_t*)result.c_str(), result.length());
printf("after base64 len: %lu\n", encode_str.length());
return encode_str;
}
网上有些说,分段RSA加密后即进行Base64编码,但我这里是RSA加密完成后再整体进行Base64编码,在进行RSA解码时先Base64解码。
/**
** @brief 用私钥进行解密,分段处理
*/
std::string CRSAEnCrypt::rsaPrivateKeyDecryptSplit(const std::string &Text)
{
printf("before base64Decode len: %lu\n", Text.length());
std::string clearText = base64Decode(Text);
printf("after base64Decode len: %lu\n", clearText.length());
//printf("BaseDecode len: %lu\n", clearText.length());
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 + rsaPrivateKeyDecrypt(input);
}
if(clearText.length() % 128 != 0)
{
int tem1 = clearText.length()/128 * 128;
input.clear();
input.assign(clearText.begin()+ tem1, clearText.end());
result = result + rsaPrivateKeyDecrypt(input);
}
return result;
}
使用例子:
int main()
{
Json::Value postMsg;
postMsg["userName"] = "zgdxgzt";
postMsg["password"] = "xcq123456";
postMsg["tenantId"] = "5fca019cb091f5b7e44c0eec";
postMsg["client_id"] = "5cf600563f4c000053007383";
postMsg["client_secret"] = "U2FsdGVkX1+oesjvWS3Z8q7ziFCu+p4tU7OQjl+5m21l7XMbBBJtof4fAL1S8/tJNrGpqIAqtTwXOeiZJFFtHw==";
postMsg["scope"] = "read"; //固定值
postMsg["grant_type"] = "password"; //固定值
CRSAEnCrypt::getInstance()->initkey(privateKey, publicKey);
std::string sourceStr = jsonToString(postMsg);
printf("string of json: %s\n", sourceStr.c_str());
//单独测试base64加密、解密
// std::string encodeStr = base64Encode((uint8_t *)sourceStr.c_str(), sourceStr.length());
// printf("decodeStr len: %lu\n", encodeStr.length());
// std::string decodeStr = base64Decode(encodeStr);
// printf("decodeStr len: %lu, %s\n", decodeStr.length(), decodeStr.c_str());
//公钥加密
std::string rsaEncryptStr = CRSAEnCrypt::getInstance()->rsaPublicKeyEncryptSplit(sourceStr);
printf("after rsaEncryptStr len: %lu\n", rsaEncryptStr.length());
//私钥解密
std::string rsaDecryptStr = CRSAEnCrypt::getInstance()->rsaPrivateKeyDecryptSplit(rsaEncryptStr);
printf("after rsaDecryptStr: %lu, %s\n", rsaDecryptStr.length(), rsaDecryptStr.c_str());
return 0;
}
关于第3个问题,公钥和私钥有固定格式,要注意,因客户只提供两个字符串,里面并没有包含固定的头和尾字符,这样可能会导致RSA 读取密钥时出错。正确格式如下:
不同的头尾标识,调用的openssl的库函数可能不同,可自行baidu。
最后一点网上一些例子只贴出部分代码,很多时候下载后根本不能直接用,这不是要流氓吗。。。。
源码下载:
git clone git@github.com:tianyexing2008/c_cpp.git
下载后cd encrypt 目录下make 即可。因代码中用到的json,如果你电脑上已经有json库,可能可以直接编译过,如果没有,这个git里也有json源码,目录json,可cd json后进行make编译libjson.a库,再修改encrypt里的Makefile里指定的头文件目录
及json库目录即可。