如何用C/OpenSSL读取公钥并验证PHP生成的EC签名?
使用C/OpenSSL验证PHP生成的EC签名
你用PHP生成了基于secp384r1曲线的EC密钥对,指定SHA256作为摘要算法,以下是用C/OpenSSL验证该签名的完整实现:
PHP生成配置(参考)
$options = [ 'digest_alg' => 'SHA256', 'private_key_type' => OPENSSL_KEYTYPE_EC, 'private_key_bits' => 2048, 'curve_name' => 'secp384r1' ];
待验证的密钥、消息与签名
-----BEGIN PUBLIC KEY----- MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEieSNdLKtNPdLIo9Y+f1DTfjnpZ4NFMXc qVV2epFEWdtCyzhZnMubQDtpmhcycVZM+aPELe0dVWvwkFAY+VO0SpLT4I+cVkar FSkjtp84jWZsWj8DT+7S0ukoEJzraeff -----END PUBLIC KEY----- msg: Hello World! sig: MGYCMQDa3+Nz6zxJOkvLjI2AVFXb4zfoYuFzy79Hg2bKq3El93oQwNleUgV k1TuS8ptSj5kCMQCYVyplp5/yoCBLQpwsQw+MKlJ78ncQgiyh9UIBOFHTQvMjWiZ BvOQ/B0VsZY7xoeo=
C/OpenSSL验证实现
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/evp.h> #include <openssl/pem.h> #include <openssl/bio.h> #include <openssl/err.h> // Base64解码:处理签名的Base64编码 int base64_decode(const char *input, unsigned char **output, size_t *output_len) { BIO *bio, *b64; int decode_len; size_t input_len = strlen(input); b64 = BIO_new(BIO_f_base64()); bio = BIO_new_mem_buf(input, input_len); bio = BIO_push(b64, bio); BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // 忽略签名中的换行符 *output = malloc(input_len); decode_len = BIO_read(bio, *output, input_len); if (decode_len < 0) { free(*output); *output = NULL; *output_len = 0; BIO_free_all(bio); return -1; } *output_len = decode_len; BIO_free_all(bio); return 0; } int main() { // 初始化OpenSSL(兼容旧版本,1.1.0+可省略) OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); // 读取PEM格式公钥 const char *pub_key_pem = "-----BEGIN PUBLIC KEY-----\n" "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEieSNdLKtNPdLIo9Y+f1DTfjnpZ4NFMXc\n" "qVV2epFEWdtCyzhZnMubQDtpmhcycVZM+aPELe0dVWvwkFAY+VO0SpLT4I+cVkar\n" "FSkjtp84jWZsWj8DT+7S0ukoEJzraeff\n" "-----END PUBLIC KEY-----"; BIO *bio = BIO_new_mem_buf(pub_key_pem, strlen(pub_key_pem)); if (!bio) { fprintf(stderr, "Failed to create BIO for public key\n"); ERR_print_errors_fp(stderr); return 1; } EVP_PKEY *pub_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); BIO_free(bio); if (!pub_key) { fprintf(stderr, "Failed to read public key\n"); ERR_print_errors_fp(stderr); return 1; } // 待验证的消息 const char *msg = "Hello World!"; size_t msg_len = strlen(msg); // 解码Base64签名为二进制数据 const char *sig_base64 = "MGYCMQDa3+Nz6zxJOkvLjI2AVFXb4zfoYuFzy79Hg2bKq3El93oQwNleUgV\n" "k1TuS8ptSj5kCMQCYVyplp5/yoCBLQpwsQw+MKlJ78ncQgiyh9UIBOFHTQvMjWiZ\n" "BvOQ/B0VsZY7xoeo="; unsigned char *sig; size_t sig_len; if (base64_decode(sig_base64, &sig, &sig_len) != 0) { fprintf(stderr, "Failed to decode Base64 signature\n"); EVP_PKEY_free(pub_key); return 1; } // 初始化验证上下文 EVP_MD_CTX *ctx = EVP_MD_CTX_new(); if (!ctx) { fprintf(stderr, "Failed to create EVP_MD_CTX\n"); ERR_print_errors_fp(stderr); free(sig); EVP_PKEY_free(pub_key); return 1; } // 执行签名验证 int verify_result = 0; if (EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, pub_key) == 1) { if (EVP_DigestVerifyUpdate(ctx, msg, msg_len) == 1) { verify_result = EVP_DigestVerifyFinal(ctx, sig, sig_len); } } // 输出验证结果 if (verify_result == 1) { printf("Signature verified successfully!\n"); } else if (verify_result == 0) { printf("Signature verification failed!\n"); } else { fprintf(stderr, "Error during signature verification\n"); ERR_print_errors_fp(stderr); } // 清理资源 EVP_MD_CTX_free(ctx); free(sig); EVP_PKEY_free(pub_key); ERR_free_strings(); return verify_result == 1 ? 0 : 1; }
关键说明
- 公钥读取:
PEM_read_bio_PUBKEY会自动识别EC密钥的曲线类型(此处为secp384r1),无需手动指定。 - 签名解码:PHP生成的签名是Base64编码的ASN.1 DER格式,必须先解码为二进制才能用于验证。
- 验证接口:使用OpenSSL推荐的高层
EVP_DigestVerify系列函数,兼容多种密钥算法和摘要类型,这里指定EVP_sha256()与PHP配置一致。 - 错误调试:
ERR_print_errors_fp会输出详细的OpenSSL错误信息,帮助定位问题。
编译与运行
使用以下命令编译代码(确保已安装OpenSSL开发库):
gcc -o ec_verify ec_verify.c -lcrypto
运行编译后的程序:
./ec_verify
若签名有效,会输出Signature verified successfully!。
内容的提问来源于stack exchange,提问作者Ricardo Fodra
相关产品推荐
相关产品推荐

