前言
RSA加密与签名是很使用率非常高的一套算法。这次工作中有需求制作一个C++版的RSA加密与签名的Demo。这里记录一下踩过的坑吧。 如果你正好也需要Cpp的RSA算法,希望能给你提供点帮助。
进入正题
本次笔者使用的是OpenSSL工具包中的RSA算法。
适用场景:两台服务器之间数据传输交互验证。
Demo工程GitHub链接
1、公钥,私钥注意事项
-
本示例使用 emersonfxbx.openssl.v140.desktop.x86 NuGet库
与其他高级语言不同的是,这里的RSA算法是基于OpenSSL 1.0.1e版本。所以需要使用密钥格式为 PKCS#1 的密钥对(只对私钥有要求,公钥PKCS#1与PKCS#8通用)。
推荐一个密钥对生成网站程默的博客. -
密钥字符串需要注意格式,如代码所示(每64位一次换行):
//来自另外一台服务器的公钥,用于验证签名,此值不变。 -------公钥使用 pkcs#8
const std::string & _publicKey =
"-----BEGIN PUBLIC KEY-----\n"\
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCOlUoW84bA+8b23PBM7OWWO3+o\n"\
"wISx42v7bWDH8c6rUa0zeeh2bh43sJrmoNdwMWkRNKgeHxw8BITXg/IV2j8x9Z2/\n"\
"+uROYoFLgcIDnxotgumylfxDZo/RxFyhpl8Rx4/zlj6quSOXoMNvkXpWKLhty8+g\n"\
"Wlsv2othCFU9Ym4cQQIDAQAB\n"\
"-----END PUBLIC KEY-----\n";
//开发者私钥,由开发者自行生成,用于解密回调数据,请将此私钥修改为开发者自已的私钥。 --------私钥使用 pkcs#1
const std::string & _privateKey =
"-----BEGIN RSA PRIVATE KEY-----\n"\
"MIICXAIBAAKBgQCLF/ZGuuNwGpS+YRFPr1kMEz+hO7HesleruuyoepVnex4cLP1V\n"\
"PDVUN6l1VgO6bJk3ToHWGme+MYIyduQwVafm6mJcL3gj5k6UymXFyZCp/+n1B1u3\n"\
"6NQQLP4/HvW/HhGBwFXhhGTdkzLUhrZE2Xfz6NbwsMdEkDq9nhcXxAC96wIDAQAB\n"\
"AoGAPUd0R9sEYppDV9CZ+NpOx+QfD2CmT2+Q8maq5tsCwZFbRZyIi6m38P+I19nq\n"\
"UJKRue0LhJEjjYZwTt1UUPsbuhTYGNpVHzCsie1QVkBX19tlRrhjETisCQF8QSiT\n"\
"DXnhmaqNXAUGchnCntp85viCXPxHmj0m0ymU5/ctr4CBXIkCQQDq64bdPet2Ky+1\n"\
"FoFNm/mYk6UlUnkBz+z1akvWYQn3H9fmhOf1gPQknPoHo7A6LZGaL+K/qPTW/7ts\n"\
"3+qFDuotAkEAl5Mnwd+grqqn0nIW/A6TxZqMBJlqxpbjcfqL+TvEprVXJMFI+ufb\n"\
"NVrpuljDVfHSZk2NYYtCVy0cK4tLlcZPdwJAIbOsY20QrKFBdN9HqZSo2CTGWnZc\n"\
"edAUlJitTJIbVeKxnJaQmH3piJ8kl5f6Hj6PVulrxEc+6OFDSDlPcctT+QJAdz53\n"\
"mpg5qu/q0y6aUnWNX3m0CbJARDdUe8il8c9JZ/Vlty6wIWPiGlmJYuaN1cFGyuDc\n"\
"Bw8tg7OjY8ZUEmJPBQJBAOao0jpWLKD/9HMM79k5v8Yhm+v5M58B1qtoguhED+Id\n"\
"UKM5FzeT04lfQtOx10DhkOaS4Rb7/h05hz+in/AQXSo=\n"\
"-----END RSA PRIVATE KEY-----\n";
2、RSA算法核心部分
RSACrypto.h
#pragma once
#include <string>
class RSACrypto
{
public:
RSACrypto(
const std::string& public_key,
const std::string& private_key) :
m_strPublicKey(public_key),
m_strPrivateKey(private_key),
m_PublickeyRsa(nullptr), m_PrivatekeyRsa(nullptr)
{
}
virtual ~RSACrypto();
bool InitRsa();
void Release();
bool EncryptByPublicKey(const std::string& src, std::string& encrypted);
bool DecryptByPublicKey(const std::string& encrypted, std::string& decrypted);
bool EncryptByPrivateKey(const std::string& src, std::string& encrypted);
bool DecryptByPrivateKey(const std::string& encrypted, std::string& decrypted);
bool SignByPrivateKey(const std::string &src, std::string& sign);
bool VerifyByPublicKey(const std::string &src, const std::string& sign);
private:
bool InitPrivateRSA();
bool InitPublicRSA();
private:
const std::string & m_strPublicKey;
const std::string & m_strPrivateKey;
RSA *m_PublickeyRsa;
RSA *m_PrivatekeyRsa;
};
RSACrypto.Cpp
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <vector>
#include <algorithm>
#include "RSACrypto.h"
RSACrypto::~RSACrypto()
{
Release();
}
bool RSACrypto::InitPrivateRSA()
{
BIO* keybio = nullptr;
keybio = BIO_new_mem_buf((char*)m_strPrivateKey.data(), -1);
if (keybio == NULL)
{
printf("Failed to create private key BIO\n");
return false;
}
m_PrivatekeyRsa = PEM_read_bio_RSAPrivateKey(keybio, &m_PrivatekeyRsa, NULL, NULL);
if (!m_PrivatekeyRsa)
{
printf("Failed to create private RSA\n");
BIO_set_close(keybio, BIO_CLOSE);
BIO_free(keybio);
return false;
}
BIO_set_close(keybio, BIO_CLOSE);
BIO_free(keybio);
return true;
}
bool RSACrypto::InitPublicRSA()
{
BIO* keybio = nullptr;
keybio = BIO_new_mem_buf((char*)m_strPublicKey.data(), -1);
if (keybio == NULL)
{
printf("Failed to create public key BIO\n");
return false;
}
m_PublickeyRsa = PEM_read_bio_RSA_PUBKEY(keybio, &m_PublickeyRsa, NULL, NULL);
if (!m_PublickeyRsa)
{
printf("Failed to create public RSA\n");
BIO_set_close(keybio, BIO_CLOSE);
BIO_free(keybio);
return false;
}
BIO_set_close(keybio, BIO_CLOSE);
BIO_free(keybio);
return true;
}
bool RSACrypto::InitRsa()
{
if (!InitPrivateRSA())
{
return false;
}
if (!InitPublicRSA())
{
return false;
}
return true;
}
void RSACrypto::Release()
{
if (m_PublickeyRsa != nullptr)
{
RSA_free(m_PublickeyRsa); m_PublickeyRsa = nullptr;
}
if (m_PrivatekeyRsa != nullptr)
{
RSA_free(m_PrivatekeyRsa); m_PrivatekeyRsa = nullptr;
}
}
bool RSACrypto::EncryptByPublicKey(const std::string & src, std::string & encrypted)
{
std::string result;
const int keysize = RSA_size(m_PublickeyRsa);
std::vector<unsigned char> block(keysize);
const int chunksize = keysize - RSA_PKCS1_PADDING_SIZE;
int inputlen = src.length();
for (int i = 0; i < inputlen; i += chunksize)
{
auto resultsize = RSA_public_encrypt(std::min(chunksize, inputlen - i), (uint8_t*)&src[i], &block[0], (RSA*)m_PublickeyRsa, RSA_PKCS1_PADDING);
if (resultsize == -1)
{
return false;
}
encrypted.append((char*)block.data(), resultsize);
}
return true;
}
bool RSACrypto::DecryptByPublicKey(const std::string & encrypted, std::string & decrypted)
{
const int keysize = RSA_size(m_PublickeyRsa);
std::vector<unsigned char> block(keysize);
int inputlen = encrypted.length();
for (int i = 0; i < (int)encrypted.length(); i += keysize)
{
int flen = std::min(keysize, inputlen - i);
auto resultsize = RSA_public_decrypt(flen, (uint8_t*)&encrypted[i], &block[0], m_PublickeyRsa, RSA_PKCS1_PADDING);
if (resultsize == -1)
{
return false;
}
decrypted.append((char*)block.data(), resultsize);
}
return true;
}
bool RSACrypto::EncryptByPrivateKey(const std::string & src, std::string & encrypted)
{
std::string result;
const int keysize = RSA_size(m_PrivatekeyRsa);
std::vector<unsigned char> block(keysize);
const int chunksize = keysize - RSA_PKCS1_PADDING_SIZE;
int inputlen = src.length();
for (int i = 0; i < (int)src.length(); i += chunksize)
{
int flen = std::min<int>(chunksize, inputlen - i);
std::fill(block.begin(), block.end(), 0);
auto resultsize = RSA_private_encrypt(flen, (uint8_t*)&src[i], &block[0], m_PrivatekeyRsa, RSA_PKCS1_PADDING);
if (resultsize == -1)
{
return false;
}
encrypted.append((char*)block.data(), resultsize);
}
return true;
}
bool RSACrypto::DecryptByPrivateKey(const std::string & encrypted, std::string & decrypted)
{
const int keysize = RSA_size(m_PrivatekeyRsa);
std::vector<unsigned char> block(keysize);
for (int i = 0; i < (int)encrypted.length(); i += keysize)
{
auto resultsize = RSA_private_decrypt(std::min<int>(keysize, encrypted.length() - i), (uint8_t*)&encrypted[i], &block[0], m_PrivatekeyRsa, RSA_PKCS1_PADDING);
if (resultsize == -1)
{
return false;
}
decrypted.append((char*)block.data(), resultsize);
}
return true;
}
bool RSACrypto::SignByPrivateKey(const std::string &src, std::string& sign)
{
EVP_MD_CTX* rsa_sign_ctx = EVP_MD_CTX_create();
EVP_PKEY* pri_key = EVP_PKEY_new();
auto clean = [pri_key, rsa_sign_ctx] {
EVP_PKEY_free(pri_key);
EVP_MD_CTX_cleanup(rsa_sign_ctx);
};
//EVP_PKEY_assign_RSA(pri_key, m_PrivatekeyRsa);
EVP_PKEY_set1_RSA(pri_key, m_PrivatekeyRsa);
if (EVP_DigestSignInit(rsa_sign_ctx, NULL, EVP_sha1(), NULL, pri_key) <= 0)
{
clean();
return false;
}
if (EVP_DigestSignUpdate(rsa_sign_ctx, src.data(), src.length()) <= 0)
{
clean();
return false;
}
size_t sign_len;
if (EVP_DigestSignFinal(rsa_sign_ctx, NULL, &sign_len) <= 0)
{
clean();
return false;
}
sign.resize(sign_len);
if (EVP_DigestSignFinal(rsa_sign_ctx, (unsigned char*)sign.data(), &sign_len) <= 0)
{
clean();
return false;
}
sign.resize(sign_len);
clean();
return true;
}
bool RSACrypto::VerifyByPublicKey(const std::string &src, const std::string& sign)
{
EVP_PKEY* pub_key = EVP_PKEY_new();
//EVP_PKEY_assign_RSA(pub_key, m_PublickeyRsa);
EVP_PKEY_set1_RSA(pub_key, m_PublickeyRsa);
EVP_MD_CTX* rsa_verify_ctx = EVP_MD_CTX_create();
auto clean = [pub_key, rsa_verify_ctx] {
EVP_PKEY_free(pub_key);
EVP_MD_CTX_destroy(rsa_verify_ctx);
};
if (EVP_DigestVerifyInit(rsa_verify_ctx, NULL, EVP_sha1(), NULL, pub_key) <= 0)
{
clean();
return false;
}
if (EVP_DigestVerifyUpdate(rsa_verify_ctx, src.data(), src.length()) <= 0)
{
clean();
return false;
}
if (EVP_DigestVerifyFinal(rsa_verify_ctx, (unsigned char*)sign.data(), sign.length()) <= 0)
{
clean();
return false;
}
clean();
return true;
}
3、RSA加密与签名示例
...
//要加密的数据源
std::string _message = "hello,world";
//初始化RSA算法
RSACrypto* _RSACrypto = new RSACrypto(_publicKey, _privateKey);
_RSACrypto->InitRsa();
std::string _utf8Message;
std::string _encrypted;
std::string _encryptedBase64;
std::string verify_data;
//加密示例******************************************************************************************************
//公钥加密
//===========================================================================================
Encoder _encoder = Encoder();
//1、 src = UTF8.encode编码(消息体)
_utf8Message = _encoder.AnsiStringToUTF8String(_message);
//2、 _encrypted = RSA公钥加密(src)
bool success = _RSACrypto->EncryptByPublicKey(_utf8Message, _encrypted);
//3、 base64Str = base64.encode编码(_encrypted )
_encryptedBase64 = OpenSSL_Base64Encode(_encrypted.data(), _encrypted.length(), false);
//4、 最终verify_data = UrlEncode编码(base64Str )
verify_data = _encoder.UrlEncode(_encryptedBase64);
//===========================================================================================
//私钥签名
//===========================================================================================
std::string _sign;
//1、 _sign = RSA私钥签名(消息体)
bool success2 = _RSACrypto->SignByPrivateKey(_message, _sign);
//2、 base64Str = base64编码(_sign )
std::string _signBase64 = OpenSSL_Base64Encode(_sign.data(), _sign.length(), false);
//3、 最终verify_sign = UrlEncode编码(_signBase64 )
auto verify_sign = _encoder.UrlEncode(_signBase64);
//===========================================================================================
//得到 最终的 验证登录状态 url : finalStr
std::string finalStr = "https://api.mguwp.com/user/verifySignin?verify_data=" + verify_data + "&verify_sign=" + verify_sign + "\n";
cout << "Url:\n" << finalStr << endl;
OutputDebugStringA(LPCSTR(finalStr.data()));
//加密示例******************************************************************************************************
...
4、RSA解密与签名验证示例
...
//回调数据示例,两个参数$param_data,$sign
std::string param_data = "etRzkAmBx5dbb%2BzggET0Z5rJ6FG8Jsh2hFFHD173og3vr%2F7bwPoueJFytrDFT7g13AkXmpz3vr9sEXFymsueG9UMMPELgJQW0gKLkhj96pA%2BNQLAN53H2%2F%2Fi8Z1Rderj%2Fes59T%2FK7bNQnHS%2Fc1LK3rduAkRFmSdm%2FLACNxHIUXpzXPZtyKRxRb7BuxJX%2B31ytaVrrDRseB9NPD9DFK4TuAMJPZqSZqNFWfzZXcyqTg2a9YVYsW6NCIFoBYv5G7%2F%2FQAFcqNQpXjCUb9ISdLHRffq1fvcqYwwdDAU8FkcGXdOQ9wNjixPj3x67GkJLaYAtwzYnCDLGMIl3T%2F%2B%2FM%2B66qw%3D%3D";
std::string sign = "cPniGrltgyofwkEeQBVdADTOuOV5rNGi55VM%2FAwd%2BPRySCMqrU0DrE90gi36Q14O00A7x8DbYl2mD9wOu%2F2ZxsaSoIf5CHHjIEEL0xhuFqAA05zP3qvD9D5m8f3ru%2F3oGgRfCbjAOr%2BHHbhZcwcuBwSlGSbJEuVGd6Ia%2ByqbvWQ%3D";
RSACrypto* _RSACrypto = new RSACrypto(_publicKey, _privateKey);
_RSACrypto->InitRsa();
std::string _urlDecodeStr;
std::string _decryptedBase64;
std::string _decrypted;
Encoder _encoder = Encoder();
//解密示例******************************************************************************************************
//使用开发者私钥解密
//===========================================================================================
//1、_urlDecodeStr = UrlDecode解码(param_data)
_urlDecodeStr = _encoder.UrlDecode(param_data);
//2、_decryptedBase64 = Base64Decode解码(_urlDecodeStr)
_decryptedBase64 = OpenSSL_Base64Decode(const_cast<char*>(_urlDecodeStr.c_str()), _urlDecodeStr.length(), false);
OutputDebugStringW(LPCWSTR(_decryptedBase64.data()));
//3、_decrypted = 使用开发者私钥进行RSA解密
bool success = _RSACrypto->DecryptByPrivateKey(_decryptedBase64, _decrypted);
//4、 得到解密后的 param_data 数据 _decrypted
cout << "param_data = \n" << _decrypted << endl;
//===========================================================================================
//使用公钥验证签名
//===========================================================================================
std::string _sign;
std::string _signDeBase64;
//1、 _sign = UrlDecode解码(sign)
_sign = _encoder.UTF8UrlDecode(sign);
OutputDebugStringA(LPCSTR(_sign.data()));
//2、 _signBase64 = Base64Decode解码(_sign)
_signDeBase64 = OpenSSL_Base64Decode(const_cast<char*>(_sign.c_str()), _sign.length(), false);
OutputDebugStringA(LPCSTR("\n"));
OutputDebugStringW(LPCWSTR(_signDeBase64.data()));
//3、 使用公钥验证签名。 参数1:私钥解密后的数据,参数2:转码后的 sign
bool success2 = _RSACrypto->VerifyByPublicKey(_decrypted, _signDeBase64);
//3、 最终验证结果 = success2
cout << "验证结果:0为失败,1为成功 = \n" << success2 << endl;
//===========================================================================================
//解密示例******************************************************************************************************
...