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

如何通过OpenSSL API获取电子护照SOD文件中证书的SKI与AKI

用OpenSSL API提取电子护照SOD文件中证书的SKI/AKI

嘿,我刚好处理过类似的电子护照SOD文件解析需求,用OpenSSL API提取SKI(SubjectKeyIdentifier)和AKI(AuthorityKeyIdentifier)其实分几个关键步骤,我给你拆解清楚:

核心前提:SOD文件的结构

首先要明确,电子护照的SOD文件是CMS(Cryptographic Message Syntax)格式的签名数据,里面包含了一组证书(包括签名者证书)。所以第一步得先解析CMS对象,再从中提取证书集合。

步骤1:加载并解析SOD文件的CMS结构

用OpenSSL的CMS相关API读取SOD文件,解析出CMS内容对象,再提取里面的证书栈:

#include <openssl/cms.h>
#include <openssl/x509.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    // 初始化OpenSSL错误处理
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();

    // 打开SOD文件
    BIO *sod_bio = BIO_new_file("passport.sod", "rb");
    if (!sod_bio) {
        ERR_print_errors_fp(stderr);
        return 1;
    }

    // 解析CMS对象
    CMS_ContentInfo *cms = d2i_CMS_bio(sod_bio, NULL);
    BIO_free(sod_bio);
    if (!cms) {
        ERR_print_errors_fp(stderr);
        return 1;
    }

    // 提取证书栈
    STACK_OF(X509) *cert_stack = CMS_get1_certs(cms);
    if (!cert_stack || sk_X509_num(cert_stack) == 0) {
        printf("SOD文件中未找到任何证书\n");
        CMS_ContentInfo_free(cms);
        return 1;
    }

步骤2:提取SubjectKeyIdentifier(SKI)

SKI是X509证书的扩展字段,OID为1.3.6.1.4.1.311.2.1.14。我们可以用OpenSSL的X509_get_ext_d2i()直接提取,再转成易读的十六进制字符串:

// 辅助函数:提取SKI并转为十六进制字符串
char *extract_ski(X509 *cert) {
    // 用预定义的NID获取SKI扩展(比直接写OID更方便)
    ASN1_OCTET_STRING *ski_data = X509_get_ext_d2i(cert, NID_subject_key_identifier, NULL, NULL);
    if (!ski_data) return NULL;

    // 分配内存存储十六进制字符串
    char *ski_hex = malloc(2 * ski_data->length + 1);
    if (!ski_hex) {
        ASN1_OCTET_STRING_free(ski_data);
        return NULL;
    }

    // 把字节数组转成十六进制
    for (int i = 0; i < ski_data->length; i++) {
        sprintf(ski_hex + 2*i, "%02X", ski_data->data[i]);
    }
    ski_hex[2 * ski_data->length] = '\0';

    ASN1_OCTET_STRING_free(ski_data);
    return ski_hex;
}

步骤3:提取AuthorityKeyIdentifier(AKI)

AKI的OID是2.5.29.35,它的结构更复杂,包含了签发者的Key ID、证书 issuer 和 serial number 等字段。这里我们提取最常用的keyIdentifier部分:

// 辅助函数:提取AKI的keyIdentifier部分(十六进制)
char *extract_aki_keyid(X509 *cert) {
    AUTHORITY_KEYID *aki_data = X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL);
    if (!aki_data || !aki_data->keyid) {
        AUTHORITY_KEYID_free(aki_data);
        return NULL;
    }

    ASN1_OCTET_STRING *keyid = aki_data->keyid;
    char *aki_hex = malloc(2 * keyid->length + 1);
    if (!aki_hex) {
        AUTHORITY_KEYID_free(aki_data);
        return NULL;
    }

    for (int i = 0; i < keyid->length; i++) {
        sprintf(aki_hex + 2*i, "%02X", keyid->data[i]);
    }
    aki_hex[2 * keyid->length] = '\0';

    AUTHORITY_KEYID_free(aki_data);
    return aki_hex;
}

步骤4:遍历证书栈,提取目标证书的SKI/AKI

SOD文件里可能有多个证书,你需要根据实际情况筛选签名者证书(比如通过签发者名称、序列号匹配)。这里我们先遍历所有证书演示:

// 遍历证书栈
    for (int i = 0; i < sk_X509_num(cert_stack); i++) {
        X509 *cert = sk_X509_value(cert_stack, i);
        printf("=== 证书 %d ===\n", i+1);

        // 提取并打印SKI
        char *ski = extract_ski(cert);
        if (ski) {
            printf("Subject Key Identifier: %s\n", ski);
            free(ski);
        } else {
            printf("未找到Subject Key Identifier\n");
        }

        // 提取并打印AKI的keyID
        char *aki = extract_aki_keyid(cert);
        if (aki) {
            printf("Authority Key Identifier (Key ID): %s\n", aki);
            free(aki);
        } else {
            printf("未找到Authority Key Identifier的Key ID部分\n");
        }

        // 可选:筛选签名者证书(示例:检查签发者和序列号)
        // X509_NAME *issuer = X509_get_issuer_name(cert);
        // ASN1_INTEGER *serial = X509_get_serialNumber(cert);
        // 这里可以添加自定义逻辑匹配目标证书
    }

    // 释放资源,避免内存泄漏
    sk_X509_pop_free(cert_stack, X509_free);
    CMS_ContentInfo_free(cms);
    ERR_free_strings();
    EVP_cleanup();

    return 0;
}

注意事项

  • 版本兼容性:上面的代码兼容OpenSSL 1.1.1系列,如果你用的是3.0+版本,部分API可能有调整(比如可以用OSSL_DECODER替代d2i_CMS_bio),但核心逻辑一致。
  • 错误处理:代码里的错误处理比较基础,实际项目中建议更严谨地处理OpenSSL的错误栈(比如用ERR_get_error()获取具体错误码)。
  • 编译链接:编译时要链接OpenSSL的crypto库,比如:
    gcc sod_parser.c -o sod_parser -lcrypto
    
  • 证书筛选:如果SOD里有多个证书,一定要根据电子护照的规范(比如ICAO 9303)找到对应的签名者证书,不要直接用第一个证书。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:22:41