• 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));
    }
    
  • 相关阅读:
    GSI发布EnCase 6.19版本
    [EnCase v7] EnCase v7 使用问题小结
    WebForm和WinForm通用的取当前根目录的方法
    存储过程示例临时表代替游标
    自定义协议的注册及程序示例(C#)
    关于System.Web.HttpContext.Current.Session 为 null的问题
    存储过程调用DTS包实现大批量数据导入
    Ext.app.SearchField在IE8中显示异常的问题
    用于 Windows Server 2003 的远程桌面连接 (Terminal Services Client 6.0) (KB925876)
    一段没有看懂的JS代码
  • 原文地址:https://www.cnblogs.com/zhoug2020/p/16689917.html
Copyright © 2020-2023  润新知