在上世紀七八十年代,公鑰密碼學提出來以後,基於數學難題(陷門函數)的公鑰密碼體制便獲得了發展,最基礎的密碼體制就是基於「大整數分解難題」的RSA密碼體制。利用的單向陷門函數是這樣定義的:已知兩個大素數 p 和 q,其中(p - 1, q - 1)的結果較大,則:經過 p * q 獲得大整數 n 是很容易的,可是反過來倒是陷門,由於經過大整數 n 獲得素數 p 和 q 是很困難的!這就是RSA密碼體制基於的數學難題。算法
公鑰密碼體制要求有一組密鑰對,一個公鑰一個私鑰。它們在安全通訊過程當中扮演不一樣角色,起着不一樣的做用。直觀來看,通訊具備一下過程,假設通訊雙方是 Alice 和 Bob,設定爲 Alice 向 Bob 發消息Msg :
已知:
Alice 的公鑰是 Ya, 私鑰是 Xa;Bob 的公鑰是 Yb, 私鑰是 Xb
分析以下:安全
①如若Alice要發給Bob的消息不想被***者Attack獲取,須要對信息加密,而加密的意義就在於不能被Attack解密而獲得明文,因此咱們得用公鑰進行加密,由於咱們必需要有私鑰(肯定是接收方)才能解密。這裏咱們的接收方是Bob,因而就用Bob的公鑰Yb進行加密,這樣Bob才能用本身的私鑰解密收到Msg。因爲公鑰是公開的,也只能拿來加密,因此就算敵手Attack獲取到密文和公鑰(可以在通訊過程當中獲取到的只有這倆信息),也沒法獲得明文。markdown
②在上述內容中,咱們只能保障Bob收到的消息是安全的,不能被敵手獲知的。可是細想也有缺陷,由於加密是公鑰完成的,這個公鑰是公開的,任何人均可以用這個Bob的公鑰加密信息給Bob發送,如如有人假扮Alice給Bob發消息,那麼這個數據仍然是不可信的!因而須要有一個來自Alice的認證,保障這個消息是Alice發的,這就有了數字簽名。那麼如何才能保障消息來自Alice呢?就得用可以體現Alice身份的,其餘人都沒有的東西進行簽名,也就是Alice的私鑰。因此Alice的消息內容,是包含加密的消息以及簽名一塊兒發送給Bob的,Bob解密後拿明文和簽證後的明文進行對比,若是一致,則肯定是來自Alice的安全可靠的消息。網絡
③上述第二種方案其實也有很大的漏洞,就是信道中有來自Alice的私鑰簽名過的消息摘要,如若敵手獲知了這個摘要,再用Alice的公鑰(公開的)進行簽證,就獲知了明文。因此這兩種都不OK,因而在實際運用中,咱們每每要把加密和簽名放在一塊兒使用。比方說,Alice要給Bob發送消息Msg,先對Msg用Bob的公鑰Yb進行加密,而後備份一份加密的密文,再對密文進行簽名。把簽名和密文一塊兒發送。等Bob收到消息後,先對簽名進行簽證,而後對比簽證後的密文和另一份密文是否一致,若是一致則得知是Alice發來的,而後再用本身的私鑰解密,獲得完整、安全可信的明文Msg。ide
首先要設定公鑰和私鑰,基於大整數分解難題,則大整數爲公鑰,分解的兩個大素數 p 和 q 是私鑰,可是還得有其餘幾個部分組成,咱們以代碼形式體現:函數
typedef struct PrimaryKey { big p; big q; big phi; big d; } PrimaryKey; typedef struct PublicKey { big n; big e; } PublicKey; typedef struct TupleKey { struct PrimaryKey* sk; struct PublicKey* pk; } TupleKey;
截取《密碼與編碼理論》一書中的RSA算法描述:
在使用Miracl實現的時候,如何高效地產生咱們所須要的公私密鑰呢?使用到的第三方函數以下:測試
由於1024 / 8 = 128 byte,因而在16進制系統下,須要的位數就是:128 * 2 = 256 (位)編碼
bigbits(256, GenPrime);// 生成一個大整數GenPrime nxprime(GenPrime, ret->sk->p);// 生成比GenPrime要大的下一個素數
這裏咱們之因此不使用:atom
while (1) { bigbits(256, ret->sk->p); if (isprime(ret->sk->p)) { statements; } }
是由於隨機生成的大整數不必定是素數,可能要隨機生成好幾回,效率低下!!因此咱們先隨機生成一個大整數做爲參照,而後再獲得下一個比它大的素數,這樣生成隨機素數就只須要算一遍,並且計算下一個比它大的素數準確性很高,開銷小,基本能夠一次算對!加密
// e = 65537 convert(65537, ret->pk->e); // d = e^-1 mod phi xgcd(ret->pk->e, ret->sk->phi, ret->sk->d, ret->sk->d, ret->sk->d);
求逆元咱們採用擴展歐幾里得算法,求安全指數,咱們爲了方便起見,每每設置安全指數爲65537。
再來看看這個xgcd()函數是怎麼定義的:
由此咱們能夠看出來:
z = x * xd + y * yd
運用到咱們的參數調用之中就是:
d = e * d + phi * d,這裏phi就是歐拉函數
因而能夠知道,e * d mod phi = 1,而它的返回值剛好就是d,也就是說結果返回的是d = e^-1 mod phi
struct TupleKey* GenKey() { big GenPrime = mirvar(0), temp1 = mirvar(0), temp2 = mirvar(0); struct TupleKey* ret = (struct TupleKey*)malloc(sizeof(struct TupleKey)); ret->sk = (struct PrimaryKey*)malloc(sizeof(struct PrimaryKey)); ret->pk = (struct PublicKey*)malloc(sizeof(struct PublicKey)); ret->sk->p = mirvar(0), ret->sk->q = mirvar(0); ret->sk->phi = mirvar(0), ret->sk->d = mirvar(0); ret->pk->e = mirvar(0), ret->pk->n = mirvar(0); while (1) { bigbits(256, GenPrime); if (nxprime(GenPrime, ret->sk->p)) { premult(ret->sk->p, 2, ret->sk->q); if (nxprime(ret->sk->q, ret->sk->q)) { // n = p * q multiply(ret->sk->p, ret->sk->q, ret->pk->n); decr(ret->sk->p, 1, temp1); decr(ret->sk->q, 1, temp2); // phi = (p - 1) * (q - 1) multiply(temp1, temp2, ret->sk->phi); // e = 65537 convert(65537, ret->pk->e); // d = e^-1 mod phi xgcd(ret->pk->e, ret->sk->phi, ret->sk->d, ret->sk->d, ret->sk->d); break; } } } return ret; }
引入《密碼編碼學與網絡安全 原理與實踐》一書的關於RSA的加解密過程的講解:
實際上這個RSA解密算法的正確性是簡單並且顯然的,簡單證實一下:
big Encrypt(unsigned char* msg, struct PublicKey* pk, big n) { big c = mirvar(0), m = mirvar(0); unsigned char* temp = (unsigned char*)malloc(sizeof(unsigned char*) * strlen(msg)); strcpy(temp, msg); bytes_to_big((int)strlen(temp), temp, m);// 把字符串轉成大數 // c = m^e mod n powmod(m, pk->e, n, c); return c; } void Decrypt(big Encode, struct PrimaryKey* sk, big n, unsigned char* Msg) { // m = c^d mod n big m = mirvar(0); unsigned char OutStr[128] = { '\0' }; powmod(Encode, sk->d, n, m); big_to_bytes(256, m, OutStr, 0); strcpy(Msg, OutStr); }
在以前的分解任務1.2中咱們談到了公鑰加密和數字簽名的含義,也就是說,咱們只須要拿着發送方本身的私鑰進行簽名,而後對方收到簽名後用發送方公開的公鑰去簽證便可!
咱們選取《密碼與編碼理論》一書上關於RSA數字簽名的方案:
咱們發現,這個數字簽名的過程和加密解密的過程極其類似,因此咱們獲得的方案以下:
①被簽名的數據是發送方加密後的密文
②用發送方(Alice)的私鑰dA進行簽名,並把(Encode, Signature)發給Bob
③接收方(Bob)用Alice的公鑰eA進行簽證,獲得CMP = Sig ^ eA mod n,而後對比CMP和收到的Encode是否一致,推斷該消息是否是來自Alice,以及推斷數據的完整和可靠。
big Signature(big Encode, struct PrimaryKey* sk, big n) { // sig = c^d mod n big d = sk->d, c = Encode; big sig = mirvar(0); powmod(c, d, n, sig); return sig; } int Verify(big sig, struct PublicKey* pk, big Encode) { // y = sig^da mod n, if y == Encode then be verified big e = pk->e, n = pk->n; big ver = mirvar(0); powmod(sig, e, n, ver); if (!mr_compare(ver, Encode)) return 1; else return 0; }
若是直接使用RSA對密文簽名,若密文長度很長,那麼RSA的效率就會大打折扣,因此每每不對密文簽名,而是對密文的Hash碼進行數字簽名,待接收方收到後,先驗籤,而後把收到的密文進行Hash,對比二者Hash值來進行數字簽名,看似過程繁瑣,實際上能夠有效地減小通訊開銷,提升計算效率。
若是發送方(Alice)和接收方(Bob)的公鑰的模數相同,這樣是存在安全隱患的!加入有兩組公鑰,分別屬於Alice和Bob:
Alice {
big e1;
big n;
}
Bob {
big e2;
big n;
}
加密以下:
c1 = m^e1 mod n
c2 = m^e2 mod n
倘若e1和e2互質,則存在***的可能,由擴展歐幾里得算法得出:
r * e1 + s * e2 = 1,能夠求得 r 和 s。
因而,計算:
c1^r * c2 ^s = m ^ (r * e1 + s * e2) = m mod n = m
因而這樣就***出了明文m,可是關於衆多***方法,我仍是有不少不理解的地方,比方說這個《公共模數***》,一個消息m怎麼會用雙方不一樣的公鑰加密兩次,而且還在信道中通訊呢?挺不能理解的,下午再去向導師請教請教!
#ifndef __MyRSA__ #define __MyRSA__ #define _CRT_SECURE_NO_WARNINGS #include "miracl.h" #include "mirdef.h" #include <time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct PrimaryKey { big p; big q; big phi; big d; } PrimaryKey; typedef struct PublicKey { big n; big e; } PublicKey; typedef struct TupleKey { struct PrimaryKey* sk; struct PublicKey* pk; } TupleKey; typedef struct MyRSA { struct TupleKey* Key;// 密鑰對 big Encoding;// 明文對應的大數 big Decoding;// 密文對應的大數 } MyRSA; miracl* mip; /*----------------------定義函數接口---------------------------*/ // 初始化RSA體制系統 void InitMySystem(); // 生成公私密鑰對 struct TupleKey* GenKey(); // 加密和解密算法接口 big Encrypt(unsigned char* msg, struct PublicKey* pk, big n); void Decrypt(big Encode, struct PrimaryKey* sk, big n, unsigned char* Msg); // 數字簽名的簽名和簽證算法接口 big Signature(big Encode, struct PrimaryKey* sk, big n); int Verify(big sig, struct PublicKey* pk, big Encode); #endif // !__MyRSA__
#include "MyRSA.h" void InitMySystem() { mip = mirsys(5000, 16); irand((unsigned int)time(NULL)); } struct TupleKey* GenKey() { big GenPrime = mirvar(0), temp1 = mirvar(0), temp2 = mirvar(0); struct TupleKey* ret = (struct TupleKey*)malloc(sizeof(struct TupleKey)); ret->sk = (struct PrimaryKey*)malloc(sizeof(struct PrimaryKey)); ret->pk = (struct PublicKey*)malloc(sizeof(struct PublicKey)); ret->sk->p = mirvar(0), ret->sk->q = mirvar(0); ret->sk->phi = mirvar(0), ret->sk->d = mirvar(0); ret->pk->e = mirvar(0), ret->pk->n = mirvar(0); while (1) { bigbits(256, GenPrime); if (nxprime(GenPrime, ret->sk->p)) { premult(ret->sk->p, 2, ret->sk->q); if (nxprime(ret->sk->q, ret->sk->q)) { // n = p * q multiply(ret->sk->p, ret->sk->q, ret->pk->n); decr(ret->sk->p, 1, temp1); decr(ret->sk->q, 1, temp2); // phi = (p - 1) * (q - 1) multiply(temp1, temp2, ret->sk->phi); // e = 65537 convert(65537, ret->pk->e); // d = e^-1 mod phi xgcd(ret->pk->e, ret->sk->phi, ret->sk->d, ret->sk->d, ret->sk->d); break; } } } return ret; } big Encrypt(unsigned char* msg, struct PublicKey* pk, big n) { big c = mirvar(0), m = mirvar(0); unsigned char* temp = (unsigned char*)malloc(sizeof(unsigned char*) * strlen(msg)); strcpy(temp, msg); bytes_to_big((int)strlen(temp), temp, m);// 把字符串轉成大數 // c = m^e mod n powmod(m, pk->e, n, c); return c; } void Decrypt(big Encode, struct PrimaryKey* sk, big n, unsigned char* Msg) { // m = c^d mod n big m = mirvar(0); unsigned char OutStr[128] = { '\0' }; powmod(Encode, sk->d, n, m); big_to_bytes(256, m, OutStr, 0); strcpy(Msg, OutStr); } big Signature(big Encode, struct PrimaryKey* sk, big n) { // sig = c^d mod n big d = sk->d, c = Encode; big sig = mirvar(0); powmod(c, d, n, sig); return sig; } int Verify(big sig, struct PublicKey* pk, big Encode) { // y = sig^da mod n, if y == Encode then be verified big e = pk->e, n = pk->n; big ver = mirvar(0); powmod(sig, e, n, ver); if (!mr_compare(ver, Encode)) return 1; else return 0; }
#include "MyRSA.h" int main() { InitMySystem(); struct MyRSA* InstanceA, *InstanceB; big text = mirvar(0); InstanceA = (struct MyRSA*)malloc(sizeof(struct MyRSA)); InstanceB = (struct MyRSA*)malloc(sizeof(struct MyRSA)); InstanceA->Key = GenKey(); InstanceB->Key = GenKey(); printf("\nAlice 的 p , q , d = \n"); cotnum(InstanceA->Key->sk->p, stdout); cotnum(InstanceA->Key->sk->q, stdout); cotnum(InstanceA->Key->sk->d, stdout); printf("\nBob 的 p , q , d = \n"); cotnum(InstanceB->Key->sk->p, stdout); cotnum(InstanceB->Key->sk->q, stdout); cotnum(InstanceB->Key->sk->d, stdout); printf("RSA加密體制已經啓動, 規定的是Alice給Bob發消息\n\n請輸入您要加密的消息:\n"); unsigned char msg[128] = { '\0' }; gets(msg); printf("\n此時的明文是:\n"); puts(msg); InstanceA->Encoding = Encrypt(msg, InstanceB->Key->pk, InstanceB->Key->pk->n); printf("\nAlice給Bob發送的消息的密文部分爲:\n"); cotnum(InstanceA->Encoding, stdout); big sig = Signature(InstanceA->Encoding, InstanceA->Key->sk, InstanceA->Key->pk->n); printf("\nAlice給Bob發送的消息的數字簽名部分爲:\n"); cotnum(sig, stdout); int isVerified = Verify(sig, InstanceA->Key->pk, InstanceA->Encoding); if (isVerified) printf("\nBob已經對Alice發送的消息成功完成簽證!\n"); else printf("\nBob已經對Alice發送的消息簽證失敗!\n"); printf("\nBob收到的消息解密出來的結果是:\n"); unsigned char* Decode[128] = { '\0' }; Decrypt(InstanceA->Encoding, InstanceB->Key->sk, InstanceB->Key->pk->n, Decode); puts(Decode); return 0; }