IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C++】crypto++加密库简单使用 -> 正文阅读

[C++知识库]【C++】crypto++加密库简单使用

crypto++加密库简单使用

目录

crypto++密码学库简单使用

一、简介

二、配置

三、使用示例

1.CRC32校验

2.Base64编码

3.Blake2b

4.AES

5.RSA


一、简介

? ? ? ? crypto++是一个免费开源(公共领域)的C++密码学库,首作者叫Wei Dai(美籍华裔姓Dai)。

? ? ? ? 它包含主流的密码学方案,比如对称加密AES,非对称RSA,哈希函数SHA2等。还包含更常见但不够安全的功能,比如SHA1MD5。还有没有安全需求的常见功能,比如CRC32Base64。在官网还列出一大堆功能,读者可以自行查阅。

二、配置

? ? ? ? 官网地址:Crypto++ Library 8.6 | Free C++ Class Library of Cryptographic Schemes

? ? ? ? wiki地址:Crypto++ Wiki

? ? ? ? 作者主页:Wei Dai's Home Page

? ? ? ? github地址:GitHub - weidai11/cryptopp: free C++ class library of cryptographic schemes

? ? ? ? 通过官网可以下载库,官网有API参考,不过看wiki的教程会比较轻松。

? ? ? ? 库自带vc工程文件,在windows平台很方便使用。

三、使用示例

? ? ? ? 为了使代码简单,我们使用两个类型声明(包含<cryptlib.h>头文件,引用CryptoPP命名空间):

#include <cryptlib.h>
using namespace CryptoPP;

using p = CryptoPP::byte*;
using cp = const CryptoPP::byte*;

1.CRC32校验

? ? ? ? CRC本意为循环冗余校验(Cyclic Redundancy Check),CRC32一般用来校验数据的完整性,它输出一个32位长度的值。由于算法简单,输出值不够长,没有安全考虑,只是单纯的验证数据的完整性。比如硬盘出错、网络传输、比特翻转等造成的数据错误可以很快的检测出来,而理论上一些不法分子可以篡改文件内容,但使CRC32校验值依旧相同,从而让别人使用被篡改的文件。

#include <crc.h>
dnd::n32 HashCrc32(const Buffer& buf)
{
	n32 ret;
	CRC32 hash;
	hash.Update((cp)buf._p, buf._size);
	hash.Final((p)&ret);
	return ret;
}

bool HashCrc32Check(const Buffer& buf, n32 digest)
{
	CRC32 hash;
	hash.Update((cp)buf._p, buf._size);
	return hash.Verify((cp)&digest);
}

? ? ? ? 其中Buffer为一段内存,p为首地址,size为长度。n32为32位无符号整型

? ? ? ? 将代码中的CRC32类型,替换为CRC32C即可使用CRC32-C版本。CRC32-C在TCP/IP中使用,所以也叫Internet校验和,它比CRC32更新一些,效率也高一点,应该优先使用它。

2.Base64编码

? ? ? ? 所谓Base64编码,意译即是使用64个符号来编码。首先它编码的目标是字节流,即基本单位为字节(byte),当然在一般的C/C++代码里面,处理数据的基本单位也是字节,而不是位。所以大家常说的二进制流,比特流与字节流差异不大(但还是有区别)。

? ? ? ? 众所周知1个字节为8位,即表示范围为[0, 255],但是ascii码表并非都是可见字符。所以任意的一段内存数据并不能直接使用可见字符的形式表示出来(比如数据中的0,在ascii码中表示结束符)。

? ? ? ? 所以Base64编码将256个值用64个值表示。64个值如下:

ABCDEFGH
IJKLMNOP
QRSTUVWX
YZabcdef
ghijklmn
opqrstuv
wxyz0123
456789+/

? ? ? ? 由于64=2^6,那么一个Base64码,需要6位二进制。那么3个比特(byte)是24位,则可以用4个Base64码表示。

? ? ? ? 所以任意数据都可以通过Base64编码为可见字符,它还有两个规则:每76个字符(密文)换行;原文不是3字节的倍数补0,每补一个字节末尾添加一个=。当然3的非0余数只有1和2,所以只会出现最多两个=。

#include <base64.h>
dnd::Buffer EncodeBase64(const Buffer& buf)
{
	Buffer ret;

	string str_sink;
	StringSource ss((cp)buf._p, buf._size, true,
		new Base64Encoder(
			new StringSink(str_sink)
		) // Base64Encoder
	); // StringSource
	ret.Copy(str_sink);
	return ret;
}

dnd::Buffer DecodeBase64(const Buffer& buf)
{
	Buffer ret;

	string str_sink;
	StringSource ss((cp)buf._p, buf._size, true,
		new Base64Decoder(
			new StringSink(str_sink)
		) // Base64Encoder
	); // StringSource
	ret.Copy(str_sink);
	return ret;
}

? ? ? ? 上面的代码中,我们使用StringSink接收编码(或解码)后的数据。然后我们调用Buffer::Copy从string_sink复制了一份内存。此处的内存复制讲道理可以优化,不过暂未研究出来。

3.Blake2b

? ? ? ? 此算法用于替代不安全的MD5,更快更简单更安全。与CRC32等哈希函数一样,它的结果值是固定长度的,所以我们使用固定大小的类型来返回结果。

using Blake2b = array<byte, 64>;//长度为64字节
#include <blake2.h>
Blake2b HashBlake2b(const Buffer& buf)
{
	assert(!buf.Empty());

	Blake2b ret;
	BLAKE2b hash;
	hash.Update((cp)buf._p, buf._size);
	hash.Final((p)&ret);
	return ret;
}

bool HashBlake2bCheck(const Buffer& buf, Blake2b digest)
{
	assert(!buf.Empty());

	BLAKE2b hash;
	hash.Update((cp)buf._p, buf._size);
	return hash.Verify((cp)&digest);
}

4.AES

? ? ? ? AES全称高级加密标准(Advanced Encryption Standard),属于对称加密算法,即加密与解密均使用同一密钥。它比DES算法先进,DES加密算法已经不安全。

? ? ? ? 使用AES首先要生成密钥,它还会生成一个IV来重复使用同一个密钥(可以简单视为密码的一部分)。它的长度我们使用默认的,所以如下定义类型:

using AesKey = array<byte, 16>;
using AesIV = array<byte, 16>;

? ? ? ? 而库本身使用SecByteBlock类来保存密码,因为它释放后会清空内存,防止关键信息残留内存。这一点可具体看文档说明,这里简单起见,直接复制了出来。

#include <osrng.h>
#include <rijndael.h>
tuple<AesKey, AesIV> GenerateAES()
{
	AutoSeededRandomPool prng;

	SecByteBlock key(AES::DEFAULT_KEYLENGTH);
	SecByteBlock iv(AES::BLOCKSIZE);

	prng.GenerateBlock(key, key.size());
	prng.GenerateBlock(iv, iv.size());

	tuple<AesKey, AesIV> ret;
	AesKey& ret_key = get<0>(ret);
	AesIV& ret_iv = get<1>(ret);

	memcpy(ret_key.data(), key.data(), key.size());
	memcpy(ret_iv.data(), iv.data(), iv.size());

	return ret;
}

? ? ? ? 通过上面的函数可以生成密钥(key、iv对),如下所示,使用了std::tuple,然后我将其打印了出来。

auto [key, iv] = Crypto::GenerateAES();
debug_test(format("key:{}", toString(key)));
debug_test(format("iv :{}", toString(iv)));
key:2d9e6c261b2625eb728121e77a68fe6c
iv :f9f9178a3ca880c21895ae4b3047a68a

????????接下来是加密与解密,它俩代码基本一致,只是CBC_Mode<AES>::Encryption换为了CBC_Mode<AES>::Decryption

Buffer EncryptAES(const Buffer& buf, const AesKey& in_key, const AesIV& in_iv)
{
	SecByteBlock key((cp)in_key.data(), in_key.size());
	SecByteBlock iv((cp)in_iv.data(), in_iv.size());

	CBC_Mode<AES>::Encryption e;
	e.SetKeyWithIV(key, key.size(), iv);

	Buffer ret;
	//ArraySink是固定长度的接收缓冲区
	string str_sink;
	StringSource s((cp)buf._p, buf._size, true,
		new StreamTransformationFilter(e,
			new StringSink(str_sink)
		) // StreamTransformationFilter 
	); // StringSource 

	ret.Copy(str_sink);
	return ret;
}


Buffer DecryptAES(const Buffer& buf, const AesKey& in_key, const AesIV& in_iv)
{
	SecByteBlock key((cp)in_key.data(), in_key.size());
	SecByteBlock iv((cp)in_iv.data(), in_iv.size());

	CBC_Mode<AES>::Decryption e;
	e.SetKeyWithIV(key, key.size(), iv);

	Buffer ret;
	//ArraySink是固定长度的接收缓冲区
	string str_sink;
	StringSource s((cp)buf._p, buf._size, true,
		new StreamTransformationFilter(e,
			new StringSink(str_sink)
		) // StreamTransformationFilter 
	); // StringSource 

	ret.Copy(str_sink);
	return ret;
}

5.RSA

? ? ? ? RSA由三位作者完成,所以RSA为三位作者名字首字母的合称。RSA为非对称加密算法,它的密钥分为公钥私钥。一般公钥用于加密数据,私钥用于解密数据。但是也可以私钥加密,而公钥来解密。

? ? ? ? 私钥由自己保存,不会通过网络传输,也就不存在窃听的可能(除非主机被入侵直接获取到私钥,或者你把私钥发给别人)。

????????公钥是公开的密码,别人使用公钥对数据进行加密,然后其余人没有私钥是无法进行解密的。所以这保证了:别人发送给你的信息不会泄漏

? ? ? ? 而你私钥加密的信息,任何人有公钥都能解密,这只能证明你拥有私钥,即验证身份。但不能对信息保密。

? ? ? ? RSA算法的根基是基于大质数难以分解的数学问题,原理可以参考我这篇博客:

RSA加密原理_略游的博客-CSDN博客

? ? ? ? 假设你要与朋友交流一些不可告人的事情,实际上使用AES对称加密算法也足够了。首先生成一个AES密钥,然后两人记录下来(不能通过网络发送,存在被窃听可能)。然后将加密后的数据通过网络传输,这样别人即使窃取到数据,而不知道密钥,是无法解密的。
? ? ? ? 服务器与客户端的交互,可以生成一个临时的AES密钥,用于双方交流信息。但是它们无法提前约定密钥,所以必须使用非对称加密的魔法来传递密钥信息。

? ? ? ? 但是使用RSA发送AES密钥时,需要注意对方给予的公钥是真的。比如甲给乙要发送AES密钥,所以乙给了甲自己的公钥,等到乙接受到密文后使用乙的私钥即可解密获得AES密钥。但是这个过程,丙可以伪装自己是乙,将丙的公钥发送给甲,待甲用丙的公钥加密后,这样甲的AES密钥便会泄漏给丙。

? ? ? ? 所以现在的CA证书,便是通过CA机构认证后颁发,来证明某公钥是某人的拥有者。

? ? ? ? 代码如下,我没有直接生成密钥,而是通过参数保存,私钥是(n,d)对,而公钥是(n,e)对,pq是两个大质数,不过此处不需要使用pq:

#include <modes.h>
#include <rsa.h>
#include <integer.h>

//BigInteger就是Integer,可以自行替换
struct RsaKeyPrivate
{
	BigInteger _n;
	BigInteger _e;
	BigInteger _d;
};
struct RsaKeyPublic
{
	BigInteger _n;
	BigInteger _e;
};

tuple<RsaKeyPrivate, RsaKeyPublic> GenerateRSA()
{
	AutoSeededRandomPool rng;

	InvertibleRSAFunction params;
	params.GenerateRandomWithKeySize(rng, 3072);

	/*
	///
	// Generated Parameters
	const Integer& n = params.GetModulus();
	const Integer& p = params.GetPrime1();
	const Integer& q = params.GetPrime2();
	const Integer& d = params.GetPrivateExponent();
	const Integer& e = params.GetPublicExponent();

	///
	// Dump
	cout << "RSA Parameters:" << endl;
	cout << " n: " << n << endl;
	cout << " p: " << p << endl;
	cout << " q: " << q << endl;
	cout << " d: " << d << endl;
	cout << " e: " << e << endl;
	cout << endl;
	*/
	///
	//生成密钥
	/*RSA::PrivateKey priKey(params);
	RSA::PublicKey	pubKey(params);*/

	//返回
	tuple<RsaKeyPrivate, RsaKeyPublic> ret;
	RsaKeyPrivate& key_pri = get<0>(ret);
	RsaKeyPublic& key_pub = get<1>(ret);

	const Integer& n = params.GetModulus();
	const Integer& d = params.GetPrivateExponent();
	const Integer& e = params.GetPublicExponent();
	*((Integer*)(key_pri._n.GetImp())) = n;
	*((Integer*)(key_pri._d.GetImp())) = d;
	*((Integer*)(key_pri._e.GetImp())) = e;

	*((Integer*)(key_pub._n.GetImp())) = n;
	*((Integer*)(key_pub._e.GetImp())) = e;

	return ret;
}

? ? ? ? 在加密和解密时,再生成密钥,代码如下:

dnd::Buffer EncryptRSA(const Buffer& buf, const RsaKeyPublic& key_pub)
{
	AutoSeededRandomPool rng;

	RSA::PublicKey pubKey;

	RsaKeyPublic& key = const_cast<RsaKeyPublic&>(key_pub);
	const Integer n = *((Integer*)key._n.GetImp());
	const Integer e = *((Integer*)key._e.GetImp());
	pubKey.Initialize(n, e);

	RSAES_OAEP_SHA_Encryptor encryptor(pubKey);

	Buffer ret;
	string str_sink;
	StringSource ss((cp)buf._p, buf._size, true,
		new PK_EncryptorFilter(rng, encryptor,
			new StringSink(str_sink)
		) // PK_EncryptorFilter 
	); // StringSource 

	ret.Copy(str_sink);
	return ret;
}


dnd::Buffer DecryptRSA(const Buffer& buf, const RsaKeyPrivate& key_pri)
{
	AutoSeededRandomPool rng;
	RSA::PrivateKey priKey;

	RsaKeyPrivate& key = const_cast<RsaKeyPrivate&>(key_pri);
	const Integer& n = *((Integer*)key._n.GetImp());
	const Integer& e = *((Integer*)key._e.GetImp());
	const Integer& d = *((Integer*)key._d.GetImp());
	priKey.Initialize(n, e, d);

	RSAES_OAEP_SHA_Decryptor decryptor(priKey);

	Buffer ret;
	string str_sink;
	StringSource ss((cp)buf._p, buf._size, true,
		new PK_DecryptorFilter(rng, decryptor,
			new StringSink(str_sink)
		) // PK_DecryptorFilter 
	); // StringSource 

	ret.Copy(str_sink);
	return ret;
}

? ? ? ? 不过上面的代码会产生内存泄漏(反复使用不会产生额外的泄漏),原因参见:

Memory leak in Singleton::Ref()? · Issue #550 · weidai11/cryptopp · GitHub

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:02:42  更:2022-07-17 16:05:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 17:01:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码