C++中使用X509证书(.crt)的RSA公钥加密遇PEM读取错误求助
解决从X509证书提取RSA公钥并加密消息的问题
首先得说清你踩的坑:你遇到的 error:0906D06C:PEM routines:PEM_read_bio:no start line,核心原因是用错了函数——PEM_read_bio_RSA_PUBKEY 是用来读取独立的PEM格式公钥文件的,但你手里的是X509证书文件,它的开头是 -----BEGIN CERTIFICATE-----,和独立公钥的格式完全不一样,自然读不出来。正确的做法是先从证书里提取公钥对象,再转成RSA结构体,之后再做加密。
下面是具体的实现步骤(用C语言+OpenSSL举例):
1. 加载X509证书文件
先把.crt文件加载成OpenSSL的X509结构体:
#include <openssl/x509.h> #include <openssl/pem.h> #include <openssl/rsa.h> #include <openssl/err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // 加载证书文件到X509结构体 X509* load_x509_cert(const char* cert_path) { FILE* cert_file = fopen(cert_path, "r"); if (!cert_file) { perror("Failed to open certificate file"); return NULL; } // 读取PEM格式的X509证书 X509* cert = PEM_read_X509(cert_file, NULL, NULL, NULL); fclose(cert_file); if (!cert) { fprintf(stderr, "Failed to parse certificate: "); ERR_print_errors_fp(stderr); return NULL; } return cert; }
2. 从证书中提取RSA公钥
证书里的公钥是EVP_PKEY通用类型,需要先取出,再转换成RSA专用结构体:
// 从X509证书中提取RSA公钥 RSA* extract_rsa_pubkey(X509* cert) { // 从证书中取出公钥 EVP_PKEY* pkey = X509_get_pubkey(cert); if (!pkey) { fprintf(stderr, "Failed to get public key from cert: "); ERR_print_errors_fp(stderr); return NULL; } // 验证公钥是否为RSA类型 if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) { fprintf(stderr, "Certificate's public key is not RSA type!\n"); EVP_PKEY_free(pkey); return NULL; } // 转换成RSA结构体 RSA* rsa_pub = EVP_PKEY_get1_RSA(pkey); EVP_PKEY_free(pkey); // 释放通用公钥对象,RSA结构体单独管理 if (!rsa_pub) { fprintf(stderr, "Failed to convert EVP_PKEY to RSA: "); ERR_print_errors_fp(stderr); } return rsa_pub; }
3. 用RSA公钥加密消息
接下来就可以用标准的RSA加密函数处理消息了,注意RSA加密的明文长度有限制(一般是密钥长度-11字节,因为用PKCS#1 v1.5填充):
// RSA公钥加密(PKCS#1 v1.5填充) int rsa_public_encrypt_data(RSA* rsa_pub, const unsigned char* plaintext, int plaintext_len, unsigned char* ciphertext) { int rsa_key_size = RSA_size(rsa_pub); // 检查明文长度是否超过RSA加密上限 if (plaintext_len > rsa_key_size - 11) { fprintf(stderr, "Plaintext is too long for RSA encryption! Max length: %d\n", rsa_key_size - 11); return -1; } // 执行加密 int cipher_len = RSA_public_encrypt(plaintext_len, plaintext, ciphertext, rsa_pub, RSA_PKCS1_PADDING); if (cipher_len == -1) { fprintf(stderr, "RSA encryption failed: "); ERR_print_errors_fp(stderr); } return cipher_len; }
4. 资源清理(重要)
用完OpenSSL的结构体后一定要释放,避免内存泄漏:
// 清理所有资源 void cleanup_resources(X509* cert, RSA* rsa_pub) { if (cert) X509_free(cert); if (rsa_pub) RSA_free(rsa_pub); ERR_free_strings(); }
完整调用示例
int main() { const char* cert_path = "your_certificate.crt"; const char* plaintext = "This is the message to encrypt with RSA public key"; int plaintext_len = strlen(plaintext); // 初始化OpenSSL(旧版本需要这两步,新版本可省略) OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); // 加载证书 X509* cert = load_x509_cert(cert_path); if (!cert) return 1; // 提取RSA公钥 RSA* rsa_pub = extract_rsa_pubkey(cert); if (!rsa_pub) { cleanup_resources(cert, NULL); return 1; } // 分配密文缓冲区 int rsa_key_size = RSA_size(rsa_pub); unsigned char* ciphertext = malloc(rsa_key_size); if (!ciphertext) { perror("Failed to allocate ciphertext buffer"); cleanup_resources(cert, rsa_pub); return 1; } // 执行加密 int cipher_len = rsa_public_encrypt_data(rsa_pub, (unsigned char*)plaintext, plaintext_len, ciphertext); if (cipher_len == -1) { free(ciphertext); cleanup_resources(cert, rsa_pub); return 1; } // 这里可以处理密文,比如打印长度或保存到文件 printf("Encryption success! Ciphertext length: %d bytes\n", cipher_len); // 清理资源 free(ciphertext); cleanup_resources(cert, rsa_pub); return 0; }
关键注意事项
- 绝对不要用
PEM_read_bio_RSA_PUBKEY直接读证书文件,这个函数只认-----BEGIN PUBLIC KEY-----开头的独立公钥文件。 - 每个OpenSSL函数的返回值都要检查,出错时用
ERR_print_errors_fp(stderr)打印详细错误信息,能帮你快速定位问题。 - RSA不适合加密大文件,建议先用AES等对称加密算法加密文件,再用RSA加密对称密钥,这是业界通用的混合加密方案。
内容的提问来源于stack exchange,提问作者Дима Менько
相关产品推荐
相关产品推荐

