密钥自己生成的方法

两人一组,在Ubuntu或openEuler中(推荐openEuler)中使用OpenSSL编程实现带签名的数字信封协议。使用OpenSSL库时,Alice发送,Bob接收。Ailice,Bob在实验中要替换为自己的8位学号+姓名。使用Markdown记录详细记录实践过程,每完成一项gitcommit一次。

一、实验环境准备

  1. 安装 OpenSSL 开发库
  2. 创建实验目录与 Git 仓库

二、生成密钥与证书(模拟 Alice、Bob 身份)

数字信封需要非对称加密(RSA)保护对称密钥,哈希与签名保证完整性。需为 Alice、Bob 生成 RSA 密钥对和自签名证书。

1. 生成 Alice 的 RSA 密钥对与证书

# 生成 RSA 私钥(2048 位)
openssl genrsa -out alice_private.key 2048

# 从私钥提取公钥
openssl rsa -in alice_private.key -pubout -out alice_public.key

# 生成自签名证书(需填写信息,如 Common Name 填“20231313zjy+alice”)
openssl req -new -x509 -key alice_private.key -out alice_cert.pem -days 365

alt text

2. 生成 Bob 的 RSA 密钥对与证书

# 生成 RSA 私钥
openssl genrsa -out bob_private.key 2048

# 提取公钥
openssl rsa -in bob_private.key -pubout -out bob_public.key

# 生成自签名证书(Common Name 填“学号+Bob”)
openssl req -new -x509 -key bob_private.key -out bob_cert.pem -days 365

三、代码编写(C 语言,依赖 OpenSSL 库)

数字信封流程:

  1. Alice:

    • 生成随机对称密钥(AES),用其加密明文 → 得到“密文”。
    • 用 Bob 的公钥加密对称密钥 → 得到“数字信封”。
    • 用 Alice 的私钥对明文哈希值签名 → 得到“签名”。
    • 将「密文 + 数字信封 + 签名」发送给 Bob。
  2. Bob:

    • 用 Bob 的私钥解密“数字信封” → 得到对称密钥。
    • 用对称密钥解密密文 → 得到明文。
    • 用 Alice 的公钥验证“签名” → 确保数据来自 Alice 且未被篡改。

1. Alice 发送端代码(alice.c

#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/err.h>

#define AES_KEY_SIZE 256    // AES-256
#define AES_IV_SIZE 16      // CBC模式IV长度
#define BUFFER_SIZE 1024

void print_openssl_error(const char *msg) {
    fprintf(stderr, "%s\n", msg);
    ERR_print_errors_fp(stderr);
}

int main() {
    ERR_load_crypto_strings();

    // 1. 明文数据
    unsigned char plaintext[] = "Hello Bob! This is a secret from Alice.";
    printf("Alice: 原始明文: %s\n", plaintext);

    // 2. 生成AES密钥和IV
    unsigned char aes_key[AES_KEY_SIZE / 8];
    unsigned char aes_iv[AES_IV_SIZE];
    if (RAND_bytes(aes_key, sizeof(aes_key)) != 1 || RAND_bytes(aes_iv, sizeof(aes_iv)) != 1) {
        print_openssl_error("Alice: 生成AES密钥/IV失败");
        return 1;
    }

    // 3. AES-CBC加密明文
    unsigned char ciphertext[BUFFER_SIZE];
    int ciphertext_len = 0;
    EVP_CIPHER_CTX *aes_ctx = EVP_CIPHER_CTX_new();
    if (!aes_ctx) { print_openssl_error("Alice: 创建AES ctx失败"); return 1; }
    if (EVP_EncryptInit_ex(aes_ctx, EVP_aes_256_cbc(), NULL, aes_key, aes_iv) != 1) {
        print_openssl_error("Alice: EVP_EncryptInit_ex 失败");
        return 1;
    }
    if (EVP_EncryptUpdate(aes_ctx, ciphertext, &ciphertext_len, plaintext, (int)strlen((char*)plaintext)) != 1) {
        print_openssl_error("Alice: EVP_EncryptUpdate 失败");
        return 1;
    }
    int temp_len = 0;
    if (EVP_EncryptFinal_ex(aes_ctx, ciphertext + ciphertext_len, &temp_len) != 1) {
        print_openssl_error("Alice: EVP_EncryptFinal_ex 失败");
        return 1;
    }
    ciphertext_len += temp_len;
    EVP_CIPHER_CTX_free(aes_ctx);

    // 4. 用Bob公钥加密AES密钥(数字信封)
    FILE *bob_pub_file = fopen("bob_public.key", "r");
    if (!bob_pub_file) { printf("Alice: 找不到bob_public.key\n"); return 1; }
    EVP_PKEY *bob_pubkey = PEM_read_PUBKEY(bob_pub_file, NULL, NULL, NULL);
    fclose(bob_pub_file);
    if (!bob_pubkey) { print_openssl_error("Alice: 读取Bob公钥失败"); return 1; }

    unsigned char envelope[BUFFER_SIZE];
    size_t envelope_len = sizeof(envelope); // **重要:初始化为缓冲区最大长度**
    EVP_PKEY_CTX *rsa_ctx = EVP_PKEY_CTX_new(bob_pubkey, NULL);
    if (!rsa_ctx) { print_openssl_error("Alice: 创建RSA ctx失败"); EVP_PKEY_free(bob_pubkey); return 1; }
    if (EVP_PKEY_encrypt_init(rsa_ctx) <= 0) { print_openssl_error("Alice: EVP_PKEY_encrypt_init 失败"); EVP_PKEY_free(bob_pubkey); EVP_PKEY_CTX_free(rsa_ctx); return 1; }
    if (EVP_PKEY_CTX_set_rsa_padding(rsa_ctx, RSA_PKCS1_PADDING) <= 0) { print_openssl_error("Alice: 设置RSA填充失败"); }
    if (EVP_PKEY_encrypt(rsa_ctx, envelope, &envelope_len, aes_key, sizeof(aes_key)) <= 0) {
        print_openssl_error("Alice: EVP_PKEY_encrypt 失败");
        EVP_PKEY_CTX_free(rsa_ctx);
        EVP_PKEY_free(bob_pubkey);
        return 1;
    }
    EVP_PKEY_CTX_free(rsa_ctx);
    EVP_PKEY_free(bob_pubkey);

    // 5. 用Alice私钥签名明文
    FILE *alice_priv_file = fopen("alice_private.key", "r");
    if (!alice_priv_file) { printf("Alice: 找不到alice_private.key\n"); return 1; }
    EVP_PKEY *alice_privkey = PEM_read_PrivateKey(alice_priv_file, NULL, NULL, NULL);
    fclose(alice_priv_file);
    if (!alice_privkey) { print_openssl_error("Alice: 读取Alice私钥失败"); return 1; }

    unsigned char signature[BUFFER_SIZE];
    size_t signature_len = sizeof(signature); // **重要:初始化为缓冲区最大长度**
    EVP_MD_CTX *sign_ctx = EVP_MD_CTX_new();
    if (!sign_ctx) { print_openssl_error("Alice: 创建签名ctx失败"); EVP_PKEY_free(alice_privkey); return 1; }
    if (EVP_DigestSignInit(sign_ctx, NULL, EVP_sha256(), NULL, alice_privkey) != 1) {
        print_openssl_error("Alice: EVP_DigestSignInit 失败");
        EVP_MD_CTX_free(sign_ctx); EVP_PKEY_free(alice_privkey); return 1;
    }
    if (EVP_DigestSignUpdate(sign_ctx, plaintext, strlen((char*)plaintext)) != 1) {
        print_openssl_error("Alice: EVP_DigestSignUpdate 失败");
        EVP_MD_CTX_free(sign_ctx); EVP_PKEY_free(alice_privkey); return 1;
    }
    if (EVP_DigestSignFinal(sign_ctx, signature, &signature_len) != 1) {
        print_openssl_error("Alice: EVP_DigestSignFinal 失败");
        EVP_MD_CTX_free(sign_ctx); EVP_PKEY_free(alice_privkey); return 1;
    }
    EVP_MD_CTX_free(sign_ctx);
    EVP_PKEY_free(alice_privkey);

    // 6. 写入消息文件(带长度信息,确保Bob正确解析)
    FILE *out = fopen("message_to_bob", "wb");
    if (!out) { perror("Alice: 打开输出文件失败"); return 1; }

    // 写入IV长度+IV
    int iv_len = AES_IV_SIZE;
    fwrite(&iv_len, sizeof(int), 1, out);
    fwrite(aes_iv, 1, iv_len, out);
    // 写入密文长度+密文
    fwrite(&ciphertext_len, sizeof(int), 1, out);
    fwrite(ciphertext, 1, ciphertext_len, out);
    // 写入信封长度+信封
    fwrite(&envelope_len, sizeof(size_t), 1, out);
    fwrite(envelope, 1, envelope_len, out);
    // 写入签名长度+签名
    fwrite(&signature_len, sizeof(size_t), 1, out);
    fwrite(signature, 1, signature_len, out);
    fclose(out);

    printf("Alice: 消息已生成(message_to_bob)\n");
    ERR_free_strings();
    return 0;
}

保存为 alice.c

2. Bob 接收端代码(bob.c

#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/pem.h>

#define BUFFER_SIZE 1024

int main() {
    FILE *in = fopen("message_to_bob", "rb");
    if (!in) { printf("Bob: 找不到 message_to_bob\n"); return 1; }

    int iv_len;
    fread(&iv_len, sizeof(int), 1, in);
    unsigned char aes_iv[iv_len];
    fread(aes_iv, 1, iv_len, in);

    int ciphertext_len;
    fread(&ciphertext_len, sizeof(int), 1, in);
    unsigned char ciphertext[ciphertext_len];
    fread(ciphertext, 1, ciphertext_len, in);

    size_t envelope_len;
    fread(&envelope_len, sizeof(size_t), 1, in);
    unsigned char envelope[envelope_len];
    fread(envelope, 1, envelope_len, in);

    size_t signature_len;
    fread(&signature_len, sizeof(size_t), 1, in);
    unsigned char signature[signature_len];
    fread(signature, 1, signature_len, in);
    fclose(in);

    // 1. Bob 用私钥解密 AES 密钥
    FILE *bob_priv_file = fopen("bob_private.key", "r");
    if (!bob_priv_file) { printf("Bob: 找不到 bob_private.key\n"); return 1; }
    EVP_PKEY *bob_privkey = PEM_read_PrivateKey(bob_priv_file, NULL, NULL, NULL);
    fclose(bob_priv_file);

    EVP_PKEY_CTX *rsa_ctx = EVP_PKEY_CTX_new(bob_privkey, NULL);
    EVP_PKEY_decrypt_init(rsa_ctx);
    EVP_PKEY_CTX_set_rsa_padding(rsa_ctx, RSA_PKCS1_PADDING);

    size_t aes_key_len = 0;
    EVP_PKEY_decrypt(rsa_ctx, NULL, &aes_key_len, envelope, envelope_len);
    unsigned char aes_key[aes_key_len];
    if (EVP_PKEY_decrypt(rsa_ctx, aes_key, &aes_key_len, envelope, envelope_len) != 1) {
        printf("Bob: RSA解密失败(密钥不匹配或数据损坏)\n");
        return 1;
    }
    EVP_PKEY_CTX_free(rsa_ctx);
    EVP_PKEY_free(bob_privkey);

    // 2. AES 解密密文
    unsigned char plaintext[BUFFER_SIZE] = {0};
    int plaintext_len = 0, len;
    EVP_CIPHER_CTX *aes_ctx = EVP_CIPHER_CTX_new();
    EVP_DecryptInit_ex(aes_ctx, EVP_aes_256_cbc(), NULL, aes_key, aes_iv);
    EVP_DecryptUpdate(aes_ctx, plaintext, &plaintext_len, ciphertext, ciphertext_len);
    EVP_DecryptFinal_ex(aes_ctx, plaintext + plaintext_len, &len);
    plaintext_len += len;
    EVP_CIPHER_CTX_free(aes_ctx);
    plaintext[plaintext_len] = '\0';

    // 3. 验证签名
    FILE *alice_pub_file = fopen("alice_public.key", "r");
    if (!alice_pub_file) { printf("Bob: 找不到 alice_public.key\n"); return 1; }
    EVP_PKEY *alice_pubkey = PEM_read_PUBKEY(alice_pub_file, NULL, NULL, NULL);
    fclose(alice_pub_file);

    EVP_MD_CTX *verify_ctx = EVP_MD_CTX_new();
    EVP_DigestVerifyInit(verify_ctx, NULL, EVP_sha256(), NULL, alice_pubkey);
    EVP_DigestVerifyUpdate(verify_ctx, plaintext, plaintext_len);
    int verify_result = EVP_DigestVerifyFinal(verify_ctx, signature, signature_len);
    EVP_MD_CTX_free(verify_ctx);
    EVP_PKEY_free(alice_pubkey);

    printf("Bob: 解密明文: %s\n", plaintext);
    if (verify_result == 1)
        printf("Bob: 签名验证成功(数据完整且来自 Alice)\n");
    else
        printf("Bob: 签名验证失败\n");

    return 0;
}

保存为 bob.c

四、编译与运行

1. 编译 Alice 代码

gcc -o alice alice.c -lcrypto

2. 运行 Alice 生成“数字信封”

./alice

3. 编译 Bob 代码

gcc -o bob bob.c -lcrypto

4. 运行 Bob 解密与验证

./bob

五、实验结果验证

运行 ./bob 后,终端应输出:

  • 解密后的明文:Hello Bob! This is a secret message from 20231313zjy.已经更改为学号和姓名
  • 签名验证结果:成功(数据完整且来自Alice)
posted @ 2025-10-19 14:34  Raymongillichmks  阅读(22)  评论(0)    收藏  举报