RSA-4096 密钥在 cryptography 和 GMP 库之间的转换

在 cryptography (Python) 和 GMP (C语言) 之间转换 RSA-4096 密钥需要注意两者的密钥表示格式差异。以下是详细的转换方法:

密钥格式对比

特性

cryptography (Python)

GMP (C语言)

私钥格式

PKCS#8 PEM/DER

自定义格式 (通常为原始大整数)

公钥格式

X.509 SubjectPublicKeyInfo PEM/DER

自定义格式 (通常为 (e, n) 对)

典型文件扩展名

.pem, .der

自定义 (常为二进制或文本数字)

方法 1,以原始整数形式在 GMP 和 Cryptography 之间转换

(1)在 python 中,从 cryptography 导出供 GMP 使用的密钥

from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend

def export_for_gmp(private_key):
    # 获取RSA参数
    private_numbers = private_key.private_numbers()
    public_numbers = private_key.public_key().public_numbers()

    return {
        'n': public_numbers.n,  # 模数
        'e': public_numbers.e,  # 公钥指数
        'd': private_numbers.d,  # 私钥指数
        'p': private_numbers.p,  # 第一个素数
        'q': private_numbers.q,  # 第二个素数
        'dmp1': private_numbers.dmp1,  # d mod (p-1)
        'dmq1': private_numbers.dmq1,  # d mod (q-1)
        'iqmp': private_numbers.iqmp  # q^-1 mod p
    }

def generate_rsa_keypair():
    """生成RSA-4096密钥对"""
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=4096,  # 使用4096位密钥
        backend=default_backend()
    )
    public_key = private_key.public_key()
    return private_key, public_key

# 1. 生成密钥对
private_key, public_key = generate_rsa_keypair()
params = export_for_gmp(private_key)

# 将参数写入文件供C程序使用
with open('rsa_params.txt', 'w') as f:
    f.write(f"n={params['n']}\n")
    f.write(f"e={params['e']}\n")
    f.write(f"d={params['d']}\n")
    f.write(f"p={params['p']}\n")
    f.write(f"q={params['q']}\n")
    f.write(f"dmp1={params['dmp1']}\n")
    f.write(f"dmq1={params['dmq1']}\n")
    f.write(f"iqmp={params['iqmp']}\n")

(2)在C语言(GMP)中加载这些参数

#include <gmp.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    mpz_t n;    // 模数
    mpz_t e;    // 公钥指数
    mpz_t d;    // 私钥指数
    mpz_t p;    // 第一个素数
    mpz_t q;    // 第二个素数
    mpz_t dmp1; // d mod (p-1)
    mpz_t dmq1; // d mod (q-1)
    mpz_t iqmp; // q^-1 mod p
} RSAKey;

void load_rsa_key(RSAKey *key, const char *filename) {
    FILE *file = fopen(filename, "r");
    if (!file) {
        perror("无法打开文件");
        exit(1);
    }

    // 初始化所有mpz_t变量
    mpz_inits(key->n, key->e, key->d, key->p, key->q, 
              key->dmp1, key->dmq1, key->iqmp, NULL);

    char line[1024];
    while (fgets(line, sizeof(line), file)) {
        if (sscanf(line, "n=%Zd", key->n) == 1) continue;
        if (sscanf(line, "e=%Zd", key->e) == 1) continue;
        if (sscanf(line, "d=%Zd", key->d) == 1) continue;
        if (sscanf(line, "p=%Zd", key->p) == 1) continue;
        if (sscanf(line, "q=%Zd", key->q) == 1) continue;
        if (sscanf(line, "dmp1=%Zd", key->dmp1) == 1) continue;
        if (sscanf(line, "dmq1=%Zd", key->dmq1) == 1) continue;
        if (sscanf(line, "iqmp=%Zd", key->iqmp) == 1) continue;
    }

    fclose(file);
}

// 使用示例
int main() {
    RSAKey key;
    load_rsa_key(&key, "rsa_params.txt");

    // 现在可以使用这些参数进行加密/解密操作
    // ...

    // 清理
    mpz_clears(key.n, key.e, key.d, key.p, key.q, 
               key.dmp1, key.dmq1, key.iqmp, NULL);
    return 0;
}

方法 2. 直接PEM格式转换

(1)python 中导出 PEM 格式文件

from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend

def generate_rsa_keypair():
    """生成RSA-4096密钥对"""
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=4096,  # 使用4096位密钥
        backend=default_backend()
    )
    public_key = private_key.public_key()
    return private_key, public_key

def export_keypair_pem(private_key, public_key, private_file, public_file):
    """打印PEM格式的密钥"""
    # 序列化私钥(PKCS8格式)
    pem_private = private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    )

    # 序列化公钥
    pem_public = public_key.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo
    )

    print("=== RSA-4096 私钥 ===")
    print(pem_private.decode('utf-8'))

    print("=== RSA-4096 公钥 ===")
    print(pem_public.decode('utf-8'))

    with open(private_file, 'w') as f:
        f.write(pem_private.decode('utf-8'))

    with open(public_file, 'w') as f:
        f.write(pem_public.decode('utf-8'))
    return

if __name__ == "__main__":
    # 1. 生成密钥对
    private_key, public_key = generate_rsa_keypair()

    export_keypair_pem(private_key, public_key, "pem_private_key.txt", "pem_public_key.txt")

(2)C 语言中加载并解析 PEM 格式文件方式 1

依赖库OpenSSL(用于解析PEM格式文件)和GMP(用于大整数运算)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gmp.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/err.h>

// 初始化GMP大整数并从BIGNUM转换
void bn_to_mpz(const BIGNUM *bn, mpz_t mpz)
{
    int size = BN_num_bytes(bn);
    unsigned char *buf = malloc(size);
    BN_bn2bin(bn, buf);
    mpz_import(mpz, size, 1, 1, 1, 0, buf);
    free(buf);
}

// 加载RSA私钥PEM文件并转换为GMP格式
int load_rsa_private_key(const char *filename, mpz_t n, mpz_t e, mpz_t d)
{
    FILE *fp = fopen(filename, "r");
    if (!fp)
    {
        fprintf(stderr, "无法打开文件: %s\n", filename);
        return 0;
    }

    RSA *rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
    fclose(fp);

    if (!rsa)
    {
        fprintf(stderr, "解析PEM文件失败:\n");
        ERR_print_errors_fp(stderr);
        return 0;
    }

    // 获取RSA密钥参数
    const BIGNUM *rsa_n, *rsa_e, *rsa_d;
    RSA_get0_key(rsa, &rsa_n, &rsa_e, &rsa_d);

    // 转换为GMP格式
    bn_to_mpz(rsa_n, n);
    bn_to_mpz(rsa_e, e);
    bn_to_mpz(rsa_d, d);

    RSA_free(rsa);
    return 1;
}

// 加载RSA公钥PEM文件并转换为GMP格式
int load_rsa_public_key(const char *filename, mpz_t n, mpz_t e)
{
    FILE *fp = fopen(filename, "r");
    if (!fp)
    {
        fprintf(stderr, "无法打开文件: %s\n", filename);
        return 0;
    }

    RSA *rsa = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
    fclose(fp);

    if (!rsa)
    {
        fprintf(stderr, "解析PEM文件失败:\n");
        ERR_print_errors_fp(stderr);
        return 0;
    }

    // 获取RSA公钥参数
    const BIGNUM *rsa_n, *rsa_e;
    RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);

    // 转换为GMP格式
    bn_to_mpz(rsa_n, n);
    bn_to_mpz(rsa_e, e);

    RSA_free(rsa);
    return 1;
}

int main()
{
    // 初始化GMP变量
    mpz_t n, e, d;
    mpz_inits(n, e, d, NULL);

    // 加载私钥
    if (load_rsa_private_key("pem_private_key.txt", n, e, d))
    {
        gmp_printf("私钥参数:\n");
        gmp_printf("模数 n (%d位):\n%Zd\n", mpz_sizeinbase(n, 2), n);
        gmp_printf("公钥指数 e: %Zd\n", e);
        gmp_printf("私钥指数 d: %Zd\n", d);
    }

    // 加载公钥
    mpz_t pub_n, pub_e;
    mpz_inits(pub_n, pub_e, NULL);
    if (load_rsa_public_key("pem_public_key.txt", pub_n, pub_e))
    {
        gmp_printf("\n公钥参数:\n");
        gmp_printf("模数 n (%d位):\n%Zd\n", mpz_sizeinbase(pub_n, 2), pub_n);
        gmp_printf("公钥指数 e: %Zd\n", pub_e);
    }

    // 验证n是否一致
    if (mpz_cmp(n, pub_n) == 0)
    {
        printf("\n验证: 公私钥的模数n一致\n");
    }
    else
    {
        printf("\n警告: 公私钥的模数n不一致!\n");
    }

    // 清理资源
    mpz_clears(n, e, d, pub_n, pub_e, NULL);
    return 0;
}
编译指令

gcc -o pem_to_gmp pem_to_gmp.c -lgmp -lcrypto

代码说明

主要函数bn_to_mpz():将OpenSSL的BIGNUM转换为GMP的mpz_t。

load_rsa_private_key():加载RSA私钥PEM文件。

load_rsa_public_key():加载RSA公钥PEM文件

错误处理:使用OpenSSL的ERR_print_errors_fp()输出详细错误信息。检查所有可能的失败情况

内存管理:正确初始化和清理GMP变量,释放所有分配的内存

注意事项

1, 确保PEM文件格式正确,支持以下格式:

  • 传统RSA私钥格式(BEGIN RSA PRIVATE KEY)
  • PKCS#8私钥格式(BEGIN PRIVATE KEY)
  • 标准公钥格式(BEGIN PUBLIC KEY)

2, 如果遇到解析错误,可以使用OpenSSL命令检查PEM文件:

openssl rsa -in private.pem -text -noout

3, 对于生产环境,应考虑:

  • 增加更严格的错误检查
  • 保护私钥数据在内存中的安全
  • 添加日志记录机制

(3)C 语言中加载并解析 PEM 格式文件方式

不依赖 openssh 的方案。

登录 · 语雀 《C语言解析PEM格式PKCS#8私钥的实现,不依赖OpenSSL。》

如果GMP端支持PEM格式:

cryptography → GMP

  1. 直接从cryptography导出PEM文件
  2. 在C程序中使用OpenSSL等库解析PEM

GMP → cryptography

  1. 使用OpenSSL在C端生成PEM文件
  2. 在Python中用cryptography直接加载PEM

注意事项

  1. 大整数处理:确保两端的整数表示一致,GMP和Python都能处理超大整数
  2. 字节序:跨平台时注意字节序问题
  3. 性能考虑:RSA-4096密钥转换可能涉及非常大的数字,处理时注意内存
  4. 安全存储:转换过程中密钥材料可能临时存储在文件中,确保安全删除

这种文本参数交换的方法虽然简单,但在生产环境中建议使用更安全的密钥交换机制,如PKCS#12或加密的密钥交换协议。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值