OpenSSL中EVP_PKEY_get_raw_public_key获取原始公钥失败求助
问题分析与解决方案
核心问题
EVP_PKEY_get_raw_public_key 并非适用于所有密钥类型,对于椭圆曲线(EC)密钥,该函数无法直接提取原始公钥,这是调用失败的根本原因。此外,代码未初始化OpenSSL错误字符串库,导致错误信息为空。
修复方案
1. 替换EC公钥提取逻辑
使用EC密钥专用接口提取原始公钥(未压缩格式):
- 从
EVP_PKEY中获取EC_KEY结构 - 通过
EC_GROUP和EC_POINT提取原始公钥字节流 - 处理二进制公钥的打印(不能直接用
%s,需转为十六进制)
2. 初始化OpenSSL错误库
添加错误库初始化代码,确保能获取有效错误信息。
修改后的完整代码
#include <openssl/types.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <openssl/bio.h> #include <openssl/err.h> #include <openssl/evp.h> #include <openssl/dh.h> #include <openssl/ec.h> #include <openssl/evp.h> // My way of error-handling typedef struct { int code; const char *msg; } Ret; #define RET \ (Ret) { .code = 0, .msg = "SUCCESS" } // 修正SUCCESS的code值,避免成功误判为失败 #define cout(...) printf(__VA_ARGS__) #define err(...) fprintf(stderr, __VA_ARGS__) #define RED "\x1B[31m" #define NORM "\033[0m" #define PUBLIC_KEY_SIZE 1000 Ret fn3() { Ret ret = RET; const char *curveName = "secp521r1"; EVP_PKEY *pkey = EVP_EC_gen(curveName); if (NULL == pkey) { ret = (Ret){1, "Unable to generate key"}; goto cleanup; } EVP_PKEY_print_public_fp(stdout, pkey, 1, NULL); int keySize = EVP_PKEY_get_size(pkey); cout("Key size is %d\n", keySize); size_t publicKeySize = PUBLIC_KEY_SIZE; unsigned char publicKey[PUBLIC_KEY_SIZE]; // 替换原有的EVP_PKEY_get_raw_public_key调用 EC_KEY *ec_key = EVP_PKEY_get1_EC_KEY(pkey); if (ec_key == NULL) { ret = (Ret){1, "Failed to get EC_KEY from EVP_PKEY"}; goto cleanup; } const EC_GROUP *group = EC_KEY_get0_group(ec_key); if (group == NULL) { ret = (Ret){1, "Failed to get EC_GROUP from EC_KEY"}; EC_KEY_free(ec_key); goto cleanup; } // 计算未压缩公钥的长度 size_t pub_len = EC_POINT_point2buf(group, EC_KEY_get0_public_key(ec_key), POINT_CONVERSION_UNCOMPRESSED, NULL, 0); if (pub_len == 0) { unsigned long err = ERR_get_error(); char *errMsg = ERR_error_string(err, NULL); ret = (Ret){1, errMsg}; EC_KEY_free(ec_key); goto cleanup; } if (pub_len > PUBLIC_KEY_SIZE) { ret = (Ret){1, "Public key buffer too small"}; EC_KEY_free(ec_key); goto cleanup; } // 提取原始公钥到缓冲区 if (EC_POINT_point2buf(group, EC_KEY_get0_public_key(ec_key), POINT_CONVERSION_UNCOMPRESSED, publicKey, &pub_len) == NULL) { unsigned long err = ERR_get_error(); char *errMsg = ERR_error_string(err, NULL); ret = (Ret){1, errMsg}; EC_KEY_free(ec_key); goto cleanup; } cout("Public key size: %zu bytes\n", pub_len); // 二进制公钥转十六进制打印 cout("Public key (hex): "); for (size_t i = 0; i < pub_len; i++) { printf("%02x", publicKey[i]); } cout("\n"); EC_KEY_free(ec_key); cleanup: EVP_PKEY_free(pkey); return ret; } int main(void) { // 初始化OpenSSL错误库,确保错误信息正常输出 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); ERR_load_crypto_strings(); Ret ret = fn3(); if (ret.code) { err(RED "[%s:%s:%d] Failed\n\t%s\n" NORM, __FILE__, __func__, __LINE__, ret.msg); } else { cout("%s\n", ret.msg); } return ret.code; }
关键修正点说明
- 修正
RET宏的code值:原代码中SUCCESS的code为1,会导致成功时误判为失败,改为0 - 使用
EC_KEY系列接口提取EC公钥,替代不兼容的EVP_PKEY_get_raw_public_key - 二进制公钥转为十六进制打印,避免
%s因空字节截断输出 - 添加OpenSSL错误库初始化,确保能获取详细错误信息
后续ECDH协议实现提示
对于ECDH密钥交换,可使用EVP_PKEY_derive函数:
- 接收对方公钥,构造为
EVP_PKEY结构 - 创建
EVP_PKEY_CTX上下文,设置为ECDH模式 - 调用
EVP_PKEY_derive生成共享密钥
内容的提问来源于stack exchange,提问作者Hackerman
相关产品推荐
相关产品推荐

