crypto++笔记

《深入浅出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 @   启林O_o  阅读(945)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示
CONTENTS