Crypto++安装和简单使用RSA加密解密

一、前言

能搜索这个的估计都知道这库是干嘛用的,不知道去Wiki下。

之前在很多地方查了很多资料,但感觉有些繁琐,而且用的时候又不说为什么这么用,虽然能用,但是一脸懵。所以捣鼓了很久,感觉还是写个博客记录下自己安装和使用的过程。

讲道理,这安装是真的麻烦,本来还想在Qt上直接用的。

其实这个博客Crypto++的安装及使用讲得还不错,可以先看看这个,我刚开始也是参照它的来做的。

另外,本文是在Windows + MSVC环境下使用。

二、下载

Github下载请选择源码(也只有源码),这里我下的是zip格式的包,下载完成后解压
(一)Crypto++官网
(二)Github直达8_5_0
源码
解压完成后会有一堆东西:
解压完成

三、安装

打开那个.sln工程文件,这里我用的是Microsoft Visual Studio 2019 Pro,所以就会一大堆黄色警告⚠,无视它。
警告
在工程树目录(解决方案)有4个项目,找到cryptolib,右键选择生成
生成
注意,此时默认生成模式是Release+Win32,你的可能是Release+x86
模式
分别在DebugRelease模式下的Win32 || x86Win64 || x64生成(和上面操作一样,只是更改模式后生成),于是你就会得到4个lib,位于解压目录/Win32/Output/Debug解压目录/Win32/Output/Release解压目录/x64/Output/Debug解压目录/x64/Output/Release,当然,这是我的结果,你的可能是Win64而不叫x64
编译生成
接着在外部(磁盘随便哪)新建一个文件夹,命名为cryptopp或者自己起名。接着把刚刚解压得到的两个文件夹(每个文件夹下又有两个子文件夹)复制到新建的文件夹里。接着把原工程目录下的所有头文件.h均复制到新建文件夹,.cpp文件不用。复制完成后在新建的文件夹给分类下,比如头文件都放到新建的include文件夹中,依赖库都放到新建的lib文件夹中,如下:
放置

四、使用

回到原来的工程,打开工程选项:
工程属性
找到配置属性->C/C++->代码生成,留心你现在的模式是Release还是Debug,好了,下面你需要记住它里面的运行库是什么。比如Debug的运行库是/MTdRelease的则是/MT,还有相关的内容,因为一会儿可能会用到。
Debug
Release
下面开始正式使用

新建一个C++工程,应该都会吧,一个main.cpp,一个crypto_rsa.h,一个crypto_rsa.cpp。不考虑类对象。

先把这个工程的配置给调整了,还记得刚才的/MTdMT吗?比较看看是不是不一样,调整到和原工程一样的运行库就行,包括设置页面下的DebugRelease模式的所有平台(别误以为是外面的那个模式)。

调整好了之后,在crypto_rsa.h写上依赖,依赖路径不记得的翻回去看。
导入依赖
上面就是导入依赖的写法了,每个模式对应不同的lib,下面是导入头文件,待会要用的。

好了,准备工作已经完成,看看还有哪里有问题回去一步步检查,应该说得够详细了。

五、RSA加密/解密

· 首先你得知道RSA是什么,这个知乎写得挺好的,简单明了:RSA加密及数字签名详解
· 或者更详细的:从“教科书式RSA”到“RSA-OAEP”

· 其次你得学会在官网搜索内容:官网的Wiki搜索页面

· 搜到RSARSA方案

一看傻眼了,这么鬼多英文(雅思托福满分就当我没说)。

我也是挖掘了很久,毕竟我英文功底也就一丢丢,所以直接带节奏算了。

看完上面的RSA介绍,你也应该知道,RSA是买一送一的方案,创建者一次产生公钥和密钥,将公钥交给接收者密钥自己保存,那么,接收者用公钥加密密文,只有具有密钥的创建者才能解,期间内容是不可查看的,实现了加密传输;而创建者用密钥签名内容,内容在传输过程大家都能看,怎么看?当然是用公钥解签,(用公钥)解签后的内容和直接收到的内容传输一致,证明这个内容是可信的(从创建者发送过来的),实现了签名验证。

那么,我们就需要保存这两个重要东西:密钥公钥,那就新建一个类型key,当然,你也可以不这么做,只是产生钥匙的时候才有用,你也可以选择直接向产生钥匙的函数传递引用来修改变量获得钥匙:
新建key
紧接着,我们需要有至少5个函数:产生钥匙、加密、解密、签名、解签。
函数
这里我都是用图片不要介意,因为图片美观些,代码都放在底部。

其中,plain代表原文cipher代表密文signature代表签名recovered代表恢复的内容,这些都是相对的,比如原文加密后得到密文密文对于签名函数来说是原文,这些函数都是直接传引用,尾部就是传回值,返回值则是bool类型。

(一)生成密钥/公钥

产生钥匙
参数:key_size,钥匙的长度,通常是2048以上。
越大相对越安全,但相对的运算越久,下面是官网基于Intel Core2 Duo的例子:
时间
来看函数内第一行代码,是产生一个伪随机数rng,具体产生过程不需要我们关心。

第二、三行是定义和初始化一个参数,这个参数根据伪随机数和指定长度产生一个包含n、p、q、d、e的运算生成的结果参数。

第四、五行根据这个参数产生配对的公钥、密钥并返回这个集合,至此,钥匙产生好了。

(二)OAEP加密/解密

值得一提的是,Crypto++布置了许多加密手段,以及许多加密方案,OAEP是RSA加密的其中一种方案,另外还有PKCS1v15,推荐用OAEP。同样,签名也有两种,推荐PSS
OAEP加密
第一行是生成随机数,怎么生成不用管,只需要知道,传入参数有待加密原文公钥加密后的密文引用即可。
第二行是采用OAEP方案确定加密计算,第三行是加密并把密文放入cipher中。

下面是解密:
解密

解密原理也是一样的,生成伪随机数rng,确定用私钥的加密方案OAEP,第三行是恢复内容到recovered

测试代码:
测试加密解密
可以自行试着,把里面的keys改成keys2会发现什么都没有,事实上抛出了异常

测试结果:
测试加密结果

六、RSA加密/解密的源码

main.cpp

#include "crypto_rsa.h"
#include <iostream>


int main()
{
	key keys = generate_key();
	key keys2 = generate_key();
	const std::string plain = "HelloWorld";
	std::string cipher, recovered;

	if (rsa_encrypt(plain, keys.public_key, cipher))
	{
		if (rsa_decrypt(cipher, keys.private_key, recovered))
		{
			std::cout << "Cipher: " << std::endl;
			for (const auto& x : cipher)
			{
				std::cout
					<< std::setfill('0')
					<< std::setw(2)
					<< std::setiosflags(std::ios::uppercase)
					<< std::hex << static_cast<unsigned int>(static_cast<unsigned char>(x))
					<< " ";
			}
			std::cout << "Recovered message: " << recovered << std::endl;
		}
	}

	return 0;
}

crypto_rsa.h

#ifndef CRYPTO_RSA_H
#define CRYPTO_RSA_H

#ifdef _DEBUG
#ifdef _WIN32
#pragma comment(lib, "D:\\cryptopp\\lib\\x86\\debug\\cryptlib.lib")
#else
#pragma comment(lib, "D:\\cryptopp\\lib\\x64\\debug\\cryptlib.lib")
#endif
#else
#ifdef _WIN32
#pragma comment(lib, "D:\\cryptopp\\lib\\x86\\release\\cryptlib.lib")
#else
#pragma comment(lib, "D:\\cryptopp\\lib\\x64\\release\\cryptlib.lib")
#endif
#endif

#include "D:/cryptopp/include/pssr.h"
#include "D:/cryptopp/include/rsa.h"
#include "D:/cryptopp/include/osrng.h"

struct key
{
	CryptoPP::RSA::PrivateKey private_key;
	CryptoPP::RSA::PublicKey public_key;
};

key generate_key(unsigned int key_size = 2048);

bool rsa_encrypt(const std::string& plain, CryptoPP::RSA::PublicKey& public_key, std::string& cipher);

bool rsa_decrypt(const std::string& cipher, CryptoPP::RSA::PrivateKey& private_key, std::string& recovered);

bool rsa_signature(const std::string& plain, CryptoPP::RSA::PrivateKey& private_key, std::string& signature);

bool rsa_verify(const std::string& cipher_sign, CryptoPP::RSA::PublicKey& public_key, std::string& recovered);

#endif

crypto_rsa.cpp

#include "crypto_rsa.h"

/**
 * \brief 生成密钥对
 * \param key_size 
 * \return 
 */
key generate_key(const unsigned int key_size)
{
	CryptoPP::AutoSeededRandomPool rng;
	CryptoPP::InvertibleRSAFunction params;
	params.GenerateRandomWithKeySize(rng, key_size);
	key keys;
	keys.private_key = params;
	keys.public_key = params;

	return keys;
}

/**
 * \brief RSA加密(OAEP方案)
 * \param plain 待加密原文
 * \param public_key 公钥
 * \param cipher 密文
 * \return 加密成功或失败
 */
bool rsa_encrypt(const std::string& plain, CryptoPP::RSA::PublicKey& public_key, std::string& cipher)
{
	try
	{
		CryptoPP::AutoSeededRandomPool rng;
		const CryptoPP::RSAES_OAEP_SHA_Encryptor encrypt(public_key);
		CryptoPP::StringSource ss_1(plain, true, new CryptoPP::PK_EncryptorFilter(rng, encrypt, new CryptoPP::StringSink(cipher)));
	}
	catch (...)
	{
		return false;
	}

	return true;
}

/**
 * \brief RSA解密(OAEP方案)
 * \param cipher 密文
 * \param private_key 密钥
 * \param recovered 恢复的原文
 * \return 恢复成功或失败
 */
bool rsa_decrypt(const std::string& cipher, CryptoPP::RSA::PrivateKey& private_key, std::string& recovered)
{
	try
	{
		CryptoPP::AutoSeededRandomPool rng;
		const CryptoPP::RSAES_OAEP_SHA_Decryptor decrypt(private_key);
		CryptoPP::StringSource ss_2(cipher, true, new CryptoPP::PK_DecryptorFilter(rng, decrypt, new CryptoPP::StringSink(recovered)));
	}
	catch (...)
	{
		return false;
	}
	return true;
}

/**
 * \brief 
 * \param plain 
 * \param private_key 
 * \param signature 
 * \return 
 */
bool rsa_signature(const std::string& plain, CryptoPP::RSA::PrivateKey& private_key, std::string& signature)
{
	try
	{
		CryptoPP::AutoSeededRandomPool rng;
		const CryptoPP::RSASS<CryptoPP::PSS, CryptoPP::SHA256>::Signer signer(private_key);
		CryptoPP::StringSource ss_1(plain, true, new CryptoPP::SignerFilter(rng, signer, new CryptoPP::StringSink(signature)));
	}
	catch (...)
	{
		return false;
	}
	return true;
}

/**
 * \brief 
 * \param cipher_sign 
 * \param public_key 
 * \param recovered 
 * \return 
 */
bool rsa_verify(const std::string& cipher_sign, CryptoPP::RSA::PublicKey& public_key, std::string& recovered)
{
	try
	{
		const CryptoPP::RSASS<CryptoPP::PSS, CryptoPP::SHA256>::Verifier verifier(public_key);
		CryptoPP::StringSource ss_2(cipher_sign, true,
			new CryptoPP::SignatureVerificationFilter(verifier, 
				new CryptoPP::StringSink(recovered), CryptoPP::SignatureVerificationFilter::THROW_EXCEPTION | CryptoPP::SignatureVerificationFilter::PUT_MESSAGE));
	}
	catch (...)
	{
		return false;
	}
	return true;
}

注意:
签名和解签我还没看懂是什么操作,如果立即签名和立即解签我是看懂了,但它有一个地方:

解签
这个解签为什么是直接内容+签名,那意思是不是说,A签名消息发给B,只需要根据内容生成签名,再在前面添加内容再发送给B?

那这样的话,假设C想伪装成A,那只需要先截取A发送给B的一次消息,用公钥解开得到原文,再比较A发过来的消息,就可以去除掉原文得到签名,接着再发送消息并用刚刚得到的签名给B发送消息,这样不就很简单?按它的写法大概就是这样:
疑惑
希望看懂这签名部分内容的同学教我下怎么用~~反正我是没看懂怎么调用 😁

最后吧,官方就是这么写的,我也是这么搬的,只不过搬着搬着发现好像就是那样,所以,推荐去读官网的Wiki


End

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值