CryptoPP C++库学习使用

本文最后更新于:2023年12月17日 下午

前言

参考书籍:《深入浅出CryptoPP密码学库》
crypto++ 密码学库 :https://github.com/weidai11/cryptopp
电子书下载 :https://gitee.com/locomotive_crypto

最近在学习比特币相关技术,在比特币系统中使用大量哈希加密、签名验证等操作,为了用代码来模拟实现比特币的运行过程,学习一个支持密码原语操作的第三方库是非常有必要的。

最为知名的密码学相关开源库应该是OpenSSL了,但是由于官网是一堆英文,学习起来太吃力,后来机缘巧合下发现了CryptoPP这个库,而且还有专门的中文书籍来讲解使用,因此就决定学习CryptoPP库,本篇论文也是基于《深入浅出CryptoPP密码学库》阅读,整理出关键章节内容,供大家参考学习。

1. CryptoPP库的安装使用这里不再详细说明,请大家自行搜索网上教程
2. 后来看到OpenSSL也有一个中文手册学习网站,也推荐给大家https://www.openssl.net.cn/

string 和 SecByteBlock类型互换

1
2
3
4
5
6
7
8
9
10
11
12
13
// SecByteBlock 转 string

SecByteBlock iv; ... // C++-style cast

std::string token = std::string(reinterpret_cast<const char*>(iv.data()), iv.size());



// string 转 SecByteBlock

std::string str; ... // C++-style cast

SecByteBlock sbb(reinterpret_cast<const byte*>(str.data()), str.size());

第四章 初识CryptoPP库

Hex编解码字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>

#include <filters.h>

#include <hex.h>

using namespace std;

using namespace CryptoPP;



int main() {

// 第一种编码方式

HexEncoder hex;

string str = "I like";

string hexstr;

hex.Detach(new StringSink(hexstr));

hex.Put(reinterpret_cast<byte*>(&str[0]),str.size()); // 注意是会追加写入的

cout << "str:" << str << endl;

cout << "hexstr:" << hexstr << endl;



// 第二种编解码写法 Source -> Filter -> Sink 这是一种Pipeline的方式

string encode,decode;

StringSource enc(str, true, new HexEncoder(new StringSink(encode)));

StringSource dec(hexstr, true, new HexDecoder(new StringSink(decode)));

cout << "encode:"<<encode << endl;

cout << "decode:"<<decode << endl;

}

第五章 随机数生成器

主要可以关注GenerateBlock方法,生成指定字节长度的随机数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include<osrng.h> // 可以使用 AutoSeededRandomPool ,该随机器不用设置种子

#include<rng.h> //包含LC_RNG算法的头文件

#include<iostream> //使用cout、cin

using namespace std; //std是C++的命名空间

using namespace CryptoPP; //CryptoPP是CryptoPP库的命名空间

#define Array_Size 64

int main()

{

//定义一个LC_RNG随机数发生器对象,并设置其种子

LC_RNG rng(time(0));

cout << "产生一个比特的随机数:" << rng.GenerateBit() << endl;

cout << "产生一个字节的随机数:" << rng.GenerateByte() << endl;

byte output[Array_Size + 1] = {0};//定义一个缓冲区



//产生Array_Size字节长度的随机数

rng.GenerateBlock(output, Array_Size); // 这里也可直接传入SecByteBlock

cout << "产生Array_Size长度的随机数(十六进制):" << endl;

for (int i = 0; i < Array_Size; ++i)

{//将获得的随机数转换成十六进制并输出

printf("%02X", output[i]);

}

cout << endl;

cout << "产生一个100到1000之间的随机数:" << rng.GenerateWord32(100, 1000) << endl;



//丢弃掉随机数发生器接下来产生的100个字节数据

rng.DiscardBytes(100);

int arry[] = { 1,2,3,4,5,6,7,8,9,10 };

rng.Shuffle(arry, arry + 10); //打乱数组arry中元素的顺序



return 0;

}

第六章 Hash函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include<sha.h> //使用SHA384

#include<filters.h>

#include<hex.h>

#include<iostream> //使用cout、cin

using namespace std; //std是C++的命名空间

using namespace CryptoPP; //CryptoPP是CryptoPP库的命名空间

int main()

{

try

{

SHA256 sha; //定义一个SHA256的类对象

byte msg[] = "I like cryptography very much";



// 使用pipeline范式

SecByteBlock tmp(msg, sizeof(msg)-1);

string r;

StringSource s1 (tmp, tmp.size(),true,

new HashFilter(sha, // 使用其他hash函数,更换一下类型即可

new HexEncoder(

new StringSink(r))));

cout << r << endl;

r.clear(); // 清空一下,不然后面会追加



// 连续两次Hash256,再用Hex编码输出

SHA256 sha2;

StringSource s2 ("I like cryptography very much",true,

new HashFilter(sha,

new HashFilter(sha2,

new HexEncoder(

new StringSink(r)))) );

cout << r << endl;



SecByteBlock digest(sha.DigestSize()); //申请内存空间以存放消息摘要

//CalculateDigest()相当于Update()+Final()

// Update用来向sha输入,Final计算hash值,同时重置hash函数内部状态

sha.CalculateDigest(digest, msg, sizeof(msg) - 1);

cout << "digest2=";

for (size_t i = 0; i < sha.DigestSize(); ++i)

{//以十六进制输出Hash值

printf("%02X", digest[i]);

}

cout << endl;



//计算msg消息的Hash值

bool res;

res = sha.VerifyDigest(digest, //可能抛出异常

msg, sizeof(msg) - 1); //去掉字符串最后的'\0'

cout << "res = " << boolalpha <<res << endl; // 这里的boolalpha 是为了输出bool值true或者false



}

catch (const Exception& e)

{//出现异常

cout << e.what() << endl; //异常原因

}



return 0;

}

第十一章 公钥密码学数学基础

大整数 与 大素数生成

  • 可以再研究一下大整数的一些用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<integer.h>//使用Integer

#include<iostream>//使用cout、cin

#include<osrng.h>//使用AutoSeededRandomPool

#include<nbtheory.h>//使用PrimeAndGenerator、VerifyPrime

using namespace std;//std是C++的命名空间

using namespace CryptoPP;//CryptoPP是CryptoPP库的命名空间

int main()

{

AutoSeededRandomPool rng; // 定义随机数发生器对象

// 定义PrimeAndGenerator对象,利用随机数发生器rng产生素数p和q

// 要求产生的p是1024比特的素数,q是512比特的素数

PrimeAndGenerator pag(1, rng, 1024, 512);

Integer p = pag.Prime(); // 获取素数p的值

Integer q = pag.SubPrime(); // 获取素数q的值

Integer r = (p - 1) / q / 2; // 计算r的值,因为p=2*r*q+1,delta=1

cout << "p(" << p.BitCount() << "):" << p << endl; // 打印p的值及比特数

cout << "q(" << q.BitCount() << "):" << q << endl; // 打印q的值及比特数

cout << "r(" << r.BitCount() << "):" << r << endl; // 打印r的值及比特数

if (VerifyPrime(rng, r, 10)) // 验证r是否为素数

{

cout << "r是素数" << endl; // 如果r为素数,则输出该信息

}

else

{

cout << "r不是素数" << endl; // 如果r不为素数,则输出该信息

}

return 0;

}

第十三章 数字签名

电子书给出的参考是ECNR数字签名算法

ECDSA椭圆曲线 - Crypto++

https://www.cryptopp.com/wiki/Elliptic_Curve_Digital_Signature_Algorithm -》关于压缩公钥的实现可以参考 Compressed Point 这一部分

私钥和公钥生成、保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include<integer.h>//使用Integer

#include<iostream>//使用cout、cin

#include<osrng.h>//使用AutoSeededRandomPool

#include<eccrypto.h>

#include <oids.h>

#include <files.h>

#include <filters.h>

#include <filesystem>

#include <hex.h>

#include <base32.h>

using namespace std;//std是C++的命名空间

using namespace CryptoPP;//CryptoPP是CryptoPP库的命名空间

void ECDSA_Generate()

{

/*

// 指定 private exponent 32字节随机数,生成对应的私钥

string exp = "E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB";



HexDecoder decoder;

decoder.Put((byte*)&exp[0], exp.size());

decoder.MessageEnd();



Integer x;

x.Decode(decoder, decoder.MaxRetrievable());



privateKey.Initialize(ASN1::secp256r1(), x);

*/



AutoSeededRandomPool prng;

ECDSA<ECP, SHA256>::PrivateKey privateKey;



privateKey.Initialize( prng, ASN1::secp256k1() );



/* 使用ByteQueue 方便将公私钥存储在内存中,如果要持久化到磁盘,可以使用FileSink

ByteQueue queue;

privateKey.Save(queue);

privateKey.Load(queue);

*/



// 验证密钥强度

bool result = privateKey.Validate( prng, 3 );

cout << boolalpha << result << endl;



Integer p = privateKey.GetPrivateExponent();

cout<< "len:" << p.BitCount() << " Private Key:" << p << endl;

string priHexKey;

HexEncoder encoder(new StringSink(priHexKey));

p.Encode(encoder, 32);

cout << "priHexKey:"<<priHexKey.size() << " " << priHexKey << endl;



// Save private key in PKCS #8 format

FileSink fs1( "../../../keys/private.ec.der", true /*binary*/ );

privateKey.Save( fs1 );



// Generate publicKey

ECDSA<ECP, CryptoPP::SHA256>::PublicKey publicKey;

privateKey.MakePublicKey(publicKey);

const ECP::Point& q = publicKey.GetPublicElement();

const Integer& qx = q.x;

const Integer& qy = q.y;

string qxHex, qyHex;

HexEncoder encoderx(new StringSink(qxHex));

HexEncoder encodery(new StringSink(qyHex));

qx.Encode(encoderx, 32);

qy.Encode(encodery, 32);

cout << "len:" << qx.BitCount() << " Public Point x:" << qx << endl;

cout << "qxHex:" << qxHex << endl;

cout << "len:" << qy.BitCount() << " Public Point y:" << qy << endl;

cout << "qyHex:" << qyHex << endl;



// Save public key in X.509 format

FileSink fs2( "../../../keys/public.ec.der", true /*binary*/ );

publicKey.Save( fs2 );



}

私钥和公钥的加载、签名和认证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// 头文件和上述一样



void ECDSA_LOAD(){

AutoSeededRandomPool prng;

FileSource fs1( "../../../keys/private.ec.der", true /*pump all*/ );

FileSource fs2( "../../../keys/public.ec.der", true /*pump all*/ );

ECDSA<ECP, SHA256>::PrivateKey privateKey;

ECDSA<ECP, SHA256>::PublicKey publicKey;

// 加载私钥, 私钥格式:PKCS #8

privateKey.Load( fs1 );

publicKey.Load(fs2);



// 用私钥进行签名

ECDSA<ECP, SHA256>::Signer signer(privateKey);

string message = "Yoda said, Do or do not. There is no try.";

string signature;





StringSource s( message, true /*pump all*/,

new SignerFilter( prng,

signer,

new StringSink( signature )

) // SignerFilter

); // StringSource

cout << "signature len:" << signature.size() << " output:" << signature << endl;



// 将签名(包含R,S)转换成 DER 格式

std::string derSign;

// Make room for the ASN.1/DER encoding

derSign.resize(3+3+3+2+signature.size());

size_t converted_size = DSAConvertSignatureFormat(

(byte*) (&derSign[0]), derSign.size(), DSA_DER,

(const byte*) (signature.data()), signature.size(), DSA_P1363);

derSign.resize(converted_size);

cout << "DER len:" << derSign.size() << " DER:" << derSign << endl;

string hexDER;

StringSource toDER(derSign, true, new HexEncoder(new StringSink(hexDER)));

cout << "DER hex:" << hexDER << endl;





// 进行验证

bool result;

ECDSA<ECP, SHA256>::Verifier verifier(publicKey);

StringSource ss( signature+message, true /*pump all*/,

new SignatureVerificationFilter(

verifier,

new ArraySink( (byte*)&result, sizeof(result) )

) // SignatureVerificationFilter

);



// 传统的C 方式 - 函数调用形式

// result = verifier.VerifyMessage( (const byte*)message.data(), message.size(), (const byte*)signature.data(), signature.size() );

if(result)

std::cout << "Verified signature on message" << std::endl;

else

std::cerr << "Failed to verify signature on message" << std::endl;



}

CryptoPP C++库学习使用
https://2017zhangyuxuan.github.io/2021/11/01/2021-11/2021-11-01 CryptoPP库使用/
作者
Zhang Yuxuan
发布于
2021年11月1日
许可协议