OpenSSL 1.1.1下X25519 ECDH实现:密钥导出与导入求助
X25519 ECDH公钥导出与导入解决方案
你提到用EC_KEY_new_by_curve_name(NID_X25519)失败是正常的——OpenSSL里X25519属于EVP_PKEY框架下的密钥类型,EC_KEY这套API并不支持它,所以你用EVP_PKEY生成密钥的路子完全正确。下面直接给你公钥导出、导入的具体实现,附完整代码示例:
一、导出无压缩二进制公钥
X25519的公钥本身就是固定32字节的无压缩格式,不需要额外处理,直接用EVP_PKEY_get_raw_public_key就能提取原始二进制数据:
#include <openssl/evp.h> #include <stdio.h> #include <stdlib.h> // 导出X25519公钥为32字节二进制 int export_x25519_public_key(EVP_PKEY *pkey, unsigned char **out_pub, size_t *out_len) { if (!pkey || !out_pub || !out_len) return 0; // X25519公钥固定32字节,直接分配内存 *out_len = 32; *out_pub = malloc(*out_len); if (!*out_pub) return 0; // 提取原始公钥 if (EVP_PKEY_get_raw_public_key(pkey, *out_pub, out_len) != 1) { free(*out_pub); *out_pub = NULL; return 0; } // 校验公钥长度(确保没有异常) if (*out_len != 32) { free(*out_pub); *out_pub = NULL; return 0; } return 1; }
调用时把你生成的pkey传进去就行,拿到的out_pub就是32字节的无压缩公钥二进制,记得用完要手动free释放内存。
二、导入对方的32字节二进制公钥
导入同样用EVP_PKEY框架,通过EVP_PKEY_fromdata加载原始公钥数据:
// 从32字节二进制导入X25519公钥 EVP_PKEY *import_x25519_public_key(const unsigned char *pub_data, size_t pub_len) { if (!pub_data || pub_len != 32) return NULL; EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(NID_X25519, NULL); if (!ctx) return NULL; EVP_PKEY *pkey = NULL; // 初始化公钥导入上下文 if (EVP_PKEY_fromdata_init(ctx) != 1) { EVP_PKEY_CTX_free(ctx); return NULL; } // 设置导入类型为公钥 if (EVP_PKEY_CTX_set_type(ctx, EVP_PKEY_PUBLIC_KEY) != 1) { EVP_PKEY_CTX_free(ctx); return NULL; } // 从原始二进制数据加载公钥 if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, pub_data, pub_len) != 1) { EVP_PKEY_CTX_free(ctx); return NULL; } EVP_PKEY_CTX_free(ctx); return pkey; }
注意:传入的公钥必须是严格32字节的X25519公钥,否则导入会失败。拿到返回的EVP_PKEY后,用完要调用EVP_PKEY_free释放资源。
三、补充:完成ECDH密钥交换(生成共享密钥)
既然你在做ECDH,顺便给你加个生成共享密钥的示例,把导入的对方公钥和自己的私钥结合起来:
// 生成X25519共享密钥 int generate_shared_secret(EVP_PKEY *my_privkey, EVP_PKEY *peer_pubkey, unsigned char **shared_secret, size_t *secret_len) { if (!my_privkey || !peer_pubkey || !shared_secret || !secret_len) return 0; EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(my_privkey, NULL); if (!ctx) return 0; int ret = 0; // 初始化密钥派生上下文 if (EVP_PKEY_derive_init(ctx) != 1) goto cleanup; // 设置对方的公钥 if (EVP_PKEY_derive_set_peer(ctx, peer_pubkey) != 1) goto cleanup; // 先获取共享密钥的长度 if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) goto cleanup; // 分配内存存储共享密钥 *shared_secret = malloc(*secret_len); if (!*shared_secret) goto cleanup; // 生成共享密钥 if (EVP_PKEY_derive(ctx, *shared_secret, secret_len) != 1) { free(*shared_secret); *shared_secret = NULL; goto cleanup; } ret = 1; cleanup: EVP_PKEY_CTX_free(ctx); return ret; }
X25519的共享密钥也是固定32字节,所以secret_len最终会是32。
注意事项
- 所有OpenSSL函数调用都要检查返回值(我上面的代码都加了基本的错误处理),避免内存泄漏或逻辑错误;
- 编译的时候要链接OpenSSL库,比如用
gcc your_code.c -o your_code -lcrypto; - 确保你的OpenSSL版本是1.1.0及以上——X25519支持是从这个版本开始加入的。
内容的提问来源于stack exchange,提问作者Anton Vainer
相关产品推荐
相关产品推荐

