现代密码学原理与实现——任务一:RSA体制下的加密、解密、签名、签证

任务一:RSA体制下的加密、解密、签名、签证

分解任务1:什么是RSA体制?

1.1、公钥密码学与数学难题

在上世纪七八十年代,公钥密码学提出来之后,基于数学难题(陷门函数)的公钥密码体制便得到了发展,最基础的密码体制就是基于“大整数分解难题”的RSA密码体制。利用的单向陷门函数是这样定义的:已知两个大素数 p 和 q,其中(p - 1, q - 1)的结果较大,则:通过 p * q 得到大整数 n 是很容易的,但是反过来却是陷门,因为通过大整数 n 得到素数 p 和 q 是很困难的!这就是RSA密码体制基于的数学难题。

1.2、公钥密码与数字签名

公钥密码体制要求有一组密钥对,一个公钥一个私钥。它们在安全通信过程中扮演不同角色,起着不同的作用。直观来看,通信具有一下过程,假设通信双方是 Alice 和 Bob,设定为 Alice 向 Bob 发消息Msg :
已知:
Alice 的公钥是 Ya, 私钥是 Xa;Bob 的公钥是 Yb, 私钥是 Xb
分析如下:

①如若Alice要发给Bob的消息不想被攻击者Attack获取,需要对信息加密,而加密的意义就在于不能被Attack解密而得到明文,所以我们得用公钥进行加密,因为我们必须要有私钥(确定是接收方)才能解密。这里我们的接收方是Bob,于是就用Bob的公钥Yb进行加密,这样Bob才能用自己的私钥解密收到Msg。由于公钥是公开的,也只能拿来加密,所以就算敌手Attack获取到密文和公钥(能够在通信过程中获取到的只有这俩信息),也无法得到明文。

②在上述内容中,我们只能保障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。

分解任务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要大的下一个素数

这里我们之所以不使用:

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

测试结果如下截图:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44274276/article/details/107283731