RSA算法原理转自:https://www.cnblogs.com/idreamo/p/9411265.html

C++代码实现部分为本文新加

RSA算法简介

RSA是最流行的非对称加密算法之一。也被称为公钥加密。它是由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)在1977年一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

RSA是非对称的,也就是用来加密的密钥和用来解密的密钥不是同一个。

和DES一样的是,RSA也是分组加密算法,不同的是分组大小可以根据密钥的大小而改变。如果加密的数据不是分组大小的整数倍,则会根据具体的应用方式增加额外的填充位。

RSA作为一种非对称的加密算法,其中很重要的一特点是当数据在网络中传输时,用来加密数据的密钥并不需要也和数据一起传送。因此,这就减少了密钥泄露的可能性。RSA在不允许加密方解密数据时也很有用,加密的一方使用一个密钥,称为公钥,解密的一方使用另一个密钥,称为私钥,私钥需要保持其私有性。

RSA被认为是非常安全的,不过计算速度要比DES慢很多。同DES一样,其安全性也从未被证明过,但想攻破RSA算法涉及的大数(至少200位的大数)的因子分解是一个极其困难的问题。所以,由于缺乏解决大数的因子分解的有效方法,因此,可以推测出目前没有有效的办法可以破解RSA。

RSA算法基于的原理,基本上来说,加密和解密数据围绕着模幂运算,这是取模计算中的一种。取模计算是整数计算中的一种常见形式。x mod n的结果就是x / n的余数。比如,40 mod 13 = 1,因为40 / 13 = 3,余数为1。模幂运算就是计算ab mod n的过程。

计算公钥和私钥

RSA中的公钥和私钥需要结合在一起工作。公钥用来对数据块加密,之后 ,只有对应的私钥才能用来解密。生成密钥时,需要遵循几个步骤以确保公钥和私钥的这种关系能够正常工作。这些步骤也确保没有实际方法能够从一个密钥推出另一个。

开始前,首先要选择两个大的素数,记为p和q。根据当今求解大数因子的技术水平,这两个数应该至少有200位,这们在实践中才可以认为是安全的。

然后,开始计算n:

n = pq

接下来,选择一个小的奇数e,它将成为公钥的一部分。选择e最需要考虑的重点是它与(p-1)(q-1)不能有相同的因子。换句话说,e与(p-1)(q-1)是互为素数关系的。比如,如果p=11而q=19,那么n=11 X 19=209。这里选择e=17,因为(p-1)(q-1)=10 X 18 =180,而17和180没有相同的因子。通常选择3、17、65、537作为e的值。使用这些值不会对RSA的安全性造成影响,因为解密数据还需要用到私钥。

一旦为e选择了一个值,接下来开始计算相对应的值d,d将成为私钥的一部分。d的值就是计算e的倒数对(p-1)(q-1)的取模结果,公式如下:

d = e-1 mod (p-1)(q-1)

这里d和e是模乘法逆元的关系。

思考一下这个问题:当d为多少时可以满足ed mod (p-1)(q-1) = 1 ?比如在等式 17d mod 180 = 1中,d的一个可能值是53。其他的可能值是233、413、593等。在实践中,可以利用欧几里德算法来计算模乘法逆元。这里就不再展开。

现在有了e和d的值,将(e,n)作为公钥P将(d,n)作为私钥S并保持其不可见。表示为:

P = (e,n) , S = (d,n)

加密方使用P来加密数据,解密方使用S来解密。为了防止就算有人知道了P也无法推算出S,必须保证p和q的值绝对不能暴露

P和S结合在一起提供的安全性来自于一个事实,那就是乘法是一种很好的单向函数。

单向函数是加密技术的基础简单的说,单向函数就是在一个方向上能够很容易算出结果,但反向推导则是不切实际的。比如,在RSA算法中,计算p和q的成绩是一种单向函数,因为尽管计算p和q的成绩很容易,但将n反向因子分解为p和q则是极其耗时的。这里,选择的p和q的值要足够大才可以。

计算P和S的步骤起源于欧拉函数中的一些有趣性质。特别是,这些性质允许对模幂运算做一些有用的操作。

欧拉函数记为φ(n),定义所有小于n的正整数里和n互素的整数的个数

只有当两个整数的唯一公因子为1时,才说这两个整数是互素的。例如,φ(8)=4,因为一共只用4个比8小的整数是互素的,它们是1,3,5,7。

欧拉方程有两个性质对RSA算法来说是特别重要的。

第一,当n是素数时,φ(n)=n-1。这是由于n的唯一因子是1和n,因此,n与之前的所有n-1个正整数都是互素的。

另一个有趣的性质是对于任意小于n且与n互素的正整数a,都有aφ(n) mod n = 1。例如,14 mod 8 = 1, 34 mod 8 = 1, 54 mod 8 = 1, 74 mod 8 = 1。对上述方程两边都乘以a,得到:

(a)(aφ(n) mod n)=a,或者aφ(n)+1 mod n = a

因此,可以得到15 mod 8 = 1, 35 mod 8 = 3, 55 mod 8 = 5, 75 mod 8 = 7。

调整之后得到的等式非常强大。因为对于某些等式c = me mod n,该等于可以让我们找出一个d的值,使得cd mod n = m。

这就是RSA算法中允许加密数据,之后再解密回原文的恒等式。可以按照如下方式表示:

cd mod n = (me)d mod n = med mod n = mφ(n)+1 mod n = m mod n

欧拉函数和指数间的关系保证了加密的任意数据都能够唯一地解密回来。为了找出d的值,解方程d = e-1 φ(n) +1。不巧的是,对于方程d = e-1φ(n)+1不一定总是有整数解。为了解决这种问题,转而计算

d mod φ(n)的值。换句话说,d = (e-1 φ(n) + 1) mod φ(n),可以简化为:

d = e-1 mod φ(n)

我们可以得到这样的简化形式,因为(φ(n)+1) mod φ(n) = (φ(n)+1) - φ(n) = 1。可以用任意的正整数替代φ(n)来证明等式成立。注意这个方程式同之前计算密钥的过程中得出d的推导式之间的相似之处。这提供了一种通过e和n来计算d的方法。当然了,e和n是公开的,对于攻击者来说是事先可知的,因此就有人问,这难道不是给了攻击者相同的机会来计算出私钥吗?讨论到这里,是时候来探讨一下RSA算法安全性保障的由来了。

RSA算法的安全性保障来自一个重要的事实,那就是欧拉函数是乘法性质的。这意味着如果p和q是互素的,那么有φ(pq)=φ(p)φ(q)。因此,如果有两个素数p和q,且n=p*q,则φ(n)=(p-1)(q-1),而且最重要的是:

d = e-1 mod (p-1)(q-1)

因此,尽管攻击者可能知道了e和n的值,为了计算出d必须知道φ(n),而这又必须同时得到p和q的值才能办到。由于p和q是不可知的,因此攻击者只能计算n的因子,只要给出的p和q的值足够大,这就是一个相当耗费时间的过程。

加密和解密数据分组

要使用RSA算法对数据进行加密和解密,首先要确定分组的大小。为了实现这一步,必须确保该分组可以保存的最大数值要小于n的位数。比如,如果p和q都是200位数字的素数,则n的结果将小于400位。因而,所选择的分组所能保存的最大值应该要以是接近于400。在实践中,通常选择的位数都是比n小的2的整数次幂。比如,如果n是209,要选择的分组大小就是7位,因为27 = 128比209小,但28 = 256又大于209。

要从缓冲区M中加密第(i)组明文Mi ,使用公钥(e,n)来获取M的数值,对其求e次幂,然后再对n取模。这将产生一组密文Ci对n的取模操作确保了Ci将和明文的分组大小保持一致。因而,要加密明文分组有:

Ci = Mie mod n

之前提到过,欧拉函数是采用幂模运算来加密数据的基础,根据欧拉函数及其推导式,能够将密文解密回原文。

要对缓冲区中C中的第(i)组密文进行解密,使用私钥(d,n)来获取Ci的数值部分,对其求d次幂,然后再对n取模。这将产生一组明文Mi。因此,要解密密文分组有:

Mi = Cid mod n

RSA的接口定义

rsa_encipher


 void rsa_encipher(Huge plaintext, Huge *ciphertext, RsaPubKey pubkey);

返回值:无

描述采用RSA算法来加密由plaintext所指定的明文分组

pubkey是所指定的公钥(e,n),其类型为结构体RsaPubKey。

ciphertext是返回的同plaintext同样大小的密文分组。由调用者负责管理ciphertext所关联的存储空间。要加密大段的数据,可以按照前面介绍的分组加密模式来调用rsa_encipher。

复杂度:O(1)

rsa_decipher


 void rsa_decipher(Huge ciphertext, Huge *plaintext, RsaPriKey prikey)

返回值:无

描述:采用RSA算法来解密由ciphertext所指定的密文分组。

prikey是所指定的私钥(d,n),其类型为结构体RsaPriKey。

plaintext是返回的同ciphertext同样大小的明文分组。由调用者负责管理plaintext所关联的存储空间。要解密大段的数据,可以按照前面介绍的分组加密模式来调用rsa_decipher。

复杂度:O(1)

RSA算法的实现与分析

因为RSA加密算法需要的只不过是计算ab mod n,所以基本的实现是比较简单的。关键的是计算幂模的函数。

但是,要使RSA的安全性得到保障,必须使用很大的整数,这就使得事情变得复杂了。特别是,所有的计算中使用到的整数位数必须是密钥位数的2倍(稍后将在幂模计算中看到)。因此,如果密钥是一个200位的整数,就需要一个抽象数据类型来支持至少400位的整数。

关于大数运算已经有一些可用的函数库。这里不再提供具体的实现。在数据加密头文件的示例中,定义了数据类型Huge,在安全的实现中,可以为Huge类型指定typedef别名以支持所选择的大整数抽象数据类型。其他的需求就只剩下替换整数计算中的运算符为Huge类型所支持的操作。为了达到说明的目的,在这里的实现中Huge类型用typedef定义为unsigned long,这种C语言内置的类型通常只能提供10位十进制数的支持。这意味着稍后的实现示例给出的实现只能支持最多5位整数的密钥。因此,虽然示例中的实现是可用的,但如果不把Huge重定义为大数类型,这个实现就不是安全的。

rsa_encipher

rsa_encipher函数采用RSA算法将明文分组加密。

通过调用函数modexp来计算ab mod n的值,这里a代表明文分组,b和n代表公钥的e和n成员。为了提高执行效率,modexp使用称为二进制平方-乘的算法来计算模幂。

二进制平方-乘算法避免了当a和b都很大时计算ab时会出现的超大中间值。比如,假设当a、b和n都是包含200位数字的超大整数,计算ab mod n,其结果是一个40 000位的整数对200位的整数取模!因为最终得到的结果就是一个200位的整数,那么这里的目的就是要避免出现40 000位的整数的中间值。

用二进制平方-乘算法计算ab mod n,主要是多个开平方运算的结果(如下图)。首先,将b表示为二进制形式,然后从右边的位开始处理对于b中的每一位,求出a的平方对n取模的结果,并将这个结果赋给a每当遇到b中为1的位时,就将当前的a值乘上另一个寄存器y(初始值为1),并将结果再赋给y一旦迭代至b的最高有效位,y的值就是ab mod n的结果。在整个计算过程中,产生的最大值是a2。因此,如果a是一个包含200位数字的整数,就不用处理大于400位的数字了。这相对于前面提到过的包含40 000位数字的整数来说已经是很大的优化了。下图中的阴影部分展示了计算511 mod 53 = 48 828 125 mod 53 = 20的过程。在这个计算过程中,相比511 = 48 828 125,所处理的最大数值只有422 =  1764。

图示:采用二进制平方-乘算法来计算模幂

rsa_encipher的时间复杂度为O(1),因为加密一个明文分组的所有步骤都能在恒定的时间内完成。由于分组的大小是固定的,因此modexp中的循环执行时间也是恒定的。

rsa_decipher

rsa_decipher函数采用RSA算法将密文分组进行解密

该操作通过调用modexp来解密。modexp计算ab mod n的结果,这里a是密文分组,b和n代表私钥成员d和n。处理过程同rsa_decipher中描述的一样。

rsa_decipher的时间复杂度为O(1),因为解密密文分组的所有步骤都可以在恒定的时间内完成。由于分组大小固定,因此,modexp中的循环执行时间也是恒定的。

C++代码实现:

下面代码只是rsa加密算法的简单实现,其并未涉及大数处理(根据上面的介绍我们已经得知只有数位足够大才能保证安全性,所以此代码并不完整)

此代码下方为涉及大数的完整RSA代码

  1. 1 #include <iostream>
  2. 2 #include <stdio.h>
  3. 3 #include <stdlib.h>
  4. 4 #include <ctype.h>
  5. 5 #include<string.h>
  6. 6 #include <math.h>
  7. 7 #include<algorithm>
  8. 8 using namespace std;
  9. 9 typedef long long ll;
  10. 10 int e, d, n;
  11. 11
  12. 12 int gcd(int a, int b) //求最大公约数
  13. 13 {
  14. 14 int c = 0;
  15. 15 if(a<b) swap(a,b);
  16. 16 c = b;
  17. 17 do
  18. 18 {
  19. 19 b = c;
  20. 20 c = a%b;
  21. 21 a = b;
  22. 22 }
  23. 23 while (c != 0);
  24. 24 return b;
  25. 25 }
  26. 26
  27. 27 int PrimarityTest(int a, int i) //判断i是否是素数
  28. 28 {
  29. 29 int flag=0;
  30. 30 for(a;a<i;a++)
  31. 31 {
  32. 32 if(i%a==0)
  33. 33 {
  34. 34 flag=1;
  35. 35 break;
  36. 36 }
  37. 37 }
  38. 38 if(flag) return 0;
  39. 39 return 1;
  40. 40 // complete this part
  41. 41 }
  42. 42
  43. 43 int ModularExponention(int a, int b, int n) //求a^bmodn
  44. 44 {
  45. 45 int y;
  46. 46
  47. 47 /*使用二进制平方乘法计算 pow(a,b) % n*/
  48. 48 y=1;
  49. 49
  50. 50 while(b != 0)
  51. 51 {
  52. 52 /*对于b中的每个1,累加y*/
  53. 53
  54. 54 if(b & 1)
  55. 55 y = (y*a) % n;
  56. 56
  57. 57 /*对于b中的每一位,计算a的平方*/
  58. 58 a = (a*a) % n;
  59. 59
  60. 60 /*准备b中的下一位*/
  61. 61 b = b>>1;
  62. 62 }
  63. 63
  64. 64 return y;
  65. 65 // complete this part
  66. 66 }
  67. 67
  68. 68 void extgcd(ll a,ll b,ll& d,ll& x,ll& y) //获取(1/a)modb得结果
  69. 69 {
  70. 70 if(!b)
  71. 71 {
  72. 72 d=a;
  73. 73 x=1;
  74. 74 y=0;
  75. 75 }
  76. 76 else
  77. 77 {
  78. 78 extgcd(b,a%b,d,y,x);
  79. 79 y-=x*(a/b);
  80. 80 }
  81. 81 }
  82. 82
  83. 83 int ModularInverse(int a,int b) //获取(1/a)modb得结果
  84. 84 {
  85. 85 ll d,x,y;
  86. 86 extgcd(a,b,d,x,y);
  87. 87 return d==1?(x+b)%b:-1;
  88. 88 // complete this part
  89. 89 }
  90. 90
  91. 91 void KeyGeneration() //获取公钥密钥
  92. 92 {
  93. 93 int p, q;
  94. 94 int phi_n;
  95. 95
  96. 96 do
  97. 97 {
  98. 98 do
  99. 99 p = rand();
  100. 100 while (p % 2 == 0);
  101. 101
  102. 102 }
  103. 103 while (!PrimarityTest(2, p));
  104. 104
  105. 105 do
  106. 106 {
  107. 107 do
  108. 108 q = rand();
  109. 109 while (q % 2 == 0);
  110. 110 }
  111. 111 while (!PrimarityTest(2, q));
  112. 112
  113. 113 n = p * q;
  114. 114 phi_n = (p - 1) * (q - 1);
  115. 115
  116. 116 do
  117. 117 e = rand() % (phi_n - 2) + 2; // 1 < e < phi_n
  118. 118 while (gcd(e, phi_n) != 1);
  119. 119
  120. 120 d = ModularInverse(e,phi_n);
  121. 121 }
  122. 122
  123. 123 void Encryption(int value, FILE* out) //加密
  124. 124 {
  125. 125 int cipher;
  126. 126 cipher = ModularExponention(value, e, n);
  127. 127 fprintf(out, "%d ", cipher);
  128. 128 }
  129. 129
  130. 130 void Decryption(int value, FILE* out) //解密
  131. 131 {
  132. 132 int decipher;
  133. 133 decipher = ModularExponention(value, d, n);
  134. 134 fprintf(out, "%c", decipher);
  135. 135 }
  136. 136 int main(void)
  137. 137 {
  138. 138 FILE* inp, * out;
  139. 139 char filepath[15], filename[100];
  140. 140
  141. 141 strcpy(filepath, "F:\Desktop\\"); //文件路径
  142. 142
  143. 143 sprintf(filename, "%s%s", filepath, "cipher.txt");
  144. 144 out = fopen(filename, "w+"); //打开文件
  145. 145 fclose(out);
  146. 146 sprintf(filename, "%s%s", filepath, "decipher.txt");
  147. 147 out = fopen(filename, "w+"); //打开文件
  148. 148 fclose(out);
  149. 149
  150. 150 KeyGeneration(); //获取公钥密钥
  151. 151
  152. 152 sprintf(filename, "%s%s", filepath, "plain.txt");
  153. 153 inp = fopen(filename, "r+"); //读取原文件
  154. 154 if (inp == NULL)
  155. 155 {
  156. 156 printf("Error opening Source File.\n");
  157. 157 exit(1);
  158. 158 }
  159. 159
  160. 160 sprintf(filename, "%s%s", filepath, "cipher.txt");
  161. 161 out = fopen(filename, "w+");
  162. 162 if (out == NULL)
  163. 163 {
  164. 164 printf("Error opening Destination File.\n");
  165. 165 exit(1);
  166. 166 }
  167. 167
  168. 168 // encryption starts
  169. 169 while (1)
  170. 170 {
  171. 171 char ch = getc(inp); //读取文件字符,一个字符一个字符的读取输出
  172. 172 if (ch == -1)
  173. 173 break;
  174. 174 int value = toascii(ch); //toascii将字符转化成对应ascall值
  175. 175 Encryption(value, out); //加密输出
  176. 176 }
  177. 177
  178. 178 fclose(inp);
  179. 179 fclose(out);
  180. 180
  181. 181 // decryption starts
  182. 182 sprintf(filename, "%s%s", filepath, "cipher.txt");
  183. 183 inp = fopen(filename, "r");
  184. 184 if (inp == NULL)
  185. 185 {
  186. 186 printf("Error opening Cipher Text.\n");
  187. 187 exit(1);
  188. 188 }
  189. 189
  190. 190 sprintf(filename, "%s%s", filepath, "decipher.txt");
  191. 191 out = fopen(filename, "w+");
  192. 192 if (out == NULL)
  193. 193 {
  194. 194 printf("Error opening File.\n");
  195. 195 exit(1);
  196. 196 }
  197. 197
  198. 198 while (1)
  199. 199 {
  200. 200 int cip;
  201. 201 if (fscanf(inp, "%d", &cip) == -1)
  202. 202 break;
  203. 203 Decryption(cip, out); //解密
  204. 204 }
  205. 205 fclose(out);
  206. 206
  207. 207 return 0;
  208. 208 }

以下内容参考自:https://blog.csdn.net/qmickecs/article/details/39676655

RSA大数所用库:

GMP简介
GMP(the GNU Multiple Precision arithmetic library)是著名的任意精度算术运算库,支持任意精度的整数、有理数以及浮点数的四则运算、求模、求幂、开方等基本运算,还支持部分数论相关运算。Maple、Mathematica等大型数学软件的高精度算术运算功能都是利用GMP实现的。
 
RSA代码:

GMP是一个基于C语言的开源库,其中包含了数种自定义数据类型,包括

  • mpz_t 多精度整型
  • mpq_t 多精度有理数
  • mpf_t 多精度浮点型

GMP要求一个mpz_t类型变量在被使用前必须手动进行初始化,并且不允许对已经初始化的变量进行初始化。

下面是一些本文中使用到的部分函数,其他函数介绍以及用法请参考GMP官方文档

  1. 1 mpz_t x
  2. 2 声明一个多精度整型变量x
  3. 3
  4. 4 void mpz_init (mpz_t x)
  5. 5 初始化x。任何一个mpz_t类型的变量在使用前都应该初始化。
  6. 6
  7. 7 void mpz_init_set_ui (mpz_t rop, unsigned long int op)
  8. 8 初始化rop,并将其值设置为op
  9. 9
  10. 10 int mpz_init_set_str (mpz_t rop, const char *str, int base)
  11. 11 初始化rop,并赋值rop = str,其中str是一个表示base进制整数的字符数组
  12. 12
  13. 13 void mpz_clear (mpz_t x)
  14. 14 释放x所占用的内存空间
  15. 15
  16. 16 void mpz_sub_ui (mpz_t rop, const mpz_t op1, unsigned long int op2)
  17. 17 计算op1 op2,结果保存在rop
  18. 18
  19. 19 void mpz_mul (mpz_t rop, const mpz_t op1, const mpz_t op2)
  20. 20 计算op1 * op2,结果保存在rop
  21. 21
  22. 22 void gmp_randinit_default (gmp_randstate_t state)
  23. 23 设置state的随机数生成算法,默认为梅森旋转算法
  24. 24
  25. 25 void gmp_randseed_ui (gmp_randstate_t state, unsigned long int seed)
  26. 26 设置state的随机化种子为seed
  27. 27
  28. 28 void mpz_urandomb (mpz_t rop, gmp_randstate_t state, mp_bitcnt_t n)
  29. 29 根据state生成一个在范围0~2^n-1内均匀分布的整数,结果保存在rop
  30. 30
  31. 31 char * mpz_get_str (char *str, int base, const mpz_t op)
  32. 32 opbase进制的形式保存到字符数组中,该函数要求指针strNULLGMP会自动为其分配合适的空间),或者所指向的数组拥有足够存放op的空间
  33. 33
  34. 34 int gmp_printf (const char *fmt, ...)
  35. 35 语法跟C语言中的标准输出函数printf类似。它在printf的基础上,增加了mpz_t等数据类型的格式化输出功能。fmt为输出格式,例如fmt=”%Zd”时,表示输出一个10进制的多精度整型。其后的所有参数为输出的内容。
  36. 36
  37. 37 int mpz_probab_prime_p (const mpz_t n, int reps)
  38. 38 检测n是否为素数。该函数首先对n进行试除,然后使用米勒-拉宾素性检测对n进行测试,reps表示进行检测的次数。如果n为素数,返回2;如果n可能为素数,返回1;如果n为合数,返回0

代码:

  1. 1 #include <cstdio>
  2. 2
  3. 3 #include <ctime>
  4. 4
  5. 5 #include <cstring>
  6. 6
  7. 7 #include <cstdlib>
  8. 8
  9. 9 #include <iostream>
  10. 10
  11. 11 #include <gmp.h>
  12. 12
  13. 13
  14. 14
  15. 15 #define KEY_LENGTH 2048 //公钥的长度
  16. 16
  17. 17 #define BASE 16 //输入输出的数字进制
  18. 18
  19. 19
  20. 20
  21. 21 using namespace std;
  22. 22
  23. 23
  24. 24
  25. 25 struct key_pair
  26. 26
  27. 27 {
  28. 28
  29. 29 char * n;
  30. 30
  31. 31 char * d;
  32. 32
  33. 33 int e;
  34. 34
  35. 35 };
  36. 36
  37. 37
  38. 38
  39. 39 //生成两个大素数
  40. 40
  41. 41 mpz_t * gen_primes()
  42. 42
  43. 43 {
  44. 44
  45. 45 gmp_randstate_t grt;
  46. 46
  47. 47 gmp_randinit_default(grt);
  48. 48
  49. 49 gmp_randseed_ui(grt, time(NULL));
  50. 50
  51. 51
  52. 52
  53. 53 mpz_t key_p, key_q;
  54. 54
  55. 55 mpz_init(key_p);
  56. 56
  57. 57 mpz_init(key_q);
  58. 58
  59. 59
  60. 60
  61. 61 mpz_urandomb(key_p, grt, KEY_LENGTH / 2);
  62. 62
  63. 63 mpz_urandomb(key_q, grt, KEY_LENGTH / 2); //随机生成两个大整数
  64. 64
  65. 65
  66. 66
  67. 67 mpz_t * result = new mpz_t[2];
  68. 68
  69. 69 mpz_init(result[0]);
  70. 70
  71. 71 mpz_init(result[1]);
  72. 72
  73. 73
  74. 74
  75. 75 mpz_nextprime(result[0], key_p); //使用GMP自带的素数生成函数
  76. 76
  77. 77 mpz_nextprime(result[1], key_q);
  78. 78
  79. 79
  80. 80
  81. 81 mpz_clear(key_p);
  82. 82
  83. 83 mpz_clear(key_q);
  84. 84
  85. 85
  86. 86
  87. 87 return result;
  88. 88
  89. 89 }
  90. 90
  91. 91
  92. 92
  93. 93 //生成密钥对
  94. 94
  95. 95 key_pair * gen_key_pair()
  96. 96
  97. 97 {
  98. 98
  99. 99 mpz_t * primes = gen_primes();
  100. 100
  101. 101
  102. 102
  103. 103 mpz_t key_n, key_e, key_f;
  104. 104
  105. 105 mpz_init(key_n);
  106. 106
  107. 107 mpz_init(key_f);
  108. 108
  109. 109 mpz_init_set_ui(key_e, 65537); //设置e为65537
  110. 110
  111. 111
  112. 112
  113. 113 mpz_mul(key_n, primes[0], primes[1]); //计算n=p*q
  114. 114
  115. 115 mpz_sub_ui(primes[0], primes[0], 1); //p=p-1
  116. 116
  117. 117 mpz_sub_ui(primes[1], primes[1], 1); //q=q-1
  118. 118
  119. 119 mpz_mul(key_f, primes[0], primes[1]); //计算欧拉函数φ(n)=(p-1)*(q-1)
  120. 120
  121. 121
  122. 122
  123. 123 mpz_t key_d;
  124. 124
  125. 125 mpz_init(key_d);
  126. 126
  127. 127 mpz_invert(key_d, key_e, key_f); //计算数论倒数
  128. 128
  129. 129
  130. 130
  131. 131 key_pair * result = new key_pair;
  132. 132
  133. 133
  134. 134
  135. 135 char * buf_n = new char[KEY_LENGTH + 10];
  136. 136
  137. 137 char * buf_d = new char[KEY_LENGTH + 10];
  138. 138
  139. 139
  140. 140
  141. 141 mpz_get_str(buf_n, BASE, key_n);
  142. 142
  143. 143 result->n = buf_n;
  144. 144
  145. 145 mpz_get_str(buf_d, BASE, key_d);
  146. 146
  147. 147 result->d = buf_d;
  148. 148
  149. 149 result->e = 65537;
  150. 150
  151. 151
  152. 152
  153. 153 mpz_clear(primes[0]); //释放内存
  154. 154
  155. 155 mpz_clear(primes[1]);
  156. 156
  157. 157 mpz_clear(key_n);
  158. 158
  159. 159 mpz_clear(key_d);
  160. 160
  161. 161 mpz_clear(key_e);
  162. 162
  163. 163 mpz_clear(key_f);
  164. 164
  165. 165 delete []primes;
  166. 166
  167. 167
  168. 168
  169. 169 return result;
  170. 170
  171. 171 }
  172. 172
  173. 173
  174. 174
  175. 175 //加密函数
  176. 176
  177. 177 char * encrypt(const char * plain_text, const char * key_n, int key_e)
  178. 178
  179. 179 {
  180. 180
  181. 181 mpz_t M, C, n;
  182. 182
  183. 183 mpz_init_set_str(M, plain_text, BASE);
  184. 184
  185. 185 mpz_init_set_str(n, key_n, BASE);
  186. 186
  187. 187 mpz_init_set_ui(C, 0);
  188. 188
  189. 189
  190. 190
  191. 191 mpz_powm_ui(C, M, key_e, n); //使用GMP中模幂计算函数
  192. 192
  193. 193
  194. 194
  195. 195 char * result = new char[KEY_LENGTH + 10];
  196. 196
  197. 197 mpz_get_str(result, BASE, C);
  198. 198
  199. 199
  200. 200
  201. 201 return result;
  202. 202
  203. 203 }
  204. 204
  205. 205
  206. 206
  207. 207 //解密函数
  208. 208
  209. 209 char * decrypt(const char * cipher_text, const char * key_n, const char * key_d)
  210. 210
  211. 211 {
  212. 212
  213. 213 mpz_t M, C, n, d;
  214. 214
  215. 215 mpz_init_set_str(C, cipher_text, BASE);
  216. 216
  217. 217 mpz_init_set_str(n, key_n, BASE);
  218. 218
  219. 219 mpz_init_set_str(d, key_d, BASE);
  220. 220
  221. 221 mpz_init(M);
  222. 222
  223. 223
  224. 224
  225. 225 mpz_powm(M, C, d, n); //使用GMP中的模幂计算函数
  226. 226
  227. 227
  228. 228
  229. 229 char * result = new char[KEY_LENGTH + 10];
  230. 230
  231. 231 mpz_get_str(result, BASE, M);
  232. 232
  233. 233
  234. 234
  235. 235 return result;
  236. 236
  237. 237 }
  238. 238
  239. 239
  240. 240
  241. 241 int main()
  242. 242
  243. 243 {
  244. 244
  245. 245 key_pair * p = gen_key_pair();
  246. 246
  247. 247
  248. 248
  249. 249 cout<<"n = "<<p->n<<endl;
  250. 250
  251. 251 cout<<"d = "<<p->d<<endl;
  252. 252
  253. 253 cout<<"e = "<<p->e<<endl;
  254. 254
  255. 255
  256. 256
  257. 257 char buf[KEY_LENGTH + 10];
  258. 258
  259. 259 cout<<"请输入要加密的数字,二进制长度不超过"<<KEY_LENGTH<<endl;
  260. 260
  261. 261 cin>>buf;
  262. 262
  263. 263
  264. 264
  265. 265 char * cipher_text = encrypt(buf, p->n, p->e);
  266. 266
  267. 267 cout<<"密文为:"<<cipher_text<<endl;
  268. 268
  269. 269 char * plain_text = decrypt(cipher_text, p->n, p->d);
  270. 270
  271. 271 cout<<"明文为:"<<plain_text<<endl;
  272. 272
  273. 273
  274. 274
  275. 275 if(strcmp(buf, plain_text) != 0)
  276. 276
  277. 277 cout<<"无法解密"<<endl;
  278. 278
  279. 279 else
  280. 280
  281. 281 cout<<"解密成功"<<endl;
  282. 282
  283. 283
  284. 284
  285. 285 return 0;
  286. 286
  287. 287 }

加密算法——RSA算法(c++简单实现)的更多相关文章

  1. 非对称加密算法-RSA算法

    一.概述 1.RSA是基于大数因子分解难题.目前各种主流计算机语言都支持RSA算法的实现 2.java6支持RSA算法 3.RSA算法可以用于数据加密和数字签名 4.RSA算法相对于DES/AES等对 ...

  2. 加密算法 - RSA算法二

    RSA算法原理(二)  声明: 本文转自阮一峰 (http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html) 有了这些知识, ...

  3. 安全体系(二)——RSA算法详解

    本文主要讲述RSA算法使用的基本数学知识.秘钥的计算过程以及加密和解密的过程. 安全体系(零)—— 加解密算法.消息摘要.消息认证技术.数字签名与公钥证书 安全体系(一)—— DES算法详解 1.概述 ...

  4. RSA算法原理与加密解密 求私钥等价求求模反元素 等价于分解出2个质数 (r*X+1)%[(p-1)(q-1)]=0

    Rsapaper.pdf http://people.csail.mit.edu/rivest/Rsapaper.pdf [概述Abstract 1.将字符串按照双方约定的规则转化为小于n的正整数m, ...

  5. 非对称加密算法RSA 学习

    非对称加密算法RSA 学习 RSA加密算法是一种非对称加密算法.RSA是1977年由罗纳德·李维斯特(Ron Rivest).阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Ad ...

  6. RSA算法基础详解

    . 首页 博客园 联系我 前言:在RSA诞生之前. RSA算法. 质数与互质数. 模运算. 同余. 欧拉函数. 欧拉定理与模反元素. 真实的例子. 计算密钥. 密钥组成与加解密公式. 安全性. 一点感 ...

  7. 非对称加密技术- RSA算法数学原理分析

    非对称加密技术,在现在网络中,有非常广泛应用.加密技术更是数字货币的基础. 所谓非对称,就是指该算法需要一对密钥,使用其中一个(公钥)加密,则需要用另一个(私钥)才能解密. 但是对于其原理大部分同学应 ...

  8. [转]应用RSACryptoServiceProvider类轻松实现RSA算法

    在我们现实当中经常会存在需要对某些数据进行加密保护 然后进行解密的操作,比方,我们需要对某些XML配置信息里面的某些数据进行加密,以防止任何人打开该XML配置信息都能正常的看到该配置信息里面的内容,从 ...

  9. 非对称加密RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)等。使用最广泛的是RSA算法

          非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey).公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密:如果用私 ...

随机推荐

  1. VBA实现相同行合并

    帮人捣鼓了个VBA代码用来实现多行合并,具体需求为:列2/列3/列4 相同的情况下,则对应的行合并为一行,且列1用空格隔开,列5则相加: (对大多数办公室职员,VBA还算是提高效率的一个利器吧) 最终 ...

  2. 【Linux】ssh远程连接到指定ip的指定用户上

    通过ssh可以远程连接到其他的机器上,但是如果只想连接到指定的用户的话 需要这样做: -l 选项 (是L不是I,小写) ssh IP -l 用户名 这里的ip如果在hosts下就可以直接输入域名或者主 ...

  3. 单片机—Arduino UNO-R3—学习笔记001

    连接方法 下载Arduino软件 安装完成打开如图所示 观察右下角的连接接口"Arduino Uno在COM(X)" 在工具-->端口-->选择之前查看的端口 即为连接 ...

  4. ctfhub技能树—web前置技能—http协议—Cookie

    打开靶机环境 查看显示内容 根据提示,需要admin登录才能得到flag 题目介绍为Cookie欺骗.认证.伪造 介绍一下cookie和session 一.cookie: 在网站中,http请求是无状 ...

  5. Eclipse中给jar包导入JavaDoc的方法

    原文转载自:http://blog.csdn.net/mr_von/article/details/7740138 在使用Java语言开发的过程中,开发人员经常需要用到一些开源的工具包.在使用别人的j ...

  6. IOC技术在前端项目中的应用

    目录 背景 什么是IOC 如何实现一个IOC 第一步:实现一个容器 第二步:用好装饰器 第三步:使用容器 扩展和展望 最后 背景 前端发展至今已经过去30余年,前端应用领域在不断壮大的过程中,也变得越 ...

  7. linux设备文件

    一.前言 在调用了alloc_chrdev_region函数或register_chrdev_region函数之后可以在/proc/devices中看到该设备的主设备号,比如我注册的hello模块的主 ...

  8. 善用tempfile库创建python进程中的临时文件

    技术背景 临时文件在python项目中时常会被使用到,其作用在于随机化的创建不重名的文件,路径一般都是放在Linux系统下的/tmp目录.如果项目中并不需要持久化的存储一个文件,就可以采用临时文件的形 ...

  9. Bitter.Core系列七:Bitter ORM NETCORE ORM 全网最粗暴简单易用高性能的 NETCore ORM 示例 更新删除插入

    Bitter Orm 在操作数据库增删改的时候,支持模型驱动和直接执行裸SQL 操作,示例代码如下: 一:模型驱动(增删改) /// <summary> /// 插入,删除,更新示例(模型 ...

  10. proxy_http_version 1.0 | 1.1

    Module ngx_http_proxy_module http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_ver ...