Crypto++安装和简单使用
一、前言
能搜索这个的估计都知道这库是干嘛用的,不知道去Wiki下。
之前在很多地方查了很多资料,但感觉有些繁琐,而且用的时候又不说为什么这么用,虽然能用,但是一脸懵。所以捣鼓了很久,感觉还是写个博客记录下自己安装和使用的过程。
讲道理,这安装是真的麻烦,本来还想在Qt上直接用的。
其实这个博客Crypto++的安装及使用讲得还不错,可以先看看这个,我刚开始也是参照它的来做的。
另外,本文是在Windows + MSVC环境下使用。
二、下载
Github下载请选择源码(也只有源码),这里我下的是zip
格式的包,下载完成后解压
。
(一)Crypto++官网
(二)Github直达8_5_0
解压完成后会有一堆东西:
三、安装
打开那个.sln
工程文件,这里我用的是Microsoft Visual Studio 2019 Pro
,所以就会一大堆黄色警告⚠,无视它。
在工程树目录(解决方案)有4个项目,找到cryptolib
,右键选择生成
:
注意,此时默认生成模式是Release
+Win32
,你的可能是Release
+x86
:
分别在Debug
和Release
模式下的Win32
|| x86
和Win64
|| 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
的运行库是/MTd
,Release
的则是/MT
,还有相关的内容,因为一会儿可能会用到。
下面开始正式使用
新建一个C++工程,应该都会吧,一个main.cpp
,一个crypto_rsa.h
,一个crypto_rsa.cpp
。不考虑类对象。
先把这个工程的配置给调整了,还记得刚才的/MTd
和MT
吗?比较看看是不是不一样,调整到和原工程一样的运行库
就行,包括设置页面下的Debug
和Release
模式的所有平台(别误以为是外面的那个模式)。
调整好了之后,在crypto_rsa.h
写上依赖,依赖路径不记得的翻回去看。
上面就是导入依赖的写法了,每个模式对应不同的lib
,下面是导入头文件,待会要用的。
好了,准备工作已经完成,看看还有哪里有问题回去一步步检查,应该说得够详细了。
五、RSA加密/解密
· 首先你得知道RSA是什么,这个知乎写得挺好的,简单明了:RSA加密及数字签名详解
· 或者更详细的:从“教科书式RSA”到“RSA-OAEP”
· 其次你得学会在官网搜索内容:官网的Wiki搜索页面
· 搜到RSA
:RSA方案
一看傻眼了,这么鬼多英文(雅思托福满分就当我没说)。
我也是挖掘了很久,毕竟我英文功底也就一丢丢,所以直接带节奏算了。
看完上面的RSA介绍,你也应该知道,RSA是买一送一的方案,创建者一次产生公钥和密钥,将公钥交给接收者,密钥自己保存,那么,接收者用公钥加密密文,只有具有密钥的创建者才能解,期间内容是不可查看的,实现了加密传输;而创建者用密钥签名内容,内容在传输过程大家都能看,怎么看?当然是用公钥解签,(用公钥)解签后的内容和直接收到的内容传输一致,证明这个内容是可信的(从创建者发送过来的),实现了签名验证。
那么,我们就需要保存这两个重要东西:密钥和公钥,那就新建一个类型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方案确定加密计算,第三行是加密并把密文放入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