cyropto++

深入浅出cryptoPP密码学库》学习笔记。crypto++库帮助文档:https://www.cryptopp.com/docs/ref/index.html

进制与编码#

以2进制,8进制,10进制,16进制字符串构造整数

Copy
Integer Int2("011111101010000b");
Integer Int8("102345676543210o");
Integer Int10("1234567890987654321");
Integer Int16("1234567890ABCDEFh");

base系列编码算法定义在各base64.h base32.h hex.h头文件中

Copy
//base64编码与解码
#include<iostream>
#include<base64.h>
#include<files.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	string message= "我我我我我我我我我我我";
	StringSource Base64String(message, true, new Base64Encoder(new FileSink(cout)));

	string base64Message = "ztLO0s7SztLO0s7SztLO0s7SztLO0g==";
	StringSource Base64String2(base64Message, true, new Base64Decoder(new FileSink(cout)));
}

ASN.1定义了一套编码标准,其中有BER,DER等,为了与PGP,openSSL兼容,cryptopp定义了对数据进行BER,DER编解码的函数
生成一个512位随机数,并保存为der格式文件

Copy
#include<iostream>
#include<osrng.h>
#include<files.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	Integer bigNumber;
	AutoSeededRandomPool rng;
	bigNumber.Randomize(rng, 512);
	bigNumber.DEREncode(FileSink("bignumber.der").Ref());
}

pipeling数据处理范式#

该范式将数据比作水流,从source经过filter最终流向sink
cryptopp将所有数据源称为source,所有数据处理类称为filter,所有能存储数据的特定数据结构称为sink

从StringSource流向HexEncoder再流向FileSink

Copy
#include<iostream>
#include<hex.h>
#include<files.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	string my;
	cin >> my;
	StringSource myCin(my, true, new HexEncoder(new FileSink("my.txt")));
}

source可以使用GET()取出数据,sink可以使用PUT()放入数据,filter可以使用GET()和PUT()取出或放入数据

Copy
#include<iostream>
#include<hex.h>
#include<files.h>
#include<secblock.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	string my;
	cin >> my;

	SecByteBlock secGet(8);//每次取8个字符
	StringSource myCin(my, true);
	HexEncoder hexEncoder;
	FileSink fileSink("my.txt");

	myCin.Get(secGet, secGet.size());
	hexEncoder.Put(secGet, secGet.size());
	hexEncoder.Get(secGet, secGet.size());
	fileSink.Put(secGet, secGet.size());
}

也可以通过Attach连接“流”的“水道”,通过Detach更改“水道”

Copy
#include<iostream>
#include<hex.h>
#include<files.h>
#include<secblock.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	string my;
	cin >> my;
	StringSource myCin(my, true);
	HexEncoder hexEncoder;
	SecByteBlock secGet(2);

	hexEncoder.Attach(new FileSink("my.txt"));
	myCin.Get(secGet, secGet.size());
	hexEncoder.Put(secGet, secGet.size());

	hexEncoder.Detach(new FileSink(cout));
	myCin.Get(secGet, secGet.size());
	hexEncoder.Put(secGet, secGet.size());

}

通过Redirector和ChannelSwitch多“水道”输出
示例中加入了不经过filter处理的,转为16进制处理的,base64处理的多个“水道”,在通过Redirector连接。

Copy
#include<iostream>
#include<hex.h>
#include<files.h>
#include<secblock.h>
#include<base64.h>
#include<channels.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	string my;
	cin >> my;

	FileSink o(cout);
	HexEncoder hexEncoder(new FileSink(cout));
	Base64Encoder base64Encoder(new FileSink(cout));
	ChannelSwitch cs;

	cs.AddDefaultRoute(o);
	cs.AddDefaultRoute(hexEncoder);
	cs.AddDefaultRoute(base64Encoder);

	StringSource myCin(my, true, new Redirector(cs));
}

工具#

计时#

Copy
//#include<hrtimer.h>
Timer tm(TimerBase::SECONDS);//设置以秒为单位
	
cout << tm.GetCurrentTimerValue() << endl;//当前计数器的值

tm.StartTimer();//开始计时
cout << tm.ElapsedTimeAsDouble() << endl;//从开始计时到现在花费时间

压缩工具#

三种压缩工具:Deflator、Gzip、ZlibCompressor
对应三种解压工具:Inflator、Gunzip、ZlibDecompressor
压缩与解压缩示例

Copy
string a = "my.txt";
FileSource fileZip(a.c_str(), true, new Gzip(new FileSink("my.zip")));
FileSource fileUnzip("my.zip", true, new Gunzip(new FileSink("my2.txt")));

数学工具#

定义在nbtheory.h中

数论#

素数

Copy
#include<iostream>
#include<nbtheory.h>
#include<osrng.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	//判断一个小素数是否为素数,能比的范围最大为32719
	Integer i("7");
	cout << IsSmallPrime(i);
	//判断是否有小素因子
	Integer j("17");
	cout << SmallDivisorsTest(j);
	//产生确定的小素数
	AutoSeededRandomPool rng;
	cout << MihailescuProvablePrime(rng, 64);//产生64比特
	cout << MaurerProvablePrime(rng, 32);//产生32比特

	//产生一定概率是素数的素数
	//按概率从低到高排
	Integer b("2");
	cout << IsFermatProbablePrime(i, b);//计算b^(i-1)=1(modi)
	cout << IsLucasProbablePrime(i);
	cout << IsStrongProbablePrime(i, b);
	cout << IsStrongLucasProbablePrime(i);
	cout << RabinMillerTest(rng, i, 10);//10轮米勒拉宾素性检测

	cout << IsPrime(i);//依次调用IsSmallPrime,IsStrongProbablePrime,IsStrongLucasProbablePrime
	cout << VerifyPrime(rng, i, 1);//依次调用IsPrime,RabinMillerTest,1表示米勒拉宾素性检测执行1轮,大于一再执行10轮
}

其它算法

Copy
	Integer a("7");
	Integer b("5");
	Integer c("3");
	cout << GCD(a, b);//求最大公约数
	cout << LCM(a, b);//最小公倍数
	cout << RelativelyPrime(a, b);//是否互素
	cout << EuclideanMultiplicativeInverse(a, b);//求amodb的逆元,逆元存在则返回逆元,不存在则返回0

	//中国剩余定理,p,q为素数,且互素,xp,xq为模p,q的值,求同余式组x=xpmodp x=xqmodq
	Integer p("7");
	Integer q("11");
	Integer xp("2");
	Integer xq("3");
	cout << CRT(xp, p, xq, q, EuclideanMultiplicativeInverse(p, q));

	cout << Jacobi(a, b);//雅克比符号,b为奇素数时,等价于计算勒让德符号,用于判断按是否为模b的二次剩余

	cout << ModularMultiplication(a, b, c);//计算a*bmodc
	cout << ModularExponentiation(a, b, c);//计算a^bmodc
	cout << ModularSquareRoot(a, c);//计算满足x^2=amodc的x,c为素数

代数#

Copy
#include<iostream>
#include<ecp.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	ECP ecp(23, 1, 1);//定义有限域上椭圆曲线y^2=x^3+1*x+1mod23
	ECP::Point P(3, 10);
	ECP::Point Q(9, 7);
	ECP::Point result = ecp.Add(P, Q);
	cout << result.x << ' ' << result.y;
	//计算10P ecp.ScalarMultiply(P,Integer(10))
	//计算ecp单位元 ecp.Identify
	//计算逆元 ecp.Inverse(P)
}

秘密分割#

定义在头文件ida.h
shamir秘密共享算法对应类SecretSharing,SecretRecovery
rabin信息传播算法对应类InformationDispersal,InformationRecovery

利用cryptopp实现shamir对文件的分存与还原

Copy
#include<iostream>
#include<ida.h>
#include<osrng.h>
#include<channels.h>
#include<files.h>
#include<string>

using namespace std;
using namespace CryptoPP;

void SecretShareFile(int threshold, int nShares, const string filename)
{
	//保证nShares合理
	CRYPTOPP_ASSERT(nShares >= 1 && nShares <= 1000);

	AutoSeededRandomPool rng;
	ChannelSwitch* channelSwitch = new ChannelSwitch;
	//file的内容流入SecretSharing中,再流入channelSwitch中
	FileSource source(filename.c_str(),false, new SecretSharing(rng, threshold, nShares, channelSwitch));

	vector_member_ptrs<FileSink> fileSinks(nShares);
	string channel;
	for (int i = 0; i < nShares; ++i)
	{
		//定义5个子文件进行分存
		fileSinks[i].reset(new FileSink((filename + "." +to_string(i) ).c_str()));
		channel = WordToString<word32>(i);
		//每个文件保存4字节的通道号,以便还原
		fileSinks[i]->Put((const byte*)channel.data(), 4);
		//再将channelSwitch分别接上这5个文件,即5个分叉的“水道”
		channelSwitch->AddRoute(channel, *fileSinks[i], DEFAULT_CHANNEL);
	}

	//“开闸放水”,即取出数据进行处理
	source.PumpAll();
}

void SecretRecoverFile(int threshold, const string& outFilename, const vector<string>& inFilename)
{
	//保证threshold合理
	CRYPTOPP_ASSERT(threshold >= 1 && threshold <= 1000);

	SecByteBlock channel(4);
	vector_member_ptrs<FileSource>fileSource(threshold);
	//SecretRecovery连接最后输出的恢复的文件
	SecretRecovery recovery(threshold, new FileSink(outFilename.c_str()));

	for (int i = 0; i < threshold; ++i)
	{
		//将每个输入的文件都连接在ChannelSwitch,再连接到SecretRecovery
		fileSource[i].reset(new FileSource(inFilename[i].c_str(), false));
		//先取出分存时前4字节的通道号
		fileSource[i]->Pump(4);
		fileSource[i]->Get(channel, 4);
		fileSource[i]->Attach(new ChannelSwitch(recovery, string((char*)channel.begin(), 4)));
	}

	//对每个输入的文件依次“开闸放水”,即取出数据
	for (int i = 0; i < threshold; i++)
	{
		fileSource[i]->PumpAll();
	}
}
int main()
{
	//秘密分存,分成5份,3份即可还原
	SecretShareFile(3, 5, "F:/code/c++/myPrimer/test.txt");

	//秘密还原
	vector<string> inFilename = { "F:/code/c++/myPrimer/test.txt.4","F:/code/c++/myPrimer/test.txt.1","F:/code/c++/myPrimer/test.txt.2" };
	SecretRecoverFile(3, "F:/code/c++/myPrimer/recoverytest.txt", inFilename);
}

随机数发生器#

分为真随机数和伪随机数,都能通过硬件和软件实现
LC_RNG(rng.h):线性同余发生器
RandomPool(randpool.h):基于AES算法构造的随机数池
AutoSeededRandomPool(osrng.h):自动调用系统的随机数池算法,利用系统自动重置种子
AutoSeededX917RNG(osrng.h):自动重置种子的ANSIX9.17伪随机数算法
RDRAND和RDSEED(rdrand):硬件芯片产生的随机数,只适用于英特尔芯片

对于一些随机算法IncorporateEntropy可以更新其内部熵

Copy
#include<iostream>
#include<rng.h>
#include<osrng.h>
#include<aes.h>
#include<fstream>
#include<hrtimer.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	//LC_RNG
	LC_RNG rng(123);//种子设为123
	cout << rng.GenerateBit();//产生1比特随机数
	cout << rng.GenerateByte();//产生1字节随机数

	byte a[32];
	rng.GenerateBlock(a, 32);//产生32字节随机数
	for (auto i : a)
	{
		cout << hex << int(i);//以16进制输出,其实这里有问题,会把前面的0省略,所以可以printf("%02X", i) ;
	}

	cout << rng.GenerateWord32(10, 100);//产生10到100间的随机数

	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	rng.Shuffle(arr, arr + 9);//打乱arr中的元素
	for (auto i : arr)
	{
		cout << i;
	}

	//AutoSeededX917RNG
	AutoSeededX917RNG<AES> rng;
	ofstream file("rand",ofstream::binary|ofstream::app);
	byte output[64];
	Timer time;

	time.StartTimer();
	for (int i = 0; i < 16; i++)
	{
		rng.GenerateBlock(output, 64);
		file << output;
	}
	cout << time.ElapsedTimeAsDouble();
	file.close();

	//也可以使用Pipeling格式
	//RandomNumberSource r(rng,64,true,new FileSink(file))
}

哈希函数#

计算字符串的哈希
但是这里中文计算的哈希是不正确的,说明此程序还有问题

Copy
#include<iostream>
#include<sha.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	SHA256 sha;

	cout << "分组长度" << sha.BlockSize()*8 << '\n';
	cout << "哈希值长度" << sha.DigestSize() * 8 << '\n';

	byte m1[] = "邃密群科济世穷";
	byte m2[] = "面壁十年图破壁";
	//分次添加带计算哈希的值
	sha.Update(m1, sizeof(m1) - 1);
	sha.Update(m2, sizeof(m2) - 1);
	//创建存放哈希值的空间
	size_t len = sha.DigestSize();
	byte* digest1 = sha.CreateUpdateSpace(len);

	sha.Final(digest1);
	cout << "分次计算哈希";
	for (size_t i=0;i< sha.DigestSize();++i)
	{
		cout << hex << int(digest1[i]);
	}
	cout << '\n';

	//-----------------------------------------
	SHA256 sha2;
	byte m3[] = "邃密群科济世穷面壁十年图破壁";

	SecByteBlock digest2(sha2.DigestSize());

	sha2.CalculateDigest(digest2, m3, sizeof(m3) - 1);
	cout << "一次计算哈希";
	for (auto i : digest2)
	{
		cout << hex << int(i);
	}
	cout << '\n';

	//------------------------------------------------
	bool res = sha2.VerifyDigest(digest1, m3, sizeof(m3) - 1);
	cout << "分次计算和一次计算哈希是否相同" << boolalpha << res;
}

计算文件的哈希
这里有个问题,如果使用c++的读取文件的方法,如果使用getline等就不能与计算哈希函数中Update的参数匹配,如果使用流的方法,就会忽略文件中的空白符,如果加上file >> noskipws使其不忽略空白符,读取时又有不清楚的错误,算出的哈希不正确。所以使用c中读取文件的方法

Copy
#include<iostream>
#include<sm3.h>
#include<fstream>

using namespace std;
using namespace CryptoPP;

int main()
{
	SM3 sm3;
	FILE* fp;
	byte buff[1024];
	SecByteBlock digest(sm3.DigestSize());
	int len;
	fopen_s(&fp, "my.txt", "rb");

	while (!feof(fp))
	{	
		len = fread(buff, sizeof(byte), 1024, fp);
		sm3.Update(buff, len);
	}

	sm3.Final(digest);
	cout << "哈希";
	for (size_t i = 0; i < sm3.DigestSize(); ++i)
	{
		printf("%02X", digest[i]);
	}
	cout << '\n';
}

pipeling处理方式

Copy
#include<iostream>
#include<fstream>
#include<files.h>
#include<filters.h>
#include<channels.h>
#include<hex.h>
#include<sm3.h>
#include<sha.h>
#include<md5.h>
#include<crc.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	SHA256 sha256;
	SHA512 sha512;
	MD5 md5;
	CRC32 crc32;
	string s1, s2, s3, s4;

	HashFilter f1(sha256, new HexEncoder(new StringSink(s1)));
	HashFilter f2(sha512, new HexEncoder(new StringSink(s2)));
	HashFilter f3(md5, new HexEncoder(new StringSink(s3)));
	HashFilter f4(crc32, new HexEncoder(new StringSink(s4)));

	ChannelSwitch cs;
	cs.AddDefaultRoute(f1);
	cs.AddDefaultRoute(f2);
	cs.AddDefaultRoute(f3);
	cs.AddDefaultRoute(f4);

	string filename = "my.txt";
	FileSource fs(filename.c_str(), true, new Redirector(cs));

	cout << "sha256: " << s1 << '\n' << "sha512: " << s2 << '\n' << "md5: " << s3 << '\n' << "crc32: " << s4 << '\n';
}

流密码#

使用pipeling

Copy
#include<iostream>
#include<salsa.h>
#include<osrng.h>
#include<secblock.h>
#include<hex.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	//定义加解密对象
	XSalsa20::Encryption en;
	XSalsa20::Decryption de;
	//定义随机数发生器
	AutoSeededRandomPool rng;
	//明文,密文,恢复的明文
	string plain = "邃密群科济世穷";
	string cipher;
	string recover;
	//用随机数生成加密用的密钥与初始向量
	SecByteBlock key(en.DefaultKeyLength());
	SecByteBlock iv(en.DefaultIVLength());
	rng.GenerateBlock(key, key.size());
	rng.GenerateBlock(iv, iv.size());

	//设置密钥与初始向量,并将加密结果以16进制编码后输出
	en.SetKeyWithIV(key, key.size(), iv, iv.size());
	StringSource enP(plain, true, new StreamTransformationFilter(en, new HexEncoder(new StringSink(cipher))));
	cout <<cipher<<'\n';
	//设置密钥与初始向量,并将加密结果以16进制解码后再解密
	de.SetKeyWithIV(key, key.size(), iv, iv.size());
	StringSource deP(cipher, true, new HexDecoder(new StreamTransformationFilter(de, new StringSink(recover))));
	cout << recover<<'\n';
}

分组密码#

除了提供基础的CBC,CFB等分组模式外,还有具有认证功能的分组模式:
CCM:CTR与CBC-MAC的组合,只支持128比特的分组
EAX:CCM的改进,对CBC-MAC做了变化,支持任意长度的分组
GCM:CTR与hash函数的组合,支持任意长度的分组
基础分组模式都定义在modes.h中,上述分组模式定义在各自名字命名的头文件中

cryptopp是通过密码算法实例化分组模式模板来实现分组加解密的
如果使用带有认证的分组模式就不能使用StreamTransformationFilter类,加解密要分别使用AuthenticatedEncryptionFilter和AuthenticatedDecryptionFilter类
以gcm模式的SM4为例

Copy
#include<iostream>
#include<osrng.h>
#include<filters.h>
#include<secblock.h>
#include<hex.h>
#include<sm4.h>
#include<gcm.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	GCM<SM4>::Encryption en;
	GCM<SM4>::Decryption de;

	string plain = "面壁十年图破壁";
	string chiper;
	string recover;

	AutoSeededRandomPool rng;
	SecByteBlock key(en.DefaultKeyLength());
	SecByteBlock iv(en.DefaultIVLength());
	rng.GenerateBlock(iv, iv.size());
	rng.GenerateBlock(key, key.size());

	en.SetKeyWithIV(key, key.size(), iv, iv.size());
	StringSource enP(plain, true, new AuthenticatedEncryptionFilter(en, new HexEncoder(new StringSink(chiper))));
	cout << chiper << '\n';

	de.SetKeyWithIV(key, key.size(), iv, iv.size());
	StringSource deC(chiper, true, new HexDecoder(new AuthenticatedDecryptionFilter(de, new StringSink(recover))));
	cout << recover << '\n';
}

如果不采用带有认证的分组模式而想使其具有认证,就必须组合加密与认证,而这又有顺序区分,以下面三种协议为例:
SSL:先对明文认证,再对明文与认证的组合加密
IPsec:先对明文加密,再对加密结果认证
SSH:先对明文加密,再对明文认证
IPSec在CBC模式下和有伪随机填充并XOR处理的流密码下是可证明安全的,其它不安全

密钥派生#

实际常常使用口令验证用户是否有访问数据的权限,但口令不适合用作密钥。所以使用基于口令的密钥函数PBKDF,产生主密钥MK,主密钥可以直接用来加解密,也可以通过密钥派生函数KDF,产生更多密钥来使用。
密钥派生函数与基于口令的密钥派生函数区别在于,其输入必须是具有密码学强度的密钥
基于口令的密钥派生函数,输入除了口令外,还需要盐值,迭代次数,及一些目的前缀(可选)。
cryptopp提供的基于口令的密钥派生算法都是以hash函数为参数的类模板,定义在hkdf.h pwdbased.h中,有HKDF,PKCS12_PBKDF等
派生示例如下,在应用中,使用口令派生密钥MK,保存派生时使用的盐值,随机生成加密的密钥DPK,MK加密DPK后保存,DPK用于加密操作。解密时,用户输入口令,根据保存的盐值与口令恢复密钥MK,然后解密得到DPK,再用DPK用于解密操作

Copy
#include<iostream>
#include<osrng.h>
#include<filters.h>
#include<secblock.h>
#include<hex.h>
#include<hkdf.h>
#include<sm3.h>
#include<files.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	string password = "123456789";

	AutoSeededRandomPool rng;
	SecByteBlock salt(128);
	SecByteBlock info(16);
	SecByteBlock derivedKey(128);
	rng.GenerateBlock(salt, salt.size());
	rng.GenerateBlock(info, info.size());

	HKDF<SM3> hkdf;
	//不同派生函数中的参数不同
	hkdf.DeriveKey(derivedKey, derivedKey.size(), (byte*)password.c_str(), password.size(), salt, salt.size(), info, info.size());
	//以十六进制输出派生的密钥
	ArraySource dkeySource(derivedKey, derivedKey.size(), true, new HexEncoder(new FileSink(cout)));
}

密钥协商#

这是带认证的diffie-hellman协议,防止中间人攻击

Copy
#include<eccrypto.h>
#include<nbtheory.h>
#include<oids.h>
#include<filters.h>
#include<files.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	AutoSeededRandomPool rng;
	PrimeAndGenerator pg;
	pg.Generate(1, rng, 512, 511);//产生p=rq+1形式的素数

	Integer p = pg.Prime();
	Integer q = pg.SubPrime();
	Integer g = pg.Generator();

	DH dh(p, q, g);
	DH2 Alice(dh);
	DH2 Bob(dh);


	//长期密钥,用于认证
	SecByteBlock A_stpri(Alice.StaticPrivateKeyLength());
	SecByteBlock A_stpub(Alice.StaticPublicKeyLength());
	//协商密钥
	SecByteBlock A_eppri(Alice.EphemeralPrivateKeyLength());
	SecByteBlock A_eppub(Alice.EphemeralPublicKeyLength());
	//长期密钥,用于认证
	SecByteBlock B_stpri(Bob.StaticPrivateKeyLength());
	SecByteBlock B_stpub(Bob.StaticPublicKeyLength());
	//协商密钥
	SecByteBlock B_eppri(Bob.EphemeralPrivateKeyLength());
	SecByteBlock B_eppub(Bob.EphemeralPublicKeyLength());
	//生成
	Alice.GenerateStaticKeyPair(rng, A_stpri, A_stpub);
	Alice.GenerateEphemeralKeyPair(rng, A_eppri, A_eppub);
	Bob.GenerateStaticKeyPair(rng, B_stpri, B_stpub);
	Bob.GenerateEphemeralKeyPair(rng, B_eppri, B_eppub);

	//密钥协商
	SecByteBlock Ashared(Alice.AgreedValueLength());
	SecByteBlock Bshared(Bob.AgreedValueLength());
	Alice.Agree(Ashared, A_stpri, A_eppri, B_stpub, B_eppub);
	Bob.Agree(Bshared, B_stpri, B_eppri, A_eppub, A_eppub);

	//输出
	Integer A, B;
	A.Decode(Ashared.BytePtr(), Ashared.size());
	B.Decode(Bshared.BytePtr(), Bshared.size());
	cout << A << '\n' << B;
}

公钥密码#

库中算法名带有IES(集成加密,公钥来分发密钥,对称密码来加密,且带有MAC算法)ES(非集成加密)SS(用来签名)PK(公钥密码相关)TF(陷门函数相关)DL(离散对数相关)

Copy
#include<iostream>
#include<osrng.h>
#include<rsa.h>
#include<filters.h>
#include<files.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	AutoSeededRandomPool rng;
	RSA::PrivateKey prikey;
	prikey.Initialize(rng, 1024);//随机产生私钥,也可以使用定义好的参数来初始化

	RSA::PublicKey pubkey(prikey);//公钥从私钥来初始化,也可以用AssignFrom()用已有对象初始化
	//或者可以使用Load() Save()从流导入或保存密钥

	//密钥各参数
	cout << "n	"<<prikey.GetModulus();
	cout << "p " << prikey.GetPrime1();
	cout << "q " << prikey.GetPrime2();
	cout << "d	" << prikey.GetPrivateExponent();
	cout << "e	" << prikey.GetPublicExponent();

	//可以利用模板使用不同填充方案的RSA,如<PKCS1v15>
	RSAES<PKCS1v15>::Decryptor dec(prikey);
	RSAES<PKCS1v15>::Encryptor enc(pubkey);

	//加解密
	string plain = "面壁十年图破壁";
	string cipher;
	StringSource encSrc(plain, true, new PK_EncryptorFilter(rng, enc,new StringSink(cipher)));
	StringSource decSrc(cipher, true, new PK_DecryptorFilter(rng, dec, new FileSink(cout)));
}

认证#

认证可以通过消息认证码与签名实现

消息认证码#

如上一节所示,实际应用中,消息认证码与加密的实现主要有三种方式
消息认证码主要有两种构造方式:
上一节中的分组方式:GCM等
基于哈希函数的方式:HMAC等

cryptopp使用与哈希函数相同的HashFilter类来计算消息认证码,同时使用HashVerificationFilter进行消息认证码的验证

以先对明文认证,再对明文与认证的组合加密为例,加密使用CBC分组的AES,认证使用基于SM3哈希的HMAC

Copy
#include<iostream>
#include<osrng.h>
#include<filters.h>
#include<secblock.h>
#include<hex.h>
#include<sm3.h>
#include<modes.h>
#include<aes.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	CBC_Mode<AES>::Encryption en;
	CBC_Mode<AES>::Decryption de;

	string plain = "面壁十年图破壁";
	string chiper;
	string recover;

	AutoSeededRandomPool rng;
	SecByteBlock key(en.DefaultKeyLength());
	SecByteBlock iv(en.DefaultIVLength());
	rng.GenerateBlock(iv, iv.size());
	rng.GenerateBlock(key, key.size());

	HMAC<SM3> hmac;
	SecByteBlock hiv(en.DefaultIVLength());
	rng.GenerateBlock(hiv, hiv.size());
	hmac.SetKey(hiv, hiv.size());

	en.SetKeyWithIV(key, key.size(), iv, iv.size());
	//第二个true表示要将消息本身传递下去
	//先哈希,再与明文拼在一起加密,再将加密结果转为16进制输出
	StringSource enP(plain, true, new HashFilter(hmac, new StreamTransformationFilter(en, new HexEncoder(new StringSink(chiper))), true));
	cout << chiper << '\n';

	de.SetKeyWithIV(key, key.size(), iv, iv.size());
	//HASH_AT_END表示MAC在消息后面,PUT_MESSAGE表示要将消息本身传递下去,THROW_EXCEPTION表示如果MAC验证不通过要抛出异常
	//先进行16进制解码,再解密,用最后的MAC进行验证
	StringSource deC(chiper, true, new HexDecoder(new StreamTransformationFilter(de, new HashVerificationFilter(hmac, new StringSink(recover), HashVerificationFilter::HASH_AT_END | HashVerificationFilter::PUT_MESSAGE | HashVerificationFilter::THROW_EXCEPTION))));
	cout << recover << '\n';
}

也可以自己利用不同的hash和加密算法构造自定义的消息认证码算法

签名#

相比于消息认证码不需要共享密钥,可以提供不可否认性
不同签名算法在实例化时可能需要提供哈希函数,或者代数结构或签名标准(如PSSR)等模板参数

Copy
#include<iostream>
#include<osrng.h>
#include<eccrypto.h>
#include<oids.h>
#include<filters.h>
#include<files.h>

using namespace std;
using namespace CryptoPP;

int main()
{
	AutoSeededRandomPool rng;
	ECNR<ECP, SHA256>::PrivateKey prikey;//指明代数结构和哈希函数
	ECNR<ECP, SHA256>::PublicKey pubkey;
	prikey.Initialize(rng, ASN1::secp160r1());
	prikey.MakePublicKey(pubkey);

	string message = "也无风雨也无晴";
	string signature;
	ECNR<ECP, SHA256>::Signer sig(prikey);
	ECNR<ECP, SHA256>::Verifier ver(pubkey);
	StringSource SigSrc(message, true, new SignerFilter(rng, sig, new StringSink(signature), true));//最后一个true表示将消息和签名一起输出,默认是false,只输出签名
	StringSource VerSrc(signature, true, new SignatureVerificationFilter(ver, new FileSink(cout), SignatureVerificationFilter::PUT_MESSAGE));
}
posted on 2022-09-13 17:10  莫水千流  阅读(108)  评论(0编辑  收藏  举报