現代密碼學原理與實現——任務一:RSA體制下的加密、解密、簽名、簽證

任務一:RSA體制下的加密、解密、簽名、簽證

分解任務1:什麼是RSA體制?

1.一、公鑰密碼學與數學難題

在上世紀七八十年代,公鑰密碼學提出來以後,基於數學難題(陷門函數)的公鑰密碼體制便獲得了發展,最基礎的密碼體制就是基於「大整數分解難題」的RSA密碼體制。利用的單向陷門函數是這樣定義的:已知兩個大素數 p 和 q,其中(p - 1, q - 1)的結果較大,則:經過 p * q 獲得大整數 n 是很容易的,可是反過來倒是陷門,由於經過大整數 n 獲得素數 p 和 q 是很困難的!這就是RSA密碼體制基於的數學難題。算法

1.二、公鑰密碼與數字簽名

公鑰密碼體制要求有一組密鑰對,一個公鑰一個私鑰。它們在安全通訊過程當中扮演不一樣角色,起着不一樣的做用。直觀來看,通訊具備一下過程,假設通訊雙方是 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

分解任務2:如何實現RSA對信息加密、解密

首先要設定公鑰和私鑰,基於大整數分解難題,則大整數爲公鑰,分解的兩個大素數 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實現的時候,如何高效地產生咱們所須要的公私密鑰呢?使用到的第三方函數以下:測試

第一:生成1024bit的大素數

由於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
// 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);
}

分解任務3:如何實現RSA對消息摘要、簽證

第一:簽名與簽證的方案

在以前的分解任務1.2中咱們談到了公鑰加密和數字簽名的含義,也就是說,咱們只須要拿着發送方本身的私鑰進行簽名,而後對方收到簽名後用發送方公開的公鑰去簽證便可!
咱們選取《密碼與編碼理論》一書上關於RSA數字簽名的方案:
在這裏插入圖片描述
咱們發現,這個數字簽名的過程和加密解密的過程極其類似,因此咱們獲得的方案以下:

①被簽名的數據是發送方加密後的密文
②用發送方(Alice)的私鑰dA進行簽名,並把(Encode, Signature)發給Bob
③接收方(Bob)用Alice的公鑰eA進行簽證,獲得CMP = Sig ^ eA mod n,而後對比CMP和收到的Encode是否一致,推斷該消息是否是來自Alice,以及推斷數據的完整和可靠。

第二:實現RSA直接簽名、簽證的代碼:
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;
}

分解任務4:使用RSA進行數字簽名的優缺點

若是直接使用RSA對密文簽名,若密文長度很長,那麼RSA的效率就會大打折扣,因此每每不對密文簽名,而是對密文的Hash碼進行數字簽名,待接收方收到後,先驗籤,而後把收到的密文進行Hash,對比二者Hash值來進行數字簽名,看似過程繁瑣,實際上能夠有效地減小通訊開銷,提升計算效率。

分解任務5:RSA算法的安全性分析

第一:公共模數***

若是發送方(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怎麼會用雙方不一樣的公鑰加密兩次,而且還在信道中通訊呢?挺不能理解的,下午再去向導師請教請教!

分解任務6:使用Miracl算法庫編碼出本身的RSA算法,以及測試結果

頭文件:

#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__

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;
}

測試結果以下截圖:

在這裏插入圖片描述

相關文章
相關標籤/搜索