You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PEM_read_bio_PUBKEY返回nullptr问题排查求助

问题:PEM_read_bio_PUBKEY返回nullptr的排查

调用EVP_PKEY * pk = PEM_read_bio_PUBKEY(bioReadPublic, nullptr, nullptr, nullptr);时返回nullptr,但使用网上示例的公钥可正常运行,私钥也能正常使用,输出的公钥格式看似正常,怀疑问题出在密钥生成环节。

完整代码

#include <iostream>
#include <iostream>

#include <openssl/bio.h>
#include <openssl/pem.h>

using std::cout;
using std::cerr;
using std::endl;
using std::string;

int main()
{
    ::RSA * rsa = nullptr;
    ::BIGNUM * bne = nullptr;
    ::BIO * bioPrivate = nullptr;
    ::BIO * bioPublic = nullptr;
    ::BIO * bioReadPublic = nullptr;
    ::BIO * bioReadPrivate = nullptr;
    ::RSA * privateRSA = nullptr;
    ::RSA * publicRSA = nullptr;

    int bits = 2048;

    try {
        // 生成公私钥
        rsa = RSA_new();
        bne = BN_new();
        BN_set_word(bne, RSA_F4);
        RSA_generate_key_ex(rsa, bits, bne, NULL);

        bioPrivate = BIO_new( BIO_s_mem() );
        bioPublic = BIO_new( BIO_s_mem() );
        PEM_write_bio_RSAPrivateKey( bioPrivate, rsa, nullptr, nullptr, 0, nullptr, nullptr );
        PEM_write_bio_RSAPublicKey( bioPublic, rsa );

        size_t privateLen = BIO_pending( bioPrivate );
        size_t publicLen = BIO_pending( bioPublic );

        // 读取密钥到缓冲区
        char privateBuffer[10000];
        char publicBuffer[10000];

        BIO_read(bioPrivate, privateBuffer, privateLen);
        BIO_read(bioPublic, publicBuffer, publicLen);

        privateBuffer[privateLen] = 0;
        publicBuffer[publicLen] = 0;

        string privateKey = privateBuffer;
        string publicKey = publicBuffer;

        cout << "Keys:\n"
             << privateKey << endl << endl
             << publicKey << endl << endl;

        // 尝试读取密钥
        cout << "Try PEM_read\n";
        bioReadPrivate = BIO_new_mem_buf( (void *)privateBuffer, privateLen );
        bioReadPublic = BIO_new_mem_buf( (void *)publicBuffer, publicLen );

        cout << "The private...\n";
        privateRSA = PEM_read_bio_RSAPrivateKey(bioReadPrivate, nullptr, nullptr, nullptr);
        if (privateRSA == nullptr) {
            cout << "Failed to read private key\n";
        }

        cout << "The public...\n";
        publicRSA = PEM_read_bio_RSAPublicKey(bioReadPublic, nullptr, nullptr, nullptr);
        if (publicRSA == nullptr) {
            cout << "Failed to read public key\n";
        }

        EVP_PKEY * pk = PEM_read_bio_PUBKEY(bioReadPublic, nullptr, nullptr, nullptr);
        if (pk == nullptr) {
            cout << "PEM_read_bio_PUBKEY failed\n";
        }
    }
    catch (const std::exception &e) {
        cerr << "Exception: " << e.what() << endl;
    }

    cout << "Done.\n";

    // 释放资源
    if (privateRSA != nullptr) {
        RSA_free(privateRSA);
    }
    if (publicRSA != nullptr) {
        RSA_free(publicRSA);
    }
    if (bioReadPrivate != nullptr) {
        BIO_free(bioReadPrivate);
    }
    if (bioReadPublic != nullptr) {
        BIO_free(bioReadPublic);
    }
    if (bioPublic != nullptr) {
        BIO_free(bioPublic);
    }
    if (bioPrivate != nullptr) {
        BIO_free(bioPrivate);
    }
    if (bne != nullptr) {
        BN_free(bne);
    }
    if (rsa != nullptr) {
        RSA_free(rsa);
    }
    return 0;
}

运行输出

Keys:
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEArqd1UmR+VYPByNJujunS5FhlkSgsuxiSTjWJCdNoUkdIcKxJ
6ZvNV6uQAkJdL3RgHBRmhrb2CBTkkYdJoB0HyY58AgTH25eJpW7aRmHMh6exzrSv
47MkKVuHVfLNw/RjA3cTDMMgg5SHg4bS8O9lXa9A3YIAUtTb3f4xhuYX8ZG64SzB
lhCruCRKubb1N7eSuqLpq8DJqMj842PEwEU5HfQhxWHU4KAK43Wkglq6BjkKYULR
0/cUm8hMK5WE63LJ0mSsU4sD9UdzpbPD1WO+KBPLX6C0O/z6fGmEIwEyjNmXomUE
tqOBhW+1tEvF9T/l5UF/a+RyQs4uPjqsuiBfrQIDAQABAoIBAH6FqSQK0Mo6LIPC
jXzSPohU0R8ar2zKHBK0DZYkpAoWwY99//1PdK3yBtzcHo1mDMnKrYsht5GbMQmL
q/ZlkWKd6Y+8zttxJ7HEYDNkXbpX7SMFGfYlrCiZuay1vAIcZC36cL5qDH/FdXX+
kYkgo5n+f0r4Biv7L1vd7RsATJAeAla8wz+SQ5TVyONMQ4KYvQFk4GrIOtkdvVjW
B+EMxNPRFdLBpGL0cAwwEMS71H30R+NqOogRWpok/gE4LTe3tohoNMr9UvRrQucG
xLLUMLlQFe+t2iys1oRLRbIsPbp8+Jb0LkPnEuz+rG86nyg5UnTyI2KSkW5GO2sP
1l+xUDECgYEA55JVMSOeQWFBfh1MySbOsbceGT/0ZPmrOEj0n2Sab59iJv/9B/W0
SlZ+nmZzDq7Zxkefaaq2+1uaqHzPstqJUPAuCvJV4dJ1dMX3gAhVxMiIyPR4KywZ
Wq22r7hWOggwQ8nt7YLhbXX0NG9MepHUpbYniGBH04y/Ff4kV/yMkKcCgYEAwRQM
5KZa2GNjcqRH+G+/DyjSml8cyv/HyA/bp5hsRUZJdNnuHK0Dl07IED31d3r023Mz
vBgwqZ0DNnfNvqebrtkeQE2MBgXBU5r8lZ/Xc1uzC5ClYxvjAMvR/GW9XpatqvZa
+tw/DeQ08htwc/yvKAlJwnp3AQRmDQyVwG+hI4sCgYAc7MBLZQGNdRJzKEHBFmHW
/OJ7kOt7+VuTqjkz96L6slPWPz9h0ST17BvguTdB7EEzbRrSXgqBFw4YYY6n7ngc
bgOyRlbH4DvfnCKwEVp1O/8p6q9f5SA9nVkmVcYfdYphzUU015ZEz+jy67wHy1Ne
Z4REmpIeMIRlwNrVSqxcHQKBgF3DMOew6Zj/2wGHSfQZwDzKZEvXOJJUWF6NZOwk
lx+lHHMEih+e3YmTFpDcpeHZ8iLH9S24yZj0yOSglWeq2W21vn0Xq5IZJWNGdrbq
oqRAudg57DcPCdQPJvBdL/NJVRka/d+pKW3Djvqr3JZW9XCJ4inxcu0ph616mRaU
nzo1AoGAbSMgdhMRq1x78LhI4zHklyonA55T5eO3Tk+tyXF513e9LmVTgVg6sCsj
pU+4/gMHa3oDn+LU1LsxuJU+xn/zHVFTjIYaw8+dLzHv7mVz3DpdeAVud3BRuHNu
kj7X7ZsRRCtGv8762B5YOdbAlXStc6i2JHLyWI1n7s324hm1f3c=
-----END RSA PRIVATE KEY-----


-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEArqd1UmR+VYPByNJujunS5FhlkSgsuxiSTjWJCdNoUkdIcKxJ6ZvN
V6uQAkJdL3RgHBRmhrb2CBTkkYdJoB0HyY58AgTH25eJpW7aRmHMh6exzrSv47Mk
KVuHVfLNw/RjA3cTDMMgg5SHg4bS8O9lXa9A3YIAUtTb3f4xhuYX8ZG64SzBlhCr
uCRKubb1N7eSuqLpq8DJqMj842PEwEU5HfQhxWHU4KAK43Wkglq6BjkKYULR0/cU
m8hMK5WE63LJ0mSsU4sD9UdzpbPD1WO+KBPLX6C0O/z6fGmEIwEyjNmXomUEtqOB
hW+1tEvF9T/l5UF/a+RyQs4uPjqsuiBfrQIDAQAB
-----END RSA PUBLIC KEY-----


Try PEM_read
The private...
The public...
PEM_read_bio_PUBKEY failed
Done.
问题原因与解决方法

核心原因

你用PEM_write_bio_RSAPublicKey生成的是RSA专属格式的公钥(头部为-----BEGIN RSA PUBLIC KEY-----),而PEM_read_bio_PUBKEY只能读取PKCS#8标准的通用公钥(头部为-----BEGIN PUBLIC KEY-----),两者格式不兼容,导致读取失败。

而PEM_read_bio_RSAPublicKey能正常工作,因为它就是专门处理RSA专属公钥格式的接口。

解决方法

方法1:生成PKCS#8格式公钥

修改公钥生成代码,替换PEM_write_bio_RSAPublicKey为PEM_write_bio_PUBKEY,直接生成兼容的通用公钥:

// 替换原有的PEM_write_bio_RSAPublicKey(bioPublic, rsa);
EVP_PKEY* evpKey = EVP_PKEY_new();
if (!evpKey || !EVP_PKEY_assign_RSA(evpKey, rsa)) {
    // 处理错误
    cerr << "Failed to assign RSA key to EVP_PKEY" << endl;
    return 1;
}
PEM_write_bio_PUBKEY(bioPublic, evpKey);
EVP_PKEY_free(evpKey); // 释放EVP_PKEY,同时会自动释放关联的rsa
// 注意:这里不要再调用RSA_free(rsa),否则会重复释放

方法2:读取后转换格式

如果需要保留原RSA专属格式公钥,可以先通过PEM_read_bio_RSAPublicKey读取到RSA*,再转换为EVP_PKEY:

// 重置bio的读取位置,因为之前已经读过一次
BIO_reset(bioReadPublic);
publicRSA = PEM_read_bio_RSAPublicKey(bioReadPublic, nullptr, nullptr, nullptr);
if (publicRSA != nullptr) {
    EVP_PKEY* pk = EVP_PKEY_new();
    EVP_PKEY_assign_RSA(pk, publicRSA); // 接管publicRSA的所有权
    // 这里可以正常使用pk
    cout << "Successfully converted RSA key to EVP_PKEY" << endl;
    EVP_PKEY_free(pk); // 释放时会同时释放publicRSA
}

额外提示

  • 所有OpenSSL接口调用后都要检查返回值,出现错误时可以用ERR_print_errors_fp(stderr)打印详细错误信息,快速定位问题。
  • 严格匹配资源释放逻辑,避免内存泄漏或重复释放。

内容的提问来源于stack exchange,提问作者Joseph Larson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 02:48:26