使用OpenSSL生成RSA密钥对后读取失败,求正确实现方法
解决OpenSSL生成RSA密钥对后读取失败的问题
你遇到的error:0906D06C:PEM routines:PEM_read_bio:no start line错误,核心原因是密钥生成的格式和读取时使用的函数不匹配,再加上代码里有一处判断错误,导致读取失败。我来帮你拆解问题并修复代码:
问题分析
公钥格式不匹配:
- 你用
PEM_write_bio_RSAPublicKey生成的是PKCS#1格式的公钥,开头是-----BEGIN RSA PUBLIC KEY----- - 但读取时用的
PEM_read_bio_PUBKEY是用来读取PKCS#8格式的公钥,开头是-----BEGIN PUBLIC KEY-----
两种格式的PEM头部不同,所以读取时找不到起始行,触发错误。
- 你用
私钥读取判断错误:
代码里判断私钥读取失败的条件写错了:用了evp_pbkey == NULL(公钥的指针),而不是evp_pkey == NULL,导致无法正确检测私钥读取的问题。(可选)内存BIO指针位置:
写入密钥到内存BIO后,指针会停在末尾,虽然你用BIO_pending获取了长度,但有时候建议重置BIO指针到开头再读取,避免意外的读取问题。
修复方案(两种可选)
方案一:统一使用PKCS#1格式(保持生成函数,修改读取函数)
这种方案不需要改生成逻辑,只需要把读取公钥和私钥的函数换成对应PKCS#1格式的:
- 公钥读取用
PEM_read_bio_RSAPublicKey - 私钥读取用
PEM_read_bio_RSAPrivateKey
方案二:统一使用PKCS#8格式(修改生成函数,保持读取函数)
如果你更倾向于标准的PKCS#8格式,把生成函数换成:
- 公钥生成用
PEM_write_bio_PUBKEY - 私钥生成用
PEM_write_bio_PrivateKey
下面是用方案一修复后的完整代码,同时修正了判断错误和内存BIO的指针问题:
#include <stdio.h> #include <iostream> #include <openssl/rsa.h> #include <openssl/pem.h> #include <openssl/err.h> #include <exception> #include <stdlib.h> bool generate_key() { size_t pri_len; // Length of private key size_t pub_len; // Length of public key char *pri_key = nullptr; // Private key in PEM char *pub_key = nullptr; // Public key in PEM int ret = 0; RSA *r = NULL; BIGNUM *bne = NULL; BIO *bp_public = NULL, *bp_private = NULL; int bits = 2048; unsigned long e = RSA_F4; RSA *read_pub_rsa = NULL; // 用RSA结构体读取PKCS#1公钥 RSA *read_priv_rsa = NULL; // 用RSA结构体读取PKCS#1私钥 BIO *pbkeybio = NULL; BIO *pkeybio = NULL; // 1. generate rsa key bne = BN_new(); ret = BN_set_word(bne, e); if (ret != 1) { goto free_all; } r = RSA_new(); ret = RSA_generate_key_ex(r, bits, bne, NULL); if (ret != 1) { goto free_all; } // 2. save public key (PKCS#1格式) bp_public = BIO_new(BIO_s_mem()); ret = PEM_write_bio_RSAPublicKey(bp_public, r); if (ret != 1) { goto free_all; } // 3. save private key (PKCS#1格式) bp_private = BIO_new(BIO_s_mem()); ret = PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL); if (ret != 1) { goto free_all; } //4. Get the keys as PEM formatted strings // 重置BIO指针到开头,确保能读取到完整内容 BIO_reset(bp_public); BIO_reset(bp_private); pri_len = BIO_pending(bp_private); pub_len = BIO_pending(bp_public); pri_key = (char*) malloc(pri_len + 1); pub_key = (char*) malloc(pub_len + 1); if (!pri_key || !pub_key) { ret = 0; goto free_all; } BIO_read(bp_private, pri_key, pri_len); BIO_read(bp_public, pub_key, pub_len); pri_key[pri_len] = '\0'; pub_key[pub_len] = '\0'; printf("\nPrivate Key:\n%s\nPublic Key:\n%s\n", pri_key, pub_key); // 验证公钥读取(PKCS#1格式对应PEM_read_bio_RSAPublicKey) pbkeybio = BIO_new_mem_buf((void*) pub_key, -1); if (pbkeybio == NULL) { ret = 0; goto free_all; } read_pub_rsa = PEM_read_bio_RSAPublicKey(pbkeybio, &read_pub_rsa, NULL, NULL); if (read_pub_rsa == NULL) { char buffer[120]; ERR_error_string(ERR_get_error(), buffer); printf("Error reading public key:%s\n", buffer); ret = 0; } else { printf("Public key read successfully!\n"); } // 验证私钥读取(PKCS#1格式对应PEM_read_bio_RSAPrivateKey) pkeybio = BIO_new_mem_buf((void*) pri_key, -1); if (pkeybio == NULL) { ret = 0; goto free_all; } read_priv_rsa = PEM_read_bio_RSAPrivateKey(pkeybio, &read_priv_rsa, NULL, NULL); if (read_priv_rsa == NULL) { // 修正:这里用read_priv_rsa判断,不是evp_pbkey char buffer[120]; ERR_error_string(ERR_get_error(), buffer); printf("Error reading private key:%s\n", buffer); ret = 0; } else { printf("Private key read successfully!\n"); } // 4. free resources free_all: BIO_free(pbkeybio); BIO_free(pkeybio); BIO_free_all(bp_public); BIO_free_all(bp_private); RSA_free(r); RSA_free(read_pub_rsa); RSA_free(read_priv_rsa); BN_free(bne); free(pri_key); free(pub_key); return (ret == 1); } int main(int argc, char* argv[]) { // 初始化OpenSSL错误处理 ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); bool success = generate_key(); if (success) { printf("\nKey generation and verification completed successfully!\n"); } else { printf("\nOperation failed!\n"); } // 清理OpenSSL资源 EVP_cleanup(); ERR_free_strings(); return 0; }
额外注意事项
- 别忘了初始化OpenSSL的错误处理和算法库(代码里加了
ERR_load_crypto_strings()和OpenSSL_add_all_algorithms()),否则错误信息可能无法正常输出。 - 内存分配后要检查是否成功,避免空指针访问。
- 所有OpenSSL的结构体和BIO都要记得释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者sburnwal
相关产品推荐
相关产品推荐

