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 #include <iostream> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <ctype.h> 5 #include<string.h> 6 #include <math.h> 7 #include<algorithm> 8 using namespace std; 9 typedef long long ll; 10 int e, d, n; 11 12 int gcd(int a, int b) //求最大公約數 13 { 14 int c = 0; 15 if(a<b) swap(a,b); 16 c = b; 17 do 18 { 19 b = c; 20 c = a%b; 21 a = b; 22 } 23 while (c != 0); 24 return b; 25 } 26 27 int PrimarityTest(int a, int i) //判斷i是否是素數 28 { 29 int flag=0; 30 for(a;a<i;a++) 31 { 32 if(i%a==0) 33 { 34 flag=1; 35 break; 36 } 37 } 38 if(flag) return 0; 39 return 1; 40 // complete this part 41 } 42 43 int ModularExponention(int a, int b, int n) //求a^bmodn 44 { 45 int y; 46 47 /*使用二進制平方乘法計算 pow(a,b) % n*/ 48 y=1; 49 50 while(b != 0) 51 { 52 /*對於b中的每個1,累加y*/ 53 54 if(b & 1) 55 y = (y*a) % n; 56 57 /*對於b中的每一位,計算a的平方*/ 58 a = (a*a) % n; 59 60 /*准備b中的下一位*/ 61 b = b>>1; 62 } 63 64 return y; 65 // complete this part 66 } 67 68 void extgcd(ll a,ll b,ll& d,ll& x,ll& y) //獲取(1/a)modb得結果 69 { 70 if(!b) 71 { 72 d=a; 73 x=1; 74 y=0; 75 } 76 else 77 { 78 extgcd(b,a%b,d,y,x); 79 y-=x*(a/b); 80 } 81 } 82 83 int ModularInverse(int a,int b) //獲取(1/a)modb得結果 84 { 85 ll d,x,y; 86 extgcd(a,b,d,x,y); 87 return d==1?(x+b)%b:-1; 88 // complete this part 89 } 90 91 void KeyGeneration() //獲取公鑰密鑰 92 { 93 int p, q; 94 int phi_n; 95 96 do 97 { 98 do 99 p = rand(); 100 while (p % 2 == 0); 101 102 } 103 while (!PrimarityTest(2, p)); 104 105 do 106 { 107 do 108 q = rand(); 109 while (q % 2 == 0); 110 } 111 while (!PrimarityTest(2, q)); 112 113 n = p * q; 114 phi_n = (p - 1) * (q - 1); 115 116 do 117 e = rand() % (phi_n - 2) + 2; // 1 < e < phi_n 118 while (gcd(e, phi_n) != 1); 119 120 d = ModularInverse(e,phi_n); 121 } 122 123 void Encryption(int value, FILE* out) //加密 124 { 125 int cipher; 126 cipher = ModularExponention(value, e, n); 127 fprintf(out, "%d ", cipher); 128 } 129 130 void Decryption(int value, FILE* out) //解密 131 { 132 int decipher; 133 decipher = ModularExponention(value, d, n); 134 fprintf(out, "%c", decipher); 135 } 136 int main(void) 137 { 138 FILE* inp, * out; 139 char filepath[15], filename[100]; 140 141 strcpy(filepath, "F:\Desktop\\"); //文件路徑 142 143 sprintf(filename, "%s%s", filepath, "cipher.txt"); 144 out = fopen(filename, "w+"); //打開文件 145 fclose(out); 146 sprintf(filename, "%s%s", filepath, "decipher.txt"); 147 out = fopen(filename, "w+"); //打開文件 148 fclose(out); 149 150 KeyGeneration(); //獲取公鑰密鑰 151 152 sprintf(filename, "%s%s", filepath, "plain.txt"); 153 inp = fopen(filename, "r+"); //讀取原文件 154 if (inp == NULL) 155 { 156 printf("Error opening Source File.\n"); 157 exit(1); 158 } 159 160 sprintf(filename, "%s%s", filepath, "cipher.txt"); 161 out = fopen(filename, "w+"); 162 if (out == NULL) 163 { 164 printf("Error opening Destination File.\n"); 165 exit(1); 166 } 167 168 // encryption starts 169 while (1) 170 { 171 char ch = getc(inp); //讀取文件字符,一個字符一個字符的讀取輸出 172 if (ch == -1) 173 break; 174 int value = toascii(ch); //toascii將字符轉化成對應ascall值 175 Encryption(value, out); //加密輸出 176 } 177 178 fclose(inp); 179 fclose(out); 180 181 // decryption starts 182 sprintf(filename, "%s%s", filepath, "cipher.txt"); 183 inp = fopen(filename, "r"); 184 if (inp == NULL) 185 { 186 printf("Error opening Cipher Text.\n"); 187 exit(1); 188 } 189 190 sprintf(filename, "%s%s", filepath, "decipher.txt"); 191 out = fopen(filename, "w+"); 192 if (out == NULL) 193 { 194 printf("Error opening File.\n"); 195 exit(1); 196 } 197 198 while (1) 199 { 200 int cip; 201 if (fscanf(inp, "%d", &cip) == -1) 202 break; 203 Decryption(cip, out); //解密 204 } 205 fclose(out); 206 207 return 0; 208 }
以下內容參考自:https://blog.csdn.net/qmickecs/article/details/39676655
RSA大數所用庫:
GMP是一個基於C語言的開源庫,其中包含了數種自定義數據類型,包括
- mpz_t 多精度整型
- mpq_t 多精度有理數
- mpf_t 多精度浮點型
GMP要求一個mpz_t類型變量在被使用前必須手動進行初始化,並且不允許對已經初始化的變量進行初始化。
下面是一些本文中使用到的部分函數,其他函數介紹以及用法請參考GMP官方文檔:
1 mpz_t x 2 聲明一個多精度整型變量x 3 4 void mpz_init (mpz_t x) 5 初始化x。任何一個mpz_t類型的變量在使用前都應該初始化。 6 7 void mpz_init_set_ui (mpz_t rop, unsigned long int op) 8 初始化rop,並將其值設置為op 9 10 int mpz_init_set_str (mpz_t rop, const char *str, int base) 11 初始化rop,並賦值rop = str,其中str是一個表示base進制整數的字符數組 12 13 void mpz_clear (mpz_t x) 14 釋放x所占用的內存空間 15 16 void mpz_sub_ui (mpz_t rop, const mpz_t op1, unsigned long int op2) 17 計算op1 – op2,結果保存在rop中 18 19 void mpz_mul (mpz_t rop, const mpz_t op1, const mpz_t op2) 20 計算op1 * op2,結果保存在rop中 21 22 void gmp_randinit_default (gmp_randstate_t state) 23 設置state的隨機數生成算法,默認為梅森旋轉算法 24 25 void gmp_randseed_ui (gmp_randstate_t state, unsigned long int seed) 26 設置state的隨機化種子為seed 27 28 void mpz_urandomb (mpz_t rop, gmp_randstate_t state, mp_bitcnt_t n) 29 根據state生成一個在范圍0~2^n-1內均勻分布的整數,結果保存在rop中 30 31 char * mpz_get_str (char *str, int base, const mpz_t op) 32 將op以base進制的形式保存到字符數組中,該函數要求指針str為NULL(GMP會自動為其分配合適的空間),或者所指向的數組擁有足夠存放op的空間 33 34 int gmp_printf (const char *fmt, ...) 35 語法跟C語言中的標准輸出函數printf類似。它在printf的基礎上,增加了mpz_t等數據類型的格式化輸出功能。fmt為輸出格式,例如fmt=”%Zd”時,表示輸出一個10進制的多精度整型。其后的所有參數為輸出的內容。 36 37 int mpz_probab_prime_p (const mpz_t n, int reps) 38 檢測n是否為素數。該函數首先對n進行試除,然后使用米勒-拉賓素性檢測對n進行測試,reps表示進行檢測的次數。如果n為素數,返回2;如果n可能為素數,返回1;如果n為合數,返回0。
代碼:

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