RSA算法-C++

笔记-RSA数字签名算法-Cpp-OpenSSL

Posted by Replay on October 31, 2018

前言

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;
//===========================================================================================
//解密示例******************************************************************************************************

...

Thanks

程默的博客

RSA公钥文件解密密文的原理分析

用OpenSSL 做Base64 编解码(C++)