You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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;
}

关键说明

  1. 公钥读取:PEM_read_bio_PUBKEY会自动识别EC密钥的曲线类型(此处为secp384r1),无需手动指定。
  2. 签名解码:PHP生成的签名是Base64编码的ASN.1 DER格式,必须先解码为二进制才能用于验证。
  3. 验证接口:使用OpenSSL推荐的高层EVP_DigestVerify系列函数,兼容多种密钥算法和摘要类型,这里指定EVP_sha256()与PHP配置一致。
  4. 错误调试:ERR_print_errors_fp会输出详细的OpenSSL错误信息,帮助定位问题。

编译与运行

使用以下命令编译代码(确保已安装OpenSSL开发库):

gcc -o ec_verify ec_verify.c -lcrypto

运行编译后的程序:

./ec_verify

若签名有效,会输出Signature verified successfully!。

内容的提问来源于stack exchange,提问作者Ricardo Fodra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:11:02