# ZJY-RSA
**Repository Path**: pukailiang/ZJY-RSA
## Basic Information
- **Project Name**: ZJY-RSA
- **Description**: 非对称加密算法
- **Primary Language**: C
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 3
- **Created**: 2023-02-17
- **Last Updated**: 2023-02-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## RSA-1024bit
### 介绍
非对称加密算法--基于 RSAES-PKCS1-v1_5 体系实现
### 原理描述
- RSA是一种非对称加密算法,采用随机生成两个大素数`p`和`q`,并通过`p`和`q`进一步计算得到对应的公钥`(n, e)`----私钥`(n, d)`对,其中`n`、`e`、`d`也是大整数。信息接收者A持有该私钥`pri`并且没有任何第二个人知道该密钥对(包括要给信息接收者发送信息的那个人),然后将私钥对应的公钥`pub`公开给全世界所有人。如果有人要给信息接收者A发送信息,信息内容会以某种固定方式进行加密,该加密过程需要用到A的私钥对应的公钥`pub`进行计算。加密之后得到一串密文,将密文通过某种方式发送给信息接收者A之后,A通过他的私钥`pri`对密文进行解密,解密过程也是一系列固定的计算过程且也需要用到私钥`pri`。解密之后的内容就是信息发送者所要发送的真实内容。
- 上述原理描述看起来会让人觉得过程非常简单,很容易就能破解,但是这个非对称加密算法的安全性还是挺高的。对极大整数进行因数分解的难度 (The Factoring Problem) 决定了 RSA 算法的可靠性。换言之,对一个极大整数做因数分解愈困难,RSA 算法就愈可靠。假如有人找到一种快速因数分解的算法的话,那么用 RSA 加密的信息的可靠性就肯定会极度下降。
- 目前还没有可靠的攻击 RSA 算法的方式。短的 RSA 钥匙可能被强力方式解破 (比如 768个二进制位、即对应于232个十进制位以下的整数)。只要 RSA 密钥的长度足够长 (比如 1024-bit 至 15360-bit),用RSA 加密的信息看来很难被破解。
### 数据结构设计
- 本次RSA算法的实现用到了`gmp`大数库来实现大数的运算,可以大大优化算法的速度和降低算法实现的难度。但要学习`gmp`大数库的相关知识。
- 由于操作的对象比较容易描述,大部分内容可以用字符数组来进行操作和存储。所以并没有使用其他的数据结构。
### 密钥生成
- 生成过程:
+ 选择两个不同的大素数 p 和 q,计算 N = pq。
+ 引用欧拉 $\varphi$ 函数,小于 N 且与 N 互素的正整数个数为$\varphi$(N) = $\varphi$(pq) = $\varphi$(p)$\varphi$(q) = (p-1) (q-1)。
+ 选择一个整数 e,1< e < $\varphi$(N) 且 gcd(e, $\varphi$(N)) = 1。
+ 求一个满足后面同余式的 d:ed $\equiv$ 1 (mod $\varphi$(N))。其中 d 是 e 模 $\varphi$(N)逆元,可以通过扩展欧几里得算法得到且 d 是必须是一个足够大的正整数。
+ 将 p 和 q 的记录销毁 (当 N 足够大时,寻找 p, q 将极其困难)。
+ 以 (e, N) 作为公钥,(d, N) 作为私钥。假设 Alice 想通过一个不可靠的媒体接收 Bob 的一条私人讯息,Alice 将她的公钥 (e, N) 公开给 Bob,而将她的私钥 (d, N) 严密收藏起来。
- 代码实现如下:
``` C
void GenerateKey(mpz_t e, mpz_t d, mpz_t N) {
// 选择两个不同的大素数
// 设置随机生成数
gmp_randstate_t grst;
gmp_randinit_default(grst);
gmp_randseed_ui(grst, time(NULL));
// 生成随机大素数
mpz_t p, q, N1;
mpz_inits(p, q, N1, NULL);
//验证e和N的欧拉是否互斥
mpz_t mol;
mpz_init(mol);
// 0 <= p,q <= 2^MODULU_BITS
while(1) {
mpz_urandomb(p, grst, MODULU_BITS / 2 - 1);
mpz_urandomb(q, grst, MODULU_BITS / 2 + 1);
// 调整为素数
mpz_nextprime(p, p);
mpz_nextprime(q, q);
// N = p*q
mpz_mul(N, p, q);
// phi(N) = (p-1)*(q-1)
mpz_sub_ui(p, p, 1);
mpz_sub_ui(q, q, 1);
mpz_mul(N1, p, q);
// chose e
// such that 1 < e < phi(N), and gcd(e, phi(N))=1
// 1. 65537
mpz_init_set_ui(e, 65537);
// 求e的逆元, ed mod phi(N) = 1
mpz_invert(d, e, N1);
// gmp_printf("PUB: \ne = %Zx\nN = %Zx\n", e, N);
// gmp_printf("PRI: \nd = %Zx\nN = %Zx\n", d, N);
mpz_gcd(mol, N1, e);
// gmp_printf("%Zd\n", mol);
char buf[2*MODULU_BYTES + 1] = { '\0' };
//cout << "strlen(buf): " << strlen(buf) << endl;
gmp_sprintf(buf, "%0Zx", N);
// cout << "strlen(buf): " << strlen(buf) << endl;
// cout << "buf: " << buf << endl;
if(strlen(buf) == MODULU_BYTES * 2 && mpz_cmp_ui(mol, 1) == 0) {
//mpz_clear(mol);
break;
}
}
//释放内存
mpz_clears(p, q, N1, mol, NULL);
}
```
### 加密
- 加密过程
+ 现在 Bob 想给 Alice 送一个消息 M,他知道 Alice 产生的公钥 (e, N)。
+ Bob 使用事先与 Alice 约好的格式将 M 转换为一个小于 N 的整数 n,比如他可以将每一个字符转换为其数字形式的 Unicode 码,然后将这些数字连在一起组成整数 n (假如他的信息非常长的话,他可以将这个信息分为几段,逐段加密)。
+ Bob 引用公钥 (e, N),利用后面的公式,将 n 加密为 c: c = ne mod N。
+ Bob 算出 c 后可以将它经公开媒体传递给 Alice。
- 代码实现如下:
``` C
/*
* RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M)
* Iuput: (n, e): recipient’s RSA public key (k denotes the length in octets of the modulus n)
* M: message to be encrypted, an octet string of length mLen, where mLen <= k - 11
* Ouput: C: ciphertext, an octet string of length k
* Error: "message too long"
*/
void RSAES_PKCS1_V1_5_ENCRYPT(mpz_t n, mpz_t e, const unsigned char* M, unsigned char* C) {
unsigned int m2Len = strlen((const char*)M);
unsigned int mLen = m2Len / 2;
//1. Length checking
if (mLen > MODULU_BYTES - 11) {
printf("ERROR: message too long\n");
exit(-1);
}
//2. EME-PKCS1-v1_5 encoding
char EM[2*MODULU_BYTES+1] = { '\0' };
PKCS1_encoding(M, mLen, MODULU_BYTES, (unsigned char*)EM);
// cout << "EM is: " << EM << endl;
// cout << "strlen(EM) is: " << strlen(EM) << endl;
//3. RSA encryption:
// 3.a OS2IP
mpz_t m;
mpz_init(m);
OS2IP((unsigned char*)EM, m, MODULU_BYTES);
//3.b RSAEP
mpz_t c;
mpz_init(c);
RSAEP(n, e, m, c);
//3.c I2OSP
I2OSP(c, MODULU_BYTES, C);
//释放内存
mpz_clear(m);
mpz_clear(c);
}
```
### 解密
- 解密过程
+ Alice 得到 Bob 的消息 c 后利用她的私钥 (d, N) 解码。
+ Alice 利用以下的公式将 c 转换为 n':n' = cd mod N
+ Alice 得到的 n’ 就是 Bob 的 n,因此可以将原来的信息 M 准确复原。
- 代码实现如下:
``` C
/*
* RSAES-PKCS1-V1_5-DECRYPT ((n, d), C)
* Iuput: (n, d): recipient’s RSA private key
* C: ciphertext to be decrypted, an octet string of length k
* where k is the length in octets of the RSA modulus n
* Ouput: M: message, an octet string of length at most k - 11
* Error: "decryption error"
*/
void RSAES_PKCS1_V1_5_DECRYPT(mpz_t n, mpz_t d, const unsigned char* C, unsigned char* M) {
//1. Length checking
if (strlen((const char*)C) != 2 * MODULU_BYTES || MODULU_BYTES < 11) {
printf("ERROR: decryption error\n");
exit(-1);
}
// cout << " hello1" << endl;
//2. RSA decryption
//2.a OS2IP
mpz_t c;
mpz_init(c);
OS2IP(C, c, MODULU_BYTES);
//2.b RSADP
mpz_t m;
mpz_init(m);
RSADP(n, d, c, m);
//2.c I2OSP
unsigned char *EM = (unsigned char *)malloc((2 * MODULU_BYTES + 1) * sizeof(char));
EM[2*MODULU_BYTES] = '\0';
I2OSP(m, MODULU_BYTES, EM);
//3. EME-PKCS1-v1_5 decoding
int mLen = 0;
PKCS1_decoding(EM, MODULU_BYTES, M, mLen);
//释放内存
free(EM);
mpz_clear(c);
mpz_clear(m);
}
```
### 编解码
#### 编码
- 对明文M进行填充操作:**EM = 0x00 || 0x02 || PS || 0x00 || M**
其中EM的长度为RSA的密钥长度,即1024bit。M的长度是可变的,PS的长度随着M的长度的改变而改变。在 PS 和 M 之间增加一个 0x00 是为了解码的时候能够正确的找出M。
- 代码实现如下:
``` C
/*
* EME-PKCS1-v1_5 encoding:
* a. Generate an octet string PS of length k - mLen - 3
* consisting of pseudo-randomly generated nonzero octets.
* The length of PS will be at least eight octets.
*
* b. Concatenate PS, the message M, and other padding to form
* an encoded message EM of length k octets as
*
*/
void PKCS1_encoding(const unsigned char* M, int mLen, int k, unsigned char* EM) {
int PS_length = k - mLen - 3;
int PS_int[PS_length];
char buf[3] = {'\0'};
char *PS_char = (char*)malloc((2 * PS_length + 1) * sizeof(char));
PS_char[2*PS_length] = '\0';
PS_char[0] = '\0';
// EM = (char*)malloc((2 * k + 1) * sizeof(char));
// EM[2*k] = '\0';
srand(time(0));
for (int i = 0; i < PS_length; ++i) {
PS_int[i] = 1 + rand() % 255;
//cout << PS_int[i] << endl;
sprintf(buf, "%02x", PS_int[i]);
strcat(PS_char, buf);
}
EM[0] = '0';
EM[1] = '0';
EM[2] = '0';
EM[3] = '2';
EM[4] = '\0';
strcat((char*)EM, PS_char);
EM[4+2*PS_length] = '0';
EM[5+2*PS_length] = '0';
EM[6+2*PS_length] = '\0';
strcat((char*)EM, (const char*)M);
//释放内存
free(PS_char);
}
```
#### 解码
- 解码过程和编码过程相反,是从EM中找出有效的明文M,根据EM的特性来找出M。在这里不加赘述。
- 代码实现如下:
``` C
/*
* EME-PKCS1-v1_5 decoding
* Separate the encoded message EM into an octet string PS consisting of nonzero octets and a message M as
* EM = 0x00 || 0x02 || PS || 0x00 || M
*/
void PKCS1_decoding(const unsigned char* EM, int k, unsigned char* M, int mLen) {
if (EM[0] != '0' || EM[1] != '0' || EM[2] != '0'|| EM[3] != '2') {
printf("ERROR: decryption error\n");
exit(-1);
}
int PS_length = 0;
int i = 4;
while (i < 2 * k) {
if (EM[i] == '0' && EM[i+1] == '0') {
break;
} else {
PS_length++;
i = i + 2;
}
}
if (PS_length < 8 || PS_length == k - 2) {
printf("ERROR: decryption error\n");
exit(-1);
}
for (int j = i + 2; j < 2 * k; ++j) {
M[j-i-2] = EM[j];
}
M[2*k-i-2] = '\0';
mLen = strlen((const char*)M);
// printf("PKCS1_decoding: EM is %s\n", EM);
// printf("PKCS1_decoding: M is %s\n", M);
}
```