前言

网络安全中RSA的C++语言描述简单实现。


代码仓库


代码特点

纯C++语言:

  • 相对规范和整洁
  • 一定程度地面向对象
  • 使用一部分高级特性
  • 考虑优化性能

详细注释:

  • 提示规范和整洁
  • 提示面向对象
  • 提示高级特性
  • 提示优化性能
  • 解析RSA步骤(网络上大部分实现代码的含义不明确,本代码相对明确
  • 注意易错点

大(素)数讨论

  • 实际的RSA需要操作大(素)数
  • 因为大(素)数结合RSA的代码实现较复杂,所以本代码未实现大(素)数部分,(简单)实现RSA部分
  • 网络上有很多大数实现的思路和代码资料,有兴趣可以参阅

部分资料

作者理解

注意:理解中部分基于学校计算机网络安全课程的实验要求

可能性:

  • 能够实现自定义大数数据结构/类及其上的相关算法
  • 但是,将其嵌入RSA算法的实现,无论是对于实验量、实验要求时长,都相对复杂些

复杂性的体现:

  • 如果使用相对面向数学计算、大数据应用的语言,如Python,直接调现有的库,可能认为无所不能。需要学习使用库的成本,相对的,就没有对“自定义大数结构和运算”这一“造轮子、拧螺丝”的底层理解
  • 如果使用面向底层原理的语言,如C和C++,可能也有库可以直接调用,但可能没有Python那么方便使用
  • 如果想自定义大数结构和运算,就需要对底层如语言支持的数据类型有一定的规划和了解。设计的这个大数结构和运算必须能够在RSA算法中被应用,而RSA算法中稍复杂的数学算法更会限制这个大数类的设计。RSA算法中,对简单数的操作已经相对复杂,再进一步对大数操作,复杂度就提升了
  • 搜索网上单纯对大数的自定义代码实现(C和C++语言),几百行快至千行还是有的,再针对RSA算法进行重构,代码量会更多

可能的实现:

1.使用十进制数数组/向量表示大数。大数的各个位是数组中的一个元素

  • 实验要求:1024位的参数p,1024位的参数q,可能为2048位的参数n
  • 实验要求需要使用二进制进行运算,另外,相应数论中使用二进制相对十进制有更简便快捷的算法
  • 1024位二进制约为309位十进制数,2048位不太清楚。也就是说,十进制数组初始化大小是不太确定的。就C++而言,向量最好初始化大小,否则在未定义大小的基础上不断添加元素,可能会造成预先分配的向量空间不够,出现越界、向量空间扩容重分配等未知不好定位问题
  • 几百位十进制的大数运算,在加法进位、减法借位、乘法等运算中,需要对每位操作,嵌套循环、循环次数、记录借位标志等情况很复杂

2.使用二进制数数组/向量表示大数。大数的各个位是数组中的一个元素

  • 能够满足实验要求的1024位
  • 但对于可能的2048位参数n,可能也不明确向量的初始化大小
  • 千位二进制相对百位十进制,定义每位操作,嵌套循环、循环次数、记录借位标志等情况会更加复杂

3.使用2^32进制数组/向量表示大数。大数的各个位是数组中的一个元素

  • 是最可能实现的方案
  • 要求1024位二进制
  • 语言的内置类型范围最多为64位二进制,考虑到乘法运算会使位数扩大,如超过64位而表示不了,则折中选取32位二进制,可用int数据类型表示
  • 再考虑到参数>0,可用unsigned int数据类型表示
  • 即:相对于1.使用十进制,2.使用二进制,该方案使用2^32进制。数组中一个0~9范围内的数字,它的基数为2^32
  • 1024 ÷ 32 = 32,则参数pq的数组/向量长度缩减为32,大大简化各种运算时的操作
  • 但是,基数为2^32,则在语言的表现层面,最小操作粒度是2^32。比如用unsigned int数据类型的数字0表示0 × 2^32的大数,用unsigned int数据类型的数字1表示1 × 2^32的大数。而数字0-1即大数0-2^32的间的各种数字,在宏观层面应该是操作不到、不好操作的
  • 再考虑到RSA算法中,可以使用大数运算求得p、q、n参数。但是,对于后续细粒度的n - 1、n的欧拉函数 = (p - 1)(q - 1) = p × q + p + q - 1等对较小数1,应该是操作不到、不好操作的
  • 更不用考虑后续RSA其他数学算法了

总结:

  • 可能性:大数可以实现,但不好实现。可以调库也可以自定义
  • 复杂度:纯大数相对简单,考虑编程语言、为了适应RSA算法,大数结构和运算的设计相对复杂
  • 可能的实现:方案都可能可以实现,综合考虑实验目的、实验量和实验时长等问题,成本可能会很高

代码

rsa.h

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
#ifndef RSA_RSA_H_
#define RSA_RSA_H_

#include <iostream> //cout、endl、string
#include <vector> // vector

using std::string;
using std::vector;

class RSA
{
public:
RSA(); // 构造
void Encrypt(const string &plaintext_str, vector<unsigned int> &ciphertext_int); // 加密
void Decrypt(const vector<unsigned int> &ciphertext_int, string &plaintext_str1); // 解密

private:
void KeyGen(); // 密钥生成

unsigned int GetPrimeNum(); // 获取素数
bool PrimalityTest(const unsigned int &n, const unsigned int &a); // Miller-Rabin素性测试
unsigned int QuickPowMod(const unsigned int &a, const unsigned int &q, const unsigned int &n); // 蒙哥马利快速幂模运算
unsigned int QuickMulMod(const unsigned int &a, const unsigned int &b, const unsigned int &c); // 快速乘模

unsigned int ExGcd(const unsigned int &a, const unsigned int &b, unsigned int &x, unsigned int &y); // 扩展欧几里得算法
unsigned int GetMulInverse(const unsigned int &a, const unsigned int &b); // 求乘法逆元

unsigned int p_arg_; // p参数
// 提示:参数>=0,使用unsigned int更符合语义
unsigned int q_arg_; // q参数
unsigned int n_arg_; // n参数
unsigned int n_Euler_func_arg_; // n的欧拉函数参数
unsigned int e_arg_; // e参数
unsigned int d_arg_; // d参数
};

#endif // RSA_RSA_H_

rsa.cpp

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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
#include <ctime>   //time()
#include <cstdlib> //srand()、rand()

#include "rsa.h"

using std::cerr;
using std::cout;
using std::endl;

// 构造
RSA::RSA()
{
this->KeyGen(); // 密钥生成

cout << "密钥生成: \t" << endl;
cout << "参数p: \t" << this->p_arg_ << endl;
cout << "参数q: \t" << this->q_arg_ << endl;
cout << "参数n: \t" << this->n_arg_ << endl;
cout << "参数n的欧拉函数: \t" << this->n_Euler_func_arg_ << endl;
cout << "参数e: \t" << this->e_arg_ << endl;
cout << "参数d: \t" << this->d_arg_ << endl;
cout << endl;
}

// 加密
// 参数:字符串类型的明文,无符号整型的密文
void RSA::Encrypt(const string &plaintext_str, vector<unsigned int> &ciphertext_int)
{
cout << "加密:\t" << endl;
cout << "字符串类型的明文:\t" << plaintext_str << endl;

// 1.依据ASCII码将明文的字符串数据类型转换为无符号整数类型
unsigned int p = 0; // 明文分组 1个字符1个数字为1个明文分组
// 提示:要求明文分组P < 参数n,依据ASCII范围0~255必 < n,不再处理
vector<unsigned int> plaintext_int(plaintext_str.size(), 0); // 无符号整数类型的明文 1个字符为1个数字

for (int i = 0; i < plaintext_str.size(); ++i)
{
p = plaintext_str[i]; // 注意:利用自动类型转换
plaintext_int[i] = (p);
}

cout << "无符号整数类型的明文:\t";
for (int num : plaintext_int)
{
cout << num << " ";
}
cout << endl;

// 2.加密
unsigned int c = 0; // 密文分组 1个数字明文加密得1个数字密文,1个数字为1个密文分组

for (int i = 0; i < plaintext_int.size(); ++i) // 对每个明文分组,蒙哥马利快速模幂加密
{
c = this->QuickPowMod(plaintext_int[i], this->e_arg_, this->n_arg_);
ciphertext_int[i] = c;
}

cout << "无符号整数类型的密文:\t";
for (int num : ciphertext_int)
{
cout << num << " ";
}
cout << endl;
cout << endl;

return;
}

// 解密
void RSA::Decrypt(const vector<unsigned int> &ciphertext_int, string &plaintext_str1)
{
cout << "解密:\t" << endl;
cout << "无符号整数类型的密文:\t";
for (int num : ciphertext_int)
{
cout << num << " ";
}
cout << endl;

// 1.解密
unsigned int p = 0; // 明文分组 1个数字密文解密得1个数字明文,1个数字为1个明文分组
vector<unsigned int> plaintext_int(ciphertext_int.size(), 0); // 无符号整数类型的明文 1个字符为1个数字

for (int i = 0; i < ciphertext_int.size(); ++i) // 对每个密文分组,蒙哥马利快速模幂解密
{
p = this->QuickPowMod(ciphertext_int[i], this->d_arg_, this->n_arg_);
plaintext_int[i] = p;
}

cout << "无符号整数类型的明文:\t";
for (int num : plaintext_int)
{
cout << num << " ";
}
cout << endl;

// 2.依据ASCII码将明文的无符号整数类型转换为字符串数据类型
char p_str = '\0'; // 字符类型的明文分组 1个数字1个字符为1个明文分组
for (int i = 0; i < plaintext_int.size(); ++i)
{
p_str = static_cast<char>(plaintext_int[i]); // 注意:利用强制类型转换
plaintext_str1[i] = (p_str);
}

cout << "字符串类型的明文:\t" << plaintext_str1 << endl;

return;
}

// 密钥生成
void RSA::KeyGen()
{
// 1. 选择p,q。p和q为素数,p不等于q
// 注意:将随机种子提取放在循环外、相同函数外,以避免时间相近获取的随机数相同
unsigned int seed = time(nullptr); // 随机种子
srand(seed); // 设置随机种子

this->p_arg_ = this->GetPrimeNum(); // 获取p参数
this->q_arg_ = this->GetPrimeNum(); // 获取q参数

// 2. 计算n = p × q
this->n_arg_ = this->p_arg_ * this->q_arg_;
// 提示:第一次写习惯中文用了×号而不是*...

// 3. 计算n的欧拉函数 = (p - 1) × (q - 1)
this->n_Euler_func_arg_ = (this->p_arg_ - 1) * (this->q_arg_ - 1);

// 4. 选择e。 e为整数,e和n的欧拉函数互素,1 < e < n的欧拉函数
// 选择3或17或65537,e越大相对的d越小,两值比较平衡
// 注意:e和n的欧拉函数互素,不能想当然的选取
if (this->n_Euler_func_arg_ % 65537 != 0)
{
this->e_arg_ = 65537;
}
else if (this->n_Euler_func_arg_ % 17 != 0)
{
this->e_arg_ = 17;
}
else if (this->n_Euler_func_arg_ % 3 != 0)
{
this->e_arg_ = 3;
}
else // 极端几乎不可能情况
{
cerr << "无法选取参数e" << endl;
exit(EXIT_FAILURE); // 程序直接退出
}

// 5. 计算d。d × e % n的欧拉函数 = 1,d < n的欧拉函数
this->d_arg_ = GetMulInverse(this->e_arg_, this->n_Euler_func_arg_);
// 注意:对n的欧拉函数而不是参数n

return;
}

// 获取素数
unsigned int RSA::GetPrimeNum()
{
unsigned int random = 0; // 随机数
unsigned int random_odd = 0; // 随机奇数

unsigned int n = 0; // 素性测试的参数n 循环中需要重新初始化
unsigned int a = 0; // 素性测试的参数a
bool primality_test_res = false; // 一次素性测试结果 false不是素数true可能为素数
bool prime_flag = false; // 素数标志,最终素性测试结果。false0不是素数,true1可能为素数
// 提示:初始化在循环外的变量在循环中注意是否需要更新、重新初始化

while (1) // 循环
{
// 1.1随机取一个期望大小的奇数
// 1.1.1取随机数
random = rand(); // 随机数 一般是4~5位数,不超过unsigned int的表示范围

// 1.1.2取奇数
if (random % 2 == 0) // 如果是偶数,+1成为奇数
{
random_odd = random + 1;
}
else // 奇数不额外操作
{
random_odd = random;
}

// 1.2使用素性测试判断
n = random_odd;

for (int i = 0; i < 128; ++i) // 选取128个参数a,测试128次
{
// 1.2.1随机选择相关参数a。满足a为整数,1 < a < n - 1
a = rand() % (n - 1); // 0 ~ n - 2
// 注意:
// 因为运行时间段相近,第一次a取的随机数可能和n相等
// 则计算后结果必为1,而后1 + 1 = 2
// 将设置随机种子代码提取出函数后,排除该错误
if (a == 0) // 如果是0,令a = 2 > 1
{
a += 2;
}
if (a == 1) // 如果是1,令a = 2 > 1
{
++a;
}

primality_test_res = PrimalityTest(random_odd, a); // 素性测试

if (primality_test_res == true) // 一次测试结果可能为素数
{
prime_flag = true; // 标记可能为素数
}
else if (primality_test_res == false) // 只要有一次素性测试不是素数,则必不为素数
{
prime_flag = false;

break; // 不再用a测试,需要重新选取随机奇数
}
}

if (prime_flag == true) // 随机奇数可能为素数,
{
break; // 退出循环
}
// 否则随机奇数不是素数,进入循环,再重新进行1.1取随机奇数,1.2素性测试步骤
}

return random_odd; // 获得素数
}

// Miller-Rabin素性测试
bool RSA::PrimalityTest(const unsigned int &n, const unsigned int &a) // 参数:随机奇数,参数a
{
// 1.2.2找到相关参数k,q。满足n - 1 = 2 ^ k × q。k、q为整数,k > 0,q为奇数
unsigned int k = 0;
unsigned int q = n - 1;

// 提示:
// 很多算法都只说明要找到k、q,却不说怎么找
// 找k,q的代码也含糊其辞的
while ((q & 1) == 0)
{
++k;
q >>= 1;
}
// 理解:
// q & 1:即q的二进制表示与二进制位1与运算,取q二进制表示的最低位0或1
// 如101 & 1 = 101 & 001 = 001 = 1
// 如0010 & 1 = 0010 & 0001 = 0

// 在最低位中,基数2 ^ 0 = 1,所以如果值是0,则1 × 0 = 0为偶数;值是1,则1 × 1 = 1为奇数
// 所以,如果运算结果为0,则是偶数,可以提取一个因子2
// while:连续提取因子2
// 每提取一个因子2,则++k,k是因子2的计数
// q >>= 1:将q的二进制表示右移缩小,继续对最低位判断提取因子2
// 直到不能连续提取因子2,则q即为所求

// 如十进制13 - 1 = 12 = 二进制1100,在第1、2位提取因子2为2 ^ 2 = 4
// 所以12 = 2 ^ 2 × 3。k = 2,q = 3
// 如十进制7 - 1 = 6 = 二进制110,在第1位提取1个因子2为2 ^ 1 = 2
// 所以6 = 2 ^ 1 × 3。k = 1,q = 3

// 提示:注意k、q的取值条件
// 对正整数素数,除了2为偶数,其他数必为奇数
// 奇数-1必为偶数,必至少能提取1个公因子2,则k至少为1 > 0满足
// 由算法性质,知提取所有的公因子2,则结果q必为奇数满足
// 一般q数很大,所以在接下来的步骤需要用蒙哥马利快速模幂算法

// 1.2.3计算a ^ q % n
unsigned int aq_mod_n = this->QuickPowMod(a, q, n);

// cout << n << endl;
// cout << k << endl;
// cout << q << endl;
// cout << a << endl;
// cout << aq_mod_n << endl;

// 1.2.4运用二次探测定理的逆否命题判断
// 正命题大概:探测,所有解只有1或n-1,则可能为素数
// 逆否命题大概:探测,存在解不为1且不为n-1,则必定不是素数
// 可以用正命题也可以用逆否命题判断。以下用正命题和逆否命题判断
// 第一个判断条件:未探测时,a ^ q % n == 1,则可能为素数
if (aq_mod_n == 1)
{
return true;
}

// 第二个判断条件:二次探测时,只要存在不为1且不为n-1,则必定不是素数
for (int j = 0; j < k; ++j) // 0 ~ k-1
{
aq_mod_n = this->QuickPowMod(aq_mod_n, 2, n);
// 对序列二次探测 计算a ^ (q × 2 ^ j) % n = aq_mod_n ^ (2 ^ j) % n。每次循环都幂2相当于(2 ^ j)

if (aq_mod_n != 1 && aq_mod_n != n - 1)
{
return false;
}
}
return true; // 第二个判断条件:二次探测时,若没有因判断为合数而返回,则可能为素数
}

// 蒙哥马利快速幂模运算
// 参数:a ^ q % n
// 返回值:a ^ q % n
unsigned int RSA::QuickPowMod(const unsigned int &a, const unsigned int &q, const unsigned int &n)
{
// 原理:
// 幂运算性质:a ^ q = a ^ q1 × a ^ q2。q = q1 + q2
// 模运算性质:(a × b) % n = [(a % n) × (b % n)] % n
// 所以:a ^ q % n = (a ^ q1 × a ^ q2) % n = [(a ^ q1 % n) × (a ^ q2 % n)] % n
unsigned int res = 1;
unsigned int a_temp = a; // 运算中会改变a的值,暂存
unsigned int q_temp = q; // 运算中会改变q的值,暂存

// 提示:很多算法代码含糊其辞的
while (q_temp > 0)
{
if ((q_temp & 1) == 1)
{
res = this->QuickMulMod(res, a_temp, n);
}

a_temp = this->QuickMulMod(a_temp, a_temp, n);

q_temp >>= 1;
}
// 理解:
// 算法是针对十进制数的二进制表示进行运算的

// while (q_temp > 0):对比素性测试的内容:while ((q & 1) == 0)
// 这里是判断值,需要判断所有二进制位,所以只要q在后面的右移位中值不为0,就循环。而素性测试中是判断位

// if ((q_temp & 1) == 1):最低位为1时,该位有效,需要计算并更新结果
// 快速乘算法:res = (res × a_temp) % n
// 该步骤相当于每次计算单个的(a ^ q2 % n),然后和之前的(a ^ q1 % n)相乘作为新的结果
// 其中第一个res是更新结果,第二个res是之前的结果,a_temp是当前的基数
// 基数:在循环中对每一位都会更新基数(见后面步骤),在二进制表示为1时,该基数有效

// a_temp = QuickMulMod(a_temp, a_temp, n);相当于a_temp = a_temp × a_temp % n
// 如初始a_temp = 2,则不断更新为2 ^ 0 = 1,2 ^ 1 = 2
// 再进行%保证基数不超过范围

return res;
}

// 快速乘
// 参数:a * b % c
// 返回值:a * b % c
unsigned int RSA::QuickMulMod(const unsigned int &a, const unsigned int &b, const unsigned int &c)
{
// 原理:
// 同快速幂模运算,将乘法转换为加法运算
// a × b % c = [(a + a) % c] + [(a + a) % c] + ... [(a + a) % c]共b个a相加求模
unsigned int res = 0;
unsigned int a_temp = a;
unsigned int b_temp = b;

while (b_temp > 0)
{
if (b_temp & 1)
{
res = (res + a_temp) % c;
}

a_temp = (a_temp + a_temp) % c;

b_temp >>= 1;
}

return res;
}

// 扩展欧几里得算法
// 参数和返回值:
// 由欧几里得算法,求两数a和b,a >= b的最大公约数g
// 由贝祖定理,存在令a × x + b × y = g的解x和y
// 由扩展欧几里得算法,求令a × x + b × y = gcd的一组解x,y
// 该组解x和y随后用于求乘法逆元
unsigned int RSA::ExGcd(const unsigned int &a, const unsigned int &b, unsigned int &x, unsigned int &y)
{
// 大致思路:
// 由欧几里得算法,求a和b的最大公约数:gcd(a,b) = gcd(b,a % b)
// 所以是个递归过程,递归出口为右数b,即右数a % b = 0,此时左数a即为最大公约数返回
// 此时即:gcd(g,0) = g = a × x + b × y = a × 1 + b × 0
// 即在递归栈顶时,求得解x = 1,y = 0
// 随后逐层返回退出递归栈,由扩展欧几里得算法推导,将x和y逐层更新,最后求得解x和y

// 递归出口
if (b == 0)
{
x = 1;
y = 0;

return a;
}

// 递归逻辑
unsigned int g = ExGcd(b, a % b, x, y); // g是当前递归的最大公约数

// 提示:
// 将x和y从栈底到栈顶更新,即正向写代码逻辑递归
// 则返回时从栈顶到栈底更新,最后求得解
int temp = y;
y = x - (a / b) * y;
x = temp;

return g; // 返回最大公约数
}

// 求乘法逆元
// 参数:a × x % b = 1的a和b
// 返回值:a模b的乘法逆元x
unsigned int RSA::GetMulInverse(const unsigned int &a, const unsigned int &b)
{
// 大致思路:
// 若求a模b的乘法逆元x,即a × x % b = 1
// 即同余式:ax ≡ 1(mod b)
// 可转换为不定方程:a × x + b × y = 1

// 对两个素数a,b,最大公约数为1,即gcd(a,b) = 1
// 所以:a × x + b × y = 1 = gcd(a,b)
// 由贝祖定理,存在令a × x + b × y = gcd(a,b)的解x和y
// 即已知a和b,求解x

// 所以:
// 使用扩展欧几里得算法求a × x + b × y = 1的解x
// 由同余式,逆元z=(x % b + b) % b

unsigned int x = 0;
unsigned int y = 0;
unsigned int g = this->ExGcd(a, b, x, y); // 使用扩展欧几里得算法求a × x + b × y = 1的解x

x = (x % b + b) % b;
// 提示:
// x是a模b的乘法逆元,但不能保证是正数,不能保证落在(a,b)内,所以需要更新
// 这一步很多讲解都含糊其辞

// 思路:如果求得x为负数,则需要转换为正数,由模运算性质保证结果不变
// 假设x = -9,b = 20
// 由模运算性质:对负数-9 ≡ -9 % 20 ≡ 对正数x1 % 20
// 想办法将负数-9转换为正数x1,且保证模后结果恒等
// 将20转换为负数,再加上一个因子构造-9,即[20 × (-1) + 11] % 20 = -9
// 由模运算性质:-9 = -9 % 20= [20 × (-1) + 11] % 20 = {[(-20) % 20] + (11 % 20)} % 20 = [0 + (11 % 20)] % 20 = 11 % 20 = 11
// 所以新更新的x1为11

// x % b:缩小负数在(-b,0]范围
// 加上1个b能够一次性转换为正数,范围在[0,b]
// 注意如果x % b = 0时,+b = b,还需要% b使范围落在[0,b)

return x;
}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "rsa.h"

int main()
{
RSA *rsa = new RSA;

string plaintext_str("yezhening"); // 字符串类型的明文
vector<unsigned int> ciphertext_int(plaintext_str.size(), 0); // 无符号整数类型的密文
string plaintext_str1(plaintext_str.size(), '\0'); // 字符串类型的明文 解密后的明文

rsa->Encrypt(plaintext_str, ciphertext_int); // 加密得密文
rsa->Decrypt(ciphertext_int, plaintext_str1); // 解密得明文

delete rsa;

return 0;
}

结果

请添加图片描述


总结

网络安全中RSA的C++语言描述简单实现。


参考资料


作者的话

  • 感谢参考资料的作者/博主
  • 作者:夜悊
  • 版权所有,转载请注明出处,谢谢~
  • 如果文章对你有帮助,请点个赞或加个粉丝吧,你的支持就是作者的动力~
  • 文章在描述时有疑惑的地方,请留言,定会一一耐心讨论、解答
  • 文章在认识上有错误的地方, 敬请批评指正
  • 望读者们都能有所收获