RSA算法实现

目录

一、实验目的

二、实验内容

三、实验原理

3.1 RSA 算法原理

1. 密钥生成

2. 加密过程

3. 解密过程

4. RSA 算法的安全性

四、实验步骤

4.1 加解密算法设计

4.2 加解密算法实现

4.2.1 素性检验

4.2.2 公私钥生成

4.2.3 模幂运算

4.2.4  RSAES-OAEP 填充

4.2.5  RSAES-PKCS1-V1_5 填充

五、实验结果

5.1 RAS无填充机制加解密

5.2 RSAES-OAEP 填充机制加解密

5.3 RSAES-PKCS1-V1_5 填充机制加解密

6. 源代码

6.1 无填充

6.2 RSAES-OAEP 填充机制

6.3 RSAES-PKCS1-V1_5 填充机制


一、实验目的

理解 RSA 算法原理:通过实现 RSA 算法,深入理解公钥加密、私钥解密以及密钥对生成的原理。掌握如何通过大数分解难题来保证数据安全。

实现加密与解密过程:通过编写 C 语言程序,实现 RSA 加密和解密的过程,了解如何使用公钥加密消息,使用私钥解密消息,验证加解密的正确性。

实现不同的填充机制:实现 RSAES-OAEP 和 RSAES-PKCS1-V1_5 两种常见的填充机制,理解它们如何增强 RSA 算法的安全性。

加密多次验证结果一致性:验证在相同密钥对的条件下,多次对同一明文进行加密时,加密结果是否相同,了解 RSA 算法中涉及的非确定性因素。

二、实验内容

使用C语言编译环境(VC++6.0)编程实现RSA算法。

三、实验原理

3.1 RSA 算法原理

RSA (Rivest–Shamir–Adleman) 是一种广泛使用的公钥加密算法,基于大整数分解的数学难题(即大数的质因数分解)来保证其安全性。RSA 主要用于数据加密和数字签名。

1. 密钥生成

RSA 密钥对包括公钥和私钥,用于加密和解密数据。生成密钥对的步骤如下:1.选择两个大素数 p 和 q,这两个数在数学上是保密的。

2.计算 n = p * q,这个 n 被用作公钥和私钥的一部分。

3.计算 φ(n) = (p-1) * (q-1),其中 φ(n) 是 n 的欧拉函数,用于后续的计算。

4.选择一个小于 φ(n) 且与 φ(n) 互质的整数 e,作为公钥的指数。通常选择较小的常数值(如 65537)。

5.计算 d,使得 d * e ≡ 1 (mod φ(n))。这里的 d 是 e 关于 φ(n) 的模反元素,即 d 是私钥的一部分。

所以,最终的公钥是 (e, n),私钥是 (d, n)。

2. 加密过程

加密过程的步骤是使用接收方的公钥 (e, n) 对消息进行加密。假设消息 M 是一个整数,且 M 必须满足 M < n,如果消息是文本,首先需要将其转换为数字。加密的过程如下:

将消息 M 转换为数字(通常使用 ASCII 编码)。

使用接收方的公钥 (e, n) 对消息进行加密:

3. 解密过程

解密过程是使用私钥 (d, n) 对密文进行解密。解密的步骤如下:

使用接收方的私钥 (d, n) 对密文 C 进行解密:

4. RSA 算法的安全性

RSA 算法的安全性基于大数分解问题。即,对于一个给定的 n,很难从中推导出 p 和 q。一旦 n 的质因数分解被破解,私钥就会被揭露。

由于 RSA 的加密过程涉及对消息进行模幂运算,它在数学上是可逆的。只要公钥和私钥配对正确,加密后的数据就能通过私钥成功解密。

四、实验步骤

4.1 加解密算法设计

首先设计rsa加解密流程,之后在此基础上加入两种填充机制

4.2 加解密算法实现

4.2.1 素性检验

在本实验中,采取随机生成两个数字,再利用米勒罗宾算法判断其是否为素数的方式来生成两个大素数(本实验中其实是比较小的两个数字)。

米勒-拉宾素性测试的核心思想是通过“反复平方”和“模幂”运算来判断一个数是否是素数。如果一个数在经过若干次测试后仍然通过了测试,它很可能是素数,但也有一定的概率是合数。测试的次数越多,结果的可信度越高。

我们写出相应算法:

// 米勒-拉宾素性测试
int miller_rabin_test(int n, int k) {
    // 若 n 是偶数,直接返回 false
    if (n == 2) return 1;
    if (n < 2 || n % 2 == 0) return 0;

    // 计算 n-1 = 2^s * d,其中 d 为奇数
    int d = n - 1;
    int s = 0;
    while (d % 2 == 0) {
        d /= 2;
        s++;
    }

    // k 次测试
    for (int i = 0; i < k; i++) {
        // 生成随机数 a,1 <= a <= n-1
        int a = rand() % (n - 1) + 1;


        int x = 1;
        for (int j = 0; j < d; j++) {
            x = (x * a) % n;
        }
        if (x == 1 || x == n - 1) continue;  //基数 a 已经通过了这个测试,直接跳过本次循环,继续进行下一个测试


        // 反复平方 a次,直到得到 n-1 或者 x == 1
        int r = 0;
        for (int j = 0; j < s - 1; j++) {
            x = (x * x) % n;
            if (x == n - 1) break;
            if (x == 1) return 0;  // 不是素数
        }

        if (x != n - 1) return 0;  // 不是素数
    }

    return 1;  // 是素数
}

测试是否可行(计算100以内的所有素数以验证):

随机生成大数,如果通过素性测试,则将之定为pq中的一个,再用pq计算出n和phi

4.2.2 公私钥生成

首先计算私钥

随机生成一个数字e测试是否与phi互素,如果互素,则选定e

通过欧几里得算法判断是否互素

// 计算最大公约数(GCD)
int gcd(int e, int phi) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

如果结果是1,则e与phi互素,e通过测试

当得到e之后,我们通过扩展欧几里得计算出公钥d

// 求模反元素 (扩展欧几里得算法)
int mod_inverse(int e, int phi) {
    int t = 0, new_t = 1;
    int r = phi, new_r = e;
    while (new_r != 0) {
        int quotient = r / new_r;
        int temp_t = t;
        t = new_t;
        new_t = temp_t - quotient * new_t;

        int temp_r = r;
        r = new_r;
        new_r = temp_r - quotient * new_r;
    }
    if (t < 0) t += phi;
    return t;
}

由此我们得到公私钥e和d

4.2.3 模幂运算

此运算运用在加解密过程中

此运算中实际是将指数转化为二进制的形式,在运算过程中迭代

代码实现如下

// 快速模幂算法
long long mod_exp(long long base, long long exp, long long mod) {
    long long result = 1;
    base = base % mod;
    while (exp > 0) {
        if (exp % 2 == 1) {
            result = (result * base) % mod;
        }
        exp = exp >> 1;
        base = (base * base) % mod;
    }
    return result;
}

至此,基本的加解密算法已实现,以下是两种不同的填充方式。

4.2.4  RSAES-OAEP 填充

OAEP 填充流程

假设消息长度 M 不超过 k−2h−2(h 是哈希长度,通常为 20 字节)。

RSA 模数长度为 k 字节

使用两个哈希函数 G 和 H,其输出长度为 h

填充步骤:

原始消息 M 加前置 0(零填充):

如果消息 M 长度不足 k−2h−2,先进行零填充,使其长度满足要求。

生成随机数 r:

随机生成长度为 h 字节的种子 r。

生成掩码数据:

使用哈希函数 G 生成掩码数据:

DataBlock=M | Padding | 0x01 | 0x00

MaskedDataBlock=DataBlock⊕G(r)

生成种子掩码:

使用哈希函数 H 生成种子掩码:

MaskedSeed=r⊕H(MaskedDataBlock)

拼接:

将填充数据组合成填充后的明文: PaddedMessage=MaskedSeed | MaskedDataBlock

代码实现如下:

// OAPE填充函数:填充至指定长度并生成One-hot编码
void oape_padding(char* s, int* padded_text, int max_len) {
    int s_len = strlen(s);
    for (int i = 0; i < s_len; i++) {
        padded_text[i] = s[i];  // 填充原始文本
    }
    // One-hot编码填充字符
    for (int i = s_len; i < max_len; i++) {
        padded_text[i] = 0xFF;  // 填充为0(表示填充位置),你也可以用其他标记
    }
}

// 解填充函数:去除填充的One-hot编码
void oape_unpadding(char* decrypted, int* decrypted_text, int max_len) {
    int i;
    for (i = 0; i < max_len; i++) {
        if (decrypted_text[i] == 0xFF) {
            break;  // 一旦遇到填充标记就停止
        }
        decrypted[i] = decrypted_text[i];  // 恢复原始字符
    }
    decrypted[i] = '\0'; // 添加字符串结束符
}
4.2.5  RSAES-PKCS1-V1_5 填充

PKCS#1 v1.5 填充与解填充原理

PKCS#1 v1.5 填充标准广泛应用于 RSA 加密算法,旨在确保加密消息的长度与 RSA 模数相匹配。填充过程不仅保护了数据的安全性,还有助于防止某些加密攻击(如选择明文攻击)。

1. PKCS#1 v1.5 填充(pkcs1_v1_5_pad)

功能:将消息填充为特定格式,以适应 RSA 加密的要求。

填充格式:

消息必须填充到 RSA 模数的大小(k 字节),其中包括:

第一字节 0x00:填充开始标志。

第二字节 0x02:填充类型标志。

随机填充字节(不包含 0x00),确保填充后的数据满足 k - mLen - 1 字节的长度。

一个字节 0x00:填充结束标志。

剩余部分为原始消息数据。

填充步骤:

检查消息长度:如果消息的长度(mLen)超过了 k - 11(即填充后需要至少剩下 11 个字节),则报错并退出。这个 11 字节包括头部标志(0x00 0x02),填充数据和结束标志(0x00)。

填充开始:填充的第一个字节设置为 0x00,第二个字节设置为 0x02。

随机填充:接下来,为填充部分分配随机字节,直到填充数据的长度为 k - mLen - 3(为剩下的字节留出空间)。填充数据不能包含 0x00,因为这会干扰解填充过程。

结束标志:填充数据的最后一个字节设置为 0x00,然后将原始消息数据复制到填充后的消息末尾。

代码实现如下:

// PKCS#1 v1.5 填充
void pkcs1_v1_5_pad(unsigned char* message, size_t mLen, unsigned char* padded, size_t k) {
    if (mLen > k - 11) {
        fprintf(stderr, "消息太长,无法填充\n");
        exit(EXIT_FAILURE);
    }
    padded[0] = 0x00;
    padded[1] = 0x02;
    for (size_t i = 2; i < k - mLen - 1; i++) {
        do { padded[i] = rand() % 256; } while (padded[i] == 0);
    }
    padded[k - mLen - 1] = 0x00;
    memcpy(&padded[k - mLen], message, mLen);
}

// PKCS#1 v1.5 解填充
void pkcs1_v1_5_unpad(unsigned char* padded, size_t k, unsigned char* message, size_t* mLen) {
    if (padded[0] != 0x00 || padded[1] != 0x02) {
        fprintf(stderr, "解填充失败,格式错误\n");
        exit(EXIT_FAILURE);
    }

    size_t start = 2;
    while (start < k && padded[start] != 0x00) start++;
    if (start == k) {
        fprintf(stderr, "解填充失败,未找到结束标志\n");
        exit(EXIT_FAILURE);
    }

    *mLen = k - start - 1;
    memcpy(message, &padded[start + 1], *mLen);
}

五、实验结果

5.1 RAS无填充机制加解密

案例1第一次:输入Hello world! I am XuXuan!

生成参数如下:

生成的素数 p = 967, q = 809

生成的公钥指数 e = 14827

生成的公钥: (e = 14827, n = 782303)

生成的私钥: (d = 714883, n = 782303)

案例1第二次:输入Hello world! I am XuXuan!

生成参数如下:

生成的素数 p = 701, q = 877

生成的公钥指数 e = 9983

生成的公钥: (e = 9983, n = 614777)

生成的私钥: (d = 488447, n = 614777)

案例1第三次:输入Hello world! I am XuXuan!

生成参数如下:

生成的素数 p = 739, q = 293

生成的公钥指数 e = 11675

生成的公钥: (e = 11675, n = 216527)

生成的私钥: (d = 28499, n = 216527)

案例2:输入QQQQ11112222WWWW

生成参数如下:

生成的素数 p = 971, q = 349

生成的公钥指数 e = 28981

生成的公钥: (e = 28981, n = 338879)

生成的私钥: (d = 264541, n = 338879)

案例3:输入RAS未填充版本测试3

生成参数如下:

生成的素数 p = 317, q = 787

生成的公钥指数 e = 26761

生成的公钥: (e = 26761, n = 249479)

生成的私钥: (d = 33097, n = 249479)

5.2 RSAES-OAEP 填充机制加解密

案例1第一次:输入Hello world! I am XuXuan!

生成参数如下:

生成的素数 p = 7, q = 467

生成的公钥指数 e = 1909

生成的公钥: (e = 1909, n = 3269)

生成的私钥: (d = 145, n = 3269)

案例1第二次:输入Hello world! I am XuXuan!

生成参数如下:

生成的素数 p = 269, q = 421

生成的公钥指数 e = 20081

生成的公钥: (e = 20081, n = 113249)

生成的私钥: (d = 41681, n = 113249)

案例1第三次:输入Hello world! I am XuXuan!

生成参数如下:

生成的素数 p = 523, q = 359

生成的公钥指数 e = 17761

生成的公钥: (e = 17761, n = 187757)

生成的私钥: (d = 173545, n = 187757)

案例2:输入QQQQ11112222WWWW

生成参数如下:

生成的素数 p = 701, q = 613

生成的公钥指数 e = 18953

生成的公钥: (e = 18953, n = 429713)

生成的私钥: (d = 23417, n = 429713)

案例3:输入RSAES-OAEP 填充机制测试3

生成参数如下:

生成的素数 p = 13, q = 1013

生成的公钥指数 e = 11459

生成的公钥: (e = 11459, n = 13169)

生成的私钥: (d = 9467, n = 13169)

5.3 RSAES-PKCS1-V1_5 填充机制加解密

案例1第一次:输入Hello world! I am XuXuan!

生成参数如下:

生成的素数 p = 421, q = 23

生成的公钥指数 e = 8341

公钥: (e = 8341, n = 9683)

私钥: (d = 5581, n = 9683)

案例1第二次:输入Hello world! I am XuXuan!

生成参数如下:

生成的素数 p = 761, q = 877

生成的公钥指数 e = 6601

公钥: (e = 6601, n = 667397)

私钥: (d = 237721, n = 667397)

案例1第三次:输入Hello world! I am XuXuan!

生成参数如下:

生成的素数 p = 751, q = 13

生成的公钥指数 e = 1957

公钥: (e = 1957, n = 9763)

私钥: (d = 4093, n = 9763)

案例2:输入QQQQ11112222WWWW

生成参数如下:

生成的素数 p = 941, q = 883

生成的公钥指数 e = 21793

公钥: (e = 21793, n = 830903)

私钥: (d = 805417, n = 830903)

案例3:输入RSAES-PKCS1-V1_5 填充机制测试3

生成参数如下:

生成的素数 p = 67, q = 433

生成的公钥指数 e = 9859

公钥: (e = 9859, n = 29011)

私钥: (d = 5515, n = 29011)

6. 源代码

6.1 无填充

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

// 米勒-拉宾素性测试
int miller_rabin_test(int n, int k) {
    // 若 n 是偶数,直接返回 false
    if (n == 2) return 1;
    if (n < 2 || n % 2 == 0) return 0;

    // 计算 n-1 = 2^s * d,其中 d 为奇数
    int d = n - 1;
    int s = 0;
    while (d % 2 == 0) {
        d /= 2;
        s++;
    }

    // k 次测试
    for (int i = 0; i < k; i++) {
        // 生成随机数 a,1 <= a <= n-1
        int a = rand() % (n - 1) + 1;
        int x = 1;
        for (int j = 0; j < d; j++) {
            x = (x * a) % n;
        }
        if (x == 1 || x == n - 1) continue;

        // 反复平方 a,直到得到 n-1 或者 x == 1
        int r = 0;
        for (int j = 0; j < s - 1; j++) {
            x = (x * x) % n;
            if (x == n - 1) break;
            if (x == 1) return 0;  // 不是素数
        }

        if (x != n - 1) return 0;  // 不是素数
    }

    return 1;  // 是素数
}

// 生成一个大素数
int generate_large_prime(int bit_length, int k) {
    int num;
    do {
        // 随机生成一个大数
        num = rand() % (1 << bit_length);
        num |= 1;  // 保证 num 是奇数
    } while (!miller_rabin_test(num, k));

    return num;
}

// 计算最大公约数(GCD)
int gcd(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

// 快速模幂算法
long long mod_exp(long long base, long long exp, long long mod) {
    long long result = 1;
    base = base % mod;
    while (exp > 0) {
        if (exp % 2 == 1) {
            result = (result * base) % mod;
        }
        exp = exp >> 1;
        base = (base * base) % mod;
    }
    return result;
}

// 求模反元素 (扩展欧几里得算法)
int mod_inverse(int e, int phi) {
    int t = 0, new_t = 1;
    int r = phi, new_r = e;
    while (new_r != 0) {
        int quotient = r / new_r;
        int temp_t = t;
        t = new_t;
        new_t = temp_t - quotient * new_t;

        int temp_r = r;
        r = new_r;
        new_r = temp_r - quotient * new_r;
    }
    if (t < 0) t += phi;
    return t;
}

// 随机生成 e 并确保 e 和 φ(n) 互质
int generate_e(int phi) {
    int e;
    do {
        e = rand() % (phi - 2) + 2;  // 随机生成 2 到 φ(n) - 1 之间的数
    } while (gcd(e, phi) != 1);  // 确保 e 和 φ(n) 互质
    return e;
}

int main() {
    srand(time(NULL));  // 使用当前时间作为随机数种子

    // 1. 输入文本串 s
    char s[128];
    printf("请输入要加密的文本串 s: ");
    fgets(s, sizeof(s), stdin);
    s[strcspn(s, "\n")] = '\0'; // 移除换行符
    int s_len = strlen(s);

    // 动态分配内存
    long long* encrypted = (long long*)malloc(s_len * sizeof(long long));
    if (encrypted == NULL) {
        printf("内存分配失败\n");
        return -1;
    }
    char* decrypted = (char*)malloc((s_len + 1) * sizeof(char));
    if (decrypted == NULL) {
        printf("内存分配失败\n");
        free(encrypted);
        return -1;
    }

    // 2. 生成大素数 p 和 q
    int p = generate_large_prime(10, 10);  // 10 位数的素数
    int q = generate_large_prime(10, 10);  // 10 位数的素数

    printf("生成的素数 p = %d, q = %d\n", p, q);

    int n = p * q;
    int phi = (p - 1) * (q - 1);

    // 随机生成公钥指数 e
    int e = generate_e(phi);

    printf("生成的公钥指数 e = %d\n", e);

    int d = mod_inverse(e, phi);

    printf("生成的公钥: (e = %d, n = %d)\n", e, n);
    printf("生成的私钥: (d = %d, n = %d)\n", d, n);

    // 3. 加密文本
    printf("加密结果: ");
    for (int i = 0; i < s_len; i++) {
        encrypted[i] = mod_exp(s[i], e, n); // 加密每个字符
        printf("%lld ", encrypted[i]);
    }
    printf("\n");

    // 4. 解密文本
    printf("解密结果: ");
    for (int i = 0; i < s_len; i++) {
        decrypted[i] = mod_exp(encrypted[i], d, n); // 解密每个字符
        printf("%c", decrypted[i]);
    }
    decrypted[s_len] = '\0'; // 添加字符串结束符
    printf("\n");

    // 5. 比较原文本和解密结果
    if (strcmp(s, decrypted) == 0) {
        printf("解密成功: 原文本和解密文本一致\n");
    }
    else {
        printf("解密失败: 原文本和解密文本不一致\n");
    }

    // 释放动态内存
    free(encrypted);
    free(decrypted);

    return 0;
}

6.2 RSAES-OAEP 填充机制

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

// 米勒-拉宾素性测试
int miller_rabin_test(int n, int k) {
    // 若 n 是偶数,直接返回 false
    if (n == 2) return 1;
    if (n < 2 || n % 2 == 0) return 0;

    // 计算 n-1 = 2^s * d,其中 d 为奇数
    int d = n - 1;
    int s = 0;
    while (d % 2 == 0) {
        d /= 2;
        s++;
    }

    // k 次测试
    for (int i = 0; i < k; i++) {
        // 生成随机数 a,1 <= a <= n-1
        int a = rand() % (n - 1) + 1;
        int x = 1;
        for (int j = 0; j < d; j++) {
            x = (x * a) % n;
        }
        if (x == 1 || x == n - 1) continue;

        // 反复平方 a,直到得到 n-1 或者 x == 1
        int r = 0;
        for (int j = 0; j < s - 1; j++) {
            x = (x * x) % n;
            if (x == n - 1) break;
            if (x == 1) return 0;  // 不是素数
        }

        if (x != n - 1) return 0;  // 不是素数
    }

    return 1;  // 是素数
}

// 生成一个大素数
int generate_large_prime(int bit_length, int k) {
    int num;
    do {
        // 随机生成一个大数
        num = rand() % (1 << bit_length);
        num |= 1;  // 保证 num 是奇数
    } while (!miller_rabin_test(num, k));

    return num;
}

// 计算最大公约数(GCD)
int gcd(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

// 快速模幂算法
long long mod_exp(long long base, long long exp, long long mod) {
    long long result = 1;
    base = base % mod;
    while (exp > 0) {
        if (exp % 2 == 1) {
            result = (result * base) % mod;
        }
        exp = exp >> 1;
        base = (base * base) % mod;
    }
    return result;
}

// 求模反元素 (扩展欧几里得算法)
int mod_inverse(int e, int phi) {
    int t = 0, new_t = 1;
    int r = phi, new_r = e;
    while (new_r != 0) {
        int quotient = r / new_r;
        int temp_t = t;
        t = new_t;
        new_t = temp_t - quotient * new_t;

        int temp_r = r;
        r = new_r;
        new_r = temp_r - quotient * new_r;
    }
    if (t < 0) t += phi;
    return t;
}

// 随机生成 e 并确保 e 和 φ(n) 互质
int generate_e(int phi) {
    int e;
    do {
        e = rand() % (phi - 2) + 2;  // 随机生成 2 到 φ(n) - 1 之间的数
    } while (gcd(e, phi) != 1);  // 确保 e 和 φ(n) 互质
    return e;
}

int main() {
    srand(time(NULL));  // 使用当前时间作为随机数种子

    // 1. 输入文本串 s
    char s[128];
    printf("请输入要加密的文本串 s: ");
    fgets(s, sizeof(s), stdin);
    s[strcspn(s, "\n")] = '\0'; // 移除换行符
    int s_len = strlen(s);

    // 动态分配内存
    long long* encrypted = (long long*)malloc(s_len * sizeof(long long));
    if (encrypted == NULL) {
        printf("内存分配失败\n");
        return -1;
    }
    char* decrypted = (char*)malloc((s_len + 1) * sizeof(char));
    if (decrypted == NULL) {
        printf("内存分配失败\n");
        free(encrypted);
        return -1;
    }

    // 2. 生成大素数 p 和 q
    int p = generate_large_prime(10, 10);  // 10 位数的素数
    int q = generate_large_prime(10, 10);  // 10 位数的素数

    printf("生成的素数 p = %d, q = %d\n", p, q);

    int n = p * q;
    int phi = (p - 1) * (q - 1);

    // 随机生成公钥指数 e
    int e = generate_e(phi);

    printf("生成的公钥指数 e = %d\n", e);

    int d = mod_inverse(e, phi);

    printf("生成的公钥: (e = %d, n = %d)\n", e, n);
    printf("生成的私钥: (d = %d, n = %d)\n", d, n);

    // 3. 加密文本
    printf("加密结果: ");
    for (int i = 0; i < s_len; i++) {
        encrypted[i] = mod_exp(s[i], e, n); // 加密每个字符
        printf("%lld ", encrypted[i]);
    }
    printf("\n");

    // 4. 解密文本
    printf("解密结果: ");
    for (int i = 0; i < s_len; i++) {
        decrypted[i] = mod_exp(encrypted[i], d, n); // 解密每个字符
        printf("%c", decrypted[i]);
    }
    decrypted[s_len] = '\0'; // 添加字符串结束符
    printf("\n");

    // 5. 比较原文本和解密结果
    if (strcmp(s, decrypted) == 0) {
        printf("解密成功: 原文本和解密文本一致\n");
    }
    else {
        printf("解密失败: 原文本和解密文本不一致\n");
    }

    // 释放动态内存
    free(encrypted);
    free(decrypted);

    return 0;
}

6.3 RSAES-PKCS1-V1_5 填充机制

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

// 米勒-拉宾素性测试
int miller_rabin_test(int n, int k) {
    if (n == 2) return 1;
    if (n < 2 || n % 2 == 0) return 0;
    int d = n - 1, s = 0;
    while (d % 2 == 0) { d /= 2; s++; }
    for (int i = 0; i < k; i++) {
        int a = rand() % (n - 1) + 1;
        int x = 1;
        for (int j = 0; j < d; j++) x = (x * a) % n;
        if (x == 1 || x == n - 1) continue;
        for (int r = 0; r < s - 1; r++) {
            x = (x * x) % n;
            if (x == n - 1) break;
            if (x == 1) return 0;
        }
        if (x != n - 1) return 0;
    }
    return 1;
}

// 生成一个大素数
int generate_large_prime(int bit_length, int k) {
    int num;
    do {
        num = rand() % (1 << bit_length) | 1;
    } while (!miller_rabin_test(num, k));
    return num;
}

// 计算最大公约数(GCD)
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }

// 快速模幂
long long mod_exp(long long base, long long exp, long long mod) {
    long long result = 1;
    while (exp > 0) {
        if (exp % 2 == 1) result = (result * base) % mod;
        base = (base * base) % mod;
        exp /= 2;
    }
    return result;
}

// 求模反元素 (扩展欧几里得)
int mod_inverse(int e, int phi) {
    int t = 0, new_t = 1, r = phi, new_r = e;
    while (new_r != 0) {
        int quotient = r / new_r;
        int tmp = t; t = new_t; new_t = tmp - quotient * new_t;
        tmp = r; r = new_r; new_r = tmp - quotient * new_r;
    }
    return (t < 0) ? t + phi : t;
}

// 随机生成 e 并确保与 phi 互质
int generate_e(int phi) {
    int e;
    do { e = rand() % (phi - 2) + 2; } while (gcd(e, phi) != 1);
    return e;
}

// PKCS#1 v1.5 填充
void pkcs1_v1_5_pad(unsigned char* message, size_t mLen, unsigned char* padded, size_t k) {
    if (mLen > k - 11) {
        fprintf(stderr, "消息太长,无法填充\n");
        exit(EXIT_FAILURE);
    }
    padded[0] = 0x00;
    padded[1] = 0x02;
    for (size_t i = 2; i < k - mLen - 1; i++) {
        do { padded[i] = rand() % 256; } while (padded[i] == 0);
    }
    padded[k - mLen - 1] = 0x00;
    memcpy(&padded[k - mLen], message, mLen);
}

// PKCS#1 v1.5 解填充
void pkcs1_v1_5_unpad(unsigned char* padded, size_t k, unsigned char* message, size_t* mLen) {
    if (padded[0] != 0x00 || padded[1] != 0x02) {
        fprintf(stderr, "解填充失败,格式错误\n");
        exit(EXIT_FAILURE);
    }

    size_t start = 2;
    while (start < k && padded[start] != 0x00) start++;
    if (start == k) {
        fprintf(stderr, "解填充失败,未找到结束标志\n");
        exit(EXIT_FAILURE);
    }

    *mLen = k - start - 1;
    memcpy(message, &padded[start + 1], *mLen);
}

int main() {
    srand(time(NULL));

    // 1. 输入文本
    char s[128];
    printf("请输入要加密的文本: ");
    fgets(s, sizeof(s), stdin);
    s[strcspn(s, "\n")] = '\0';
    int s_len = strlen(s);

    // 动态内存分配
    int key_size = 64;  // 模数位数(单位:字节)
    unsigned char* padded_message = (unsigned char*)malloc(key_size);
    long long* encrypted = (long long*)malloc(key_size * sizeof(long long));
    if (!padded_message || !encrypted) {
        fprintf(stderr, "内存分配失败\n");
        return -1;
    }

    // 2. 生成大素数 p 和 q
    int p = generate_large_prime(10, 10);
    int q = generate_large_prime(10, 10);

    printf("生成的素数 p = %d, q = %d\n", p, q);
    int n = p * q, phi = (p - 1) * (q - 1);

    // 随机生成 e 并计算 d
    int e = generate_e(phi);
    int d = mod_inverse(e, phi);
    printf("生成的公钥指数 e = %d\n", e);

    printf("公钥: (e = %d, n = %d)\n", e, n);
    printf("私钥: (d = %d, n = %d)\n", d, n);

    // 3. 填充并加密
    pkcs1_v1_5_pad((unsigned char*)s, s_len, padded_message, key_size);

    printf("加密结果: ");
    for (int i = 0; i < key_size; i++) {
        encrypted[i] = mod_exp(padded_message[i], e, n);
        printf("%lld ", encrypted[i]);
    }
    printf("\n");

    // 4. 解密并去除填充
    printf("解密结果: ");
    unsigned char* decrypted_message = (unsigned char*)malloc(key_size);
    size_t decrypted_len;
    for (int i = 0; i < key_size; i++) {
        decrypted_message[i] = mod_exp(encrypted[i], d, n);
    }

    // 解填充
    pkcs1_v1_5_unpad(decrypted_message, key_size, (unsigned char*)s, &decrypted_len);

    printf("%s\n", (char*)s);

    // 释放内存
    free(padded_message);
    free(encrypted);
    free(decrypted_message);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值