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
相关产品推荐
相关产品推荐

