在上世纪七八十年代,公钥密码学提出来以后,基于数学难题(陷门函数)的公钥密码体制便获得了发展,最基础的密码体制就是基于“大整数分解难题”的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; }