C/C++使用openssl进行摘要和加密解密(md5, sha256, des, rsa)

openssl里面有很多用于摘要哈希、加密解密的算法,方便集成于工程项目,被广泛应用于网络报文中的安全传输和认证。下面以md5,sha256,des,rsa几个典型的api简单使用作为例子。

 

算法介绍

md5:https://en.wikipedia.org/wiki/MD5

sha256:https://en.wikipedia.org/wiki/SHA-2

des: https://en.wikipedia.org/wiki/Data_Encryption_Standard

rsa: https://en.wikipedia.org/wiki/RSA_(cryptosystem)

 

工程配置

windows

 

  1. 编译openssl库,得到头文件include和链接库lib和dll
  2. 配置包含头文件目录和库目录
  3. 工程中设置链接指定的lib:fenbieshlibssl.lib,libcrypto.lib
  4. 将对应的dll拷贝到exe执行目录:libcrypto-1_1.dll, libssl-1_1.dll
linux
  1. 编译openssl库,得到头文件include和链接库a和so
  2. 配置包含头文件目录和库目录
  3. 工程中设置链接指定的lib:libcrypto.a 后者libcrypto.so

 

代码

 

#include   
#include 
#include   
#include   
#include "openssl/md5.h"  
#include "openssl/sha.h"  
#include "openssl/des.h"  
#include "openssl/rsa.h"  
#include "openssl/pem.h"  

// ---- md5摘要哈希 ---- //  
void md5(const std::string &srcStr, std::string &encodedStr, std::string &encodedHexStr)
{
	// 调用md5哈希  
	unsigned char mdStr[33] = {0};
	MD5((const unsigned char *)srcStr.c_str(), srcStr.length(), mdStr);

	// 哈希后的字符串  
	encodedStr = std::string((const char *)mdStr);
	// 哈希后的十六进制串 32字节  
	char buf[65] = {0};
	char tmp[3] = {0};
	for (int i = 0; i < 32; i++)
	{
		sprintf(tmp, "%02x", mdStr[i]);
		strcat(buf, tmp);
	}
	buf[32] = '\0'; // 后面都是0,从32字节截断  
	encodedHexStr = std::string(buf);
}

// ---- sha256摘要哈希 ---- //  
void sha256(const std::string &srcStr, std::string &encodedStr, std::string &encodedHexStr)
{
	// 调用sha256哈希  
	unsigned char mdStr[33] = {0};
	SHA256((const unsigned char *)srcStr.c_str(), srcStr.length(), mdStr);

	// 哈希后的字符串  
	encodedStr = std::string((const char *)mdStr);
	// 哈希后的十六进制串 32字节  
	char buf[65] = {0};
	char tmp[3] = {0};
	for (int i = 0; i < 32; i++)
	{
		sprintf(tmp, "%02x", mdStr[i]);
		strcat(buf, tmp);
	}
	buf[32] = '\0'; // 后面都是0,从32字节截断  
	encodedHexStr = std::string(buf);
}

// ---- des对称加解密 ---- //  
// 加密 ecb模式  
std::string des_encrypt(const std::string &clearText, const std::string &key)
{
	std::string cipherText; // 密文  

	DES_cblock keyEncrypt;
	memset(keyEncrypt, 0, 8);

	// 构造补齐后的密钥  
	if (key.length() <= 8)
		memcpy(keyEncrypt, key.c_str(), key.length());
	else
		memcpy(keyEncrypt, key.c_str(), 8);

	// 密钥置换  
	DES_key_schedule keySchedule;
	DES_set_key_unchecked(&keyEncrypt, &keySchedule);

	// 循环加密,每8字节一次  
	const_DES_cblock inputText;
	DES_cblock outputText;
	std::vector vecCiphertext;
	unsigned char tmp[8];

	for (int i = 0; i < clearText.length() / 8; i++)
	{
		memcpy(inputText, clearText.c_str() + i * 8, 8);
		DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_ENCRYPT);
		memcpy(tmp, outputText, 8);

		for (int j = 0; j < 8; j++)
			vecCiphertext.push_back(tmp[j]);
	}

	if (clearText.length() % 8 != 0)
	{
		int tmp1 = clearText.length() / 8 * 8;
		int tmp2 = clearText.length() - tmp1;
		memset(inputText, 0, 8);
		memcpy(inputText, clearText.c_str() + tmp1, tmp2);
		// 加密函数  
		DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_ENCRYPT);
		memcpy(tmp, outputText, 8);

		for (int j = 0; j < 8; j++)
			vecCiphertext.push_back(tmp[j]);
	}

	cipherText.clear();
	cipherText.assign(vecCiphertext.begin(), vecCiphertext.end());

	return cipherText;
}

// 解密 ecb模式  
std::string des_decrypt(const std::string &cipherText, const std::string &key)
{
	std::string clearText; // 明文  

	DES_cblock keyEncrypt;
	memset(keyEncrypt, 0, 8);

	if (key.length() <= 8)
		memcpy(keyEncrypt, key.c_str(), key.length());
	else
		memcpy(keyEncrypt, key.c_str(), 8);

	DES_key_schedule keySchedule;
	DES_set_key_unchecked(&keyEncrypt, &keySchedule);

	const_DES_cblock inputText;
	DES_cblock outputText;
	std::vector vecCleartext;
	unsigned char tmp[8];

	for (int i = 0; i < cipherText.length() / 8; i++)
	{
		memcpy(inputText, cipherText.c_str() + i * 8, 8);
		DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_DECRYPT);
		memcpy(tmp, outputText, 8);

		for (int j = 0; j < 8; j++)
			vecCleartext.push_back(tmp[j]);
	}

	if (cipherText.length() % 8 != 0)
	{
		int tmp1 = cipherText.length() / 8 * 8;
		int tmp2 = cipherText.length() - tmp1;
		memset(inputText, 0, 8);
		memcpy(inputText, cipherText.c_str() + tmp1, tmp2);
		// 解密函数  
		DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_DECRYPT);
		memcpy(tmp, outputText, 8);

		for (int j = 0; j < 8; j++)
			vecCleartext.push_back(tmp[j]);
	}

	clearText.clear();
	clearText.assign(vecCleartext.begin(), vecCleartext.end());

	return clearText;
}


// ---- rsa非对称加解密 ---- //  
#define KEY_LENGTH  2048               // 密钥长度
#define PUB_KEY_FILE "pubkey.pem"    // 公钥路径
#define PRI_KEY_FILE "prikey.pem"    // 私钥路径

// 函数方法生成密钥对 
void generateRSAKey(std::string strKey[2])
{
	// 公私密钥对  
	size_t pri_len;
	size_t pub_len;
	char *pri_key = NULL;
	char *pub_key = NULL;

	// 生成密钥对  
	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);
	PEM_write_bio_RSAPublicKey(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';

	// 存储密钥对  
	strKey[0] = pub_key;
	strKey[1] = pri_key;

	// 存储到磁盘(这种方式存储的是begin rsa public key/ begin rsa private key开头的)
	FILE *pubFile = fopen(PUB_KEY_FILE, "w");
	if (pubFile == NULL)
	{
		assert(false);
		return;
	}
	fputs(pub_key, pubFile);
	fclose(pubFile);

	FILE *priFile = fopen(PRI_KEY_FILE, "w");
	if (priFile == NULL)
	{
		assert(false);
		return;
	}
	fputs(pri_key, priFile);
	fclose(priFile);

	// 内存释放
	RSA_free(keypair);
	BIO_free_all(pub);
	BIO_free_all(pri);

	free(pri_key);
	free(pub_key);
}

// 命令行方法生成公私钥对(begin public key/ begin private key)
// 找到openssl命令行工具,运行以下
// openssl genrsa -out prikey.pem 1024 
// openssl rsa - in privkey.pem - pubout - out pubkey.pem

// 公钥加密  
std::string rsa_pub_encrypt(const std::string &clearText, const std::string &pubKey)
{
	std::string strRet;
	RSA *rsa = NULL;
	BIO *keybio = BIO_new_mem_buf((unsigned char *)pubKey.c_str(), -1);
	// 此处有三种方法
	// 1, 读取内存里生成的密钥对,再从内存生成rsa
	// 2, 读取磁盘里生成的密钥对文本文件,在从内存生成rsa
	// 3,直接从读取文件指针生成rsa
	RSA* pRSAPublicKey = RSA_new();
	rsa = PEM_read_bio_RSAPublicKey(keybio, &rsa, NULL, NULL);

	int len = RSA_size(rsa);
	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 rsa_pri_decrypt(const std::string &cipherText, const std::string &priKey)
{
	std::string strRet;
	RSA *rsa = RSA_new();
	BIO *keybio;
	keybio = BIO_new_mem_buf((unsigned char *)priKey.c_str(), -1);

	// 此处有三种方法
	// 1, 读取内存里生成的密钥对,再从内存生成rsa
	// 2, 读取磁盘里生成的密钥对文本文件,在从内存生成rsa
	// 3,直接从读取文件指针生成rsa
	rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);

	int len = RSA_size(rsa);
	char *decryptedText = (char *)malloc(len + 1);
	memset(decryptedText, 0, len + 1);

	// 解密函数
	int ret = RSA_private_decrypt(cipherText.length(), (const unsigned char*)cipherText.c_str(), (unsigned char*)decryptedText, rsa, RSA_PKCS1_PADDING);
	if (ret >= 0)
		strRet = std::string(decryptedText, ret);

	// 释放内存
	free(decryptedText);
	BIO_free_all(keybio);
	RSA_free(rsa);

	return strRet;
}

int main(int argc, char **argv)
{
	// 原始明文  
	std::string srcText = "this is an example";

	std::string encryptText;
	std::string encryptHexText;
	std::string decryptText;

	std::cout << "=== 原始明文 ===" << std::endl;
	std::cout << srcText << std::endl;

	// md5  
	std::cout << "=== md5哈希 ===" << std::endl;
	md5(srcText, encryptText, encryptHexText);
	std::cout << "摘要字符: " << encryptText << std::endl;
	std::cout << "摘要串: " << encryptHexText << std::endl;

	// sha256  
	std::cout << "=== sha256哈希 ===" << std::endl;
	sha256(srcText, encryptText, encryptHexText);
	std::cout << "摘要字符: " << encryptText << std::endl;
	std::cout << "摘要串: " << encryptHexText << std::endl;

	// des  
	std::cout << "=== des加解密 ===" << std::endl;
	std::string desKey = "12345";
	encryptText = des_encrypt(srcText, desKey);
	std::cout << "加密字符: " << std::endl;
	std::cout << encryptText << std::endl;
	decryptText = des_decrypt(encryptText, desKey);
	std::cout << "解密字符: " << std::endl;
	std::cout << decryptText << std::endl;

	// rsa  
	std::cout << "=== rsa加解密 ===" << std::endl;
	std::string key[2];
	generateRSAKey(key);
	std::cout << "公钥: " << std::endl;
	std::cout << key[0] << std::endl;
	std::cout << "私钥: " << std::endl;
	std::cout << key[1] << std::endl;
	encryptText = rsa_pub_encrypt(srcText, key[0]);
	std::cout << "加密字符: " << std::endl;
	std::cout << encryptText << std::endl;
	decryptText = rsa_pri_decrypt(encryptText, key[1]);
	std::cout << "解密字符: " << std::endl;
	std::cout << decryptText << std::endl;

	system("pause");
	return 0;
}


运行结果

 

 

=== 原始明文 ===
this is an example

=== md5哈希 ===
摘要字符: 乵驥!範
摘要串: 9202816dabaaf34bb106a10421b9a0d0
=== sha256哈希 ===
摘要字符: 訪X5衽鄁媫j/醢?17?P?4膡zD
摘要串: d44c035835f1c5e0668b7d186a2ff5b0
=== des加解密 ===
加密字符:
?/灲取鮋t8:夽U錺?说
解密字符:
this is an example

=== rsa加解密 ===
公钥:
-----BEGIN RSA PUBLIC KEY-----
MIIBCAKCAQEA59WESdYbPsD6cYATooC4ebClTpvbTsu3X29Ha0g31kW3AmLR2zLj
hMvdWjUhhVuM7xBoh3Ufoyj4jTGHVhunFfbzxNrt1Nb64N95bZH8e9u6LjJYqh4e
sNoFknG+McjoSLNqGW9Yd8ejKH1Ju6C9SBUcC43XbB3XdC2matgV1zTsKhqjuywm
gVN9DZdo2TlZkqsvOHC23rbQ+lP09rpQJ/RI4NQSnCUBqQxErCN85trcWRj1zyJA
WaBZSvKh7J5RJcrC2ByMDmL7jrDDZl7YEolyW93SSc4xTE9Dr20OXznXNDsfQc9r
RQHBri8Aqsu4WW3tHSBRmjW5kxFMxS4qxwIBAw==
-----END RSA PUBLIC KEY-----

私钥:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA59WESdYbPsD6cYATooC4ebClTpvbTsu3X29Ha0g31kW3AmLR
2zLjhMvdWjUhhVuM7xBoh3Ufoyj4jTGHVhunFfbzxNrt1Nb64N95bZH8e9u6LjJY
qh4esNoFknG+McjoSLNqGW9Yd8ejKH1Ju6C9SBUcC43XbB3XdC2matgV1zTsKhqj
uywmgVN9DZdo2TlZkqsvOHC23rbQ+lP09rpQJ/RI4NQSnCUBqQxErCN85trcWRj1
zyJAWaBZSvKh7J5RJcrC2ByMDmL7jrDDZl7YEolyW93SSc4xTE9Dr20OXznXNDsf
Qc9rRQHBri8Aqsu4WW3tHSBRmjW5kxFMxS4qxwIBAwKCAQEAmo5YMTlnfytRoQAN
FwB6+8sY3xKSNIfPlPTaR4V6jtkkrEHhPMyXrd0+PCNrrj0In2BFr6NqbMX7CMuv
jr0aDqSigzyejeSnQJT7nmFS/T0myXblxr6/IJFZDEvUITCa2yJGu5+QT9psxajb
0mso2ri9XQk6SBPk+B5u8eVj5Myt4tqpWL0DEEDzwfhihs+uEGM7g6bPvQBI4JXu
8uxfSRUkpyZ5s1koEhqj+RCguksPzSWO/Ut2Sd60iOUMRhya2aEbAyRTtfhsXja3
4NMWjXorJ0SRkryM1iLJvVWkhkcr2vShH9rm9qz16BkrkI9/9Yx++GNNr6VU/p/+
Waa8CwKBgQD4m0ryXi6rCqazdCICGoZJGzaljApOZ1rWOiotM9TekaYE7tZ2NDAT
eytiCzxvs4/+1Jt5XzdGJ035VJKSai/n2ZzAq1YYtVHy5CG2olmeFtwaIWU18m2s
RjHQf/FiscVB4XdKrHjh3gLgSB8MWMDg/krisxT86HNyp1UE2jZv+QKBgQDuuoez
V+H23ktb9oDS9HuLXt+wZuww29uNb0jhVoLiqK6M90Pl2u8yErjsq04cG9pF0MUl
8/nIw4RRKQh9GUOBBbxZqA/1yBxmHTz48siYJ3YXf5HB+0WxxOlEk3s05AnTilTi
5Y4u9Ptwieoy+TOXatBL9XZgKkpHbcxKZH2gvwKBgQClvNyhlB8cscR3osFWvFmG
EiRuXVw0ROc5fBweIo3ptm6t9I75eCAM/MeWsihKd7VUjbz7lM+EGjP7jbcMRsqa
kRMrHOQQeOFMmBZ5wZEUDz1mwO4j9vPILsvgVUuXIS4r66TccvtBPqyVhWoIOytA
qYdBzLiomvehxONYkXmf+wKBgQCfJwUiOpaklDI9TwCMov0HlJUgRJ115+ezn4Xr
jwHscHRd+i1D50ohYdCdx4loEpGD4INuoqaF162LcLBTZi0Arn2RGrVOhWhEE337
TIW6xPlk/7aBUi52g0Ytt6d4mAaNBuNB7l7J+KegW/F3UM0PnIrdTk7qxtwvnogx
mFPAfwKBgAEuRGqF2Q9bNu/r0OufeFxsYm0zFvWBIxbq3DxPYRtzfhiQMeTOzl1g
5rowAtb/w1SusGAZ4/lEUZoBgzV+8fr+rpx3eavVCmcXBVjDi9B5nNLIXWkcoEQG
G/4ZwXUr5kyTBktL6mIBVNJ8dJUQo8xyxK0GjfWhlsk5t/Zu8tmK
-----END RSA PRIVATE KEY-----

加密字符:
佷篒?z_&欗霐嗪K赸;J╄[i9?S絑?て晄p?[hD∞51鱠,k|1裡郿     犓鈪鑒?饞w2?`vlu
L<萿囂?圖L潥?O0佲y▃飕E堿^桮??,e鉀煯ACsJ挈R聡-鳊帔!eQC乥+1\(齀
я盈Xj饮[o6覾羂≯傁澓
解密字符:
this is an example

 

 

 

注:

(1)在读取公钥文件时,PEM_read_RSA_PUBKEY()函数和PEM_read_RSAPublicKEY()的疑惑。有时候为什么读取私钥文件用的PEM_read_RSAPrivateKey(),针对上述openssl命令生成的公钥文件,在读取其内容时用对称的PEM_read_RSAPublicKEY()接口却会报错,必须要用PEM_read_RSA_PUBKEY()才可以。

          RSA PUBLIC KEY和PUBLIC KEY的两种公钥文件其存储方式是不一样的,PEM_read_RSAPublicKEY()只能读取RSA PUBLIC KEY开头形式的公钥文件(用函数生成的);而PEM_read_RSA_PUBKEY()只能读取PUBLIC KEY开头格式的公钥文件(用命令行生成),所以公钥私钥读取函数一定要跟生成的密钥对的格式对应起来。

(2)公钥加密和私钥解密, 私钥加密公钥解密 这两种都可以使用

(3)一般加密之后的字符串因为编码跟中文对应不上所以是乱码,在很多场合选择用十六进制串输出

 

(4)实际的工程应用中读取密钥对需要加安全验证

(5)用纯代码不依赖openssl库也是可以自己实现这些加解密算法的,搞清楚原理就行

你可能感兴趣的:(C/C++)