如何使用C++和openssl库实现ES256算法加密数据
前置说明
首先明确:ES256是ECDSA签名算法的一种实现,基于P-256(secp256r1)椭圆曲线配合SHA256哈希,核心能力是对数据做签名/验签,实现防篡改、身份校验,不属于加密算法,无法直接用于数据加密解密。
实现步骤
1. 环境依赖
- 安装OpenSSL 1.1.1及以上版本(低版本对EC算法API兼容性较差)
- 编译时需要链接OpenSSL的crypto库,参数为
-lcrypto
2. 核心实现流程
2.1 生成P-256密钥对
首先需要生成ES256对应的EC密钥对,你可以选择直接用OpenSSL命令行生成pem文件,也可以代码动态生成:
命令行生成密钥对的方法:
生成私钥:openssl ecparam -name prime256v1 -genkey -noout -out ec_private.pem
导出公钥:openssl ec -in ec_private.pem -pubout -out ec_public.pem
2.2 数据签名流程
- 加载P-256私钥
- 对原始数据做SHA256哈希计算
- 用私钥对哈希结果做ECDSA签名,输出DER编码或者raw(R+S)格式的签名结果
2.3 签名验证流程
- 加载P-256公钥
- 对原始数据做SHA256哈希计算
- 用公钥验证哈希和收到的签名是否匹配
完整代码示例
#include <openssl/ec.h> #include <openssl/ecdsa.h> #include <openssl/sha.h> #include <openssl/pem.h> #include <openssl/err.h> #include <iostream> #include <vector> #include <cstring> // 加载PEM格式的私钥 EC_KEY* load_private_key(const char* key_path) { FILE* fp = fopen(key_path, "r"); if (!fp) return nullptr; EC_KEY* ec_key = PEM_read_ECPrivateKey(fp, nullptr, nullptr, nullptr); fclose(fp); return ec_key; } // 加载PEM格式的公钥 EC_KEY* load_public_key(const char* key_path) { FILE* fp = fopen(key_path, "r"); if (!fp) return nullptr; EC_KEY* ec_key = PEM_read_EC_PUBKEY(fp, nullptr, nullptr, nullptr); fclose(fp); return ec_key; } // ES256签名 std::vector<unsigned char> es256_sign(EC_KEY* private_key, const unsigned char* data, size_t data_len) { std::vector<unsigned char> signature; if (!private_key || !data || data_len == 0) return signature; // 计算SHA256哈希 unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256(data, data_len, hash); // 签名 signature.resize(ECDSA_size(private_key)); unsigned int sig_len = 0; if (ECDSA_sign(0, hash, SHA256_DIGEST_LENGTH, signature.data(), &sig_len, private_key) != 1) { signature.clear(); return signature; } signature.resize(sig_len); return signature; } // ES256验签 bool es256_verify(EC_KEY* public_key, const unsigned char* data, size_t data_len, const unsigned char* sig, size_t sig_len) { if (!public_key || !data || data_len == 0 || !sig || sig_len == 0) return false; // 计算SHA256哈希 unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256(data, data_len, hash); // 验签 return ECDSA_verify(0, hash, SHA256_DIGEST_LENGTH, sig, sig_len, public_key) == 1; } int main() { // 初始化OpenSSL错误栈 ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); // 测试数据 const char* test_data = "Hello ES256 with OpenSSL!"; size_t data_len = strlen(test_data); // 加载密钥 EC_KEY* priv_key = load_private_key("ec_private.pem"); EC_KEY* pub_key = load_public_key("ec_public.pem"); if (!priv_key || !pub_key) { std::cerr << "Load key failed" << std::endl; return 1; } // 签名 auto sig = es256_sign(priv_key, (const unsigned char*)test_data, data_len); if (sig.empty()) { std::cerr << "Sign failed" << std::endl; return 1; } std::cout << "Signature length: " << sig.size() << std::endl; // 验签 bool verify_result = es256_verify(pub_key, (const unsigned char*)test_data, data_len, sig.data(), sig.size()); std::cout << "Verify result: " << (verify_result ? "Success" : "Failed") << std::endl; // 释放资源 EC_KEY_free(priv_key); EC_KEY_free(pub_key); EVP_cleanup(); ERR_free_strings(); return 0; }
3. 编译与测试
- 编译命令:
g++ es256_demo.cpp -o es256_demo -lcrypto - 先按之前的OpenSSL命令生成公私钥文件放在同一目录,再运行编译后的二进制即可测试
4. 注意事项
- 如果需要输出JWT规范里的raw格式签名(64字节,R占32字节+S占32字节),需要对DER编码的签名做解析,提取R和S字段拼接即可,不需要额外填充
- 密钥加载的时候也可以直接加载内存里的密钥字符串,不需要读写文件,只需要把
PEM_read_ECPrivateKey的FILE参数替换成BIO即可 - 生产环境注意私钥的存储安全,禁止明文硬编码到代码或者配置文件里
内容的提问来源于stack exchange,提问作者Denis Butuzov
相关产品推荐
相关产品推荐

