在 C 中使用 OpenSSL 修改 X509 证书中的扩展名列表

Modifying extension list in X509 certificate using OpenSSL in C

我正在尝试将已签名的 sct 插入到具有毒扩展名的预证书中。 所以我首先删除毒扩展,然后添加 SCT。 这就是我所做的:

int main(int argc, char **argv) {
    size_t lenCert = 0, lenCert2 = 0;
    char *filePEM  = "testpem/precert3.cert.pem";
    char *strCertPem = loadFileContent(filePEM, &lenCert);
    const X509 *cert = parse_certificate(strCertPem);
    X509 *certRef = X509_dup(cert);

    if(!cert || cert==NULL){
        printf("Failed parsing\n");
        return -1;
    }

    int len_init = -1;
    unsigned char *buf_init = NULL;
    len_init = i2d_X509(certRef, &buf_init);

    if(len_init < 0){
        printf("INIT: failed conversion to DER\n");
        return -1;
    } else {
        printf("INIT: Successful conversion to DER[%d]\n", len_init);
    }


    printf("size certificate: %ld\n", lenCert);
    X509_EXTENSION *tmpext;
    const STACK_OF(X509_EXTENSION) *allExt = X509_get0_extensions(cert);
    const STACK_OF(X509_EXTENSION) *allExt2 = X509_get0_extensions(certRef);
    int my_idx = X509v3_get_ext_by_NID(allExt, NID_ct_precert_poison, -1);
    int idx = my_idx;
    int cc = X509_get_ext_count(cert);
    printf("Extension count in cert BEFORE = %d\n", cc);

    printf((allExt==NULL) ? "Extensions extraction FAILED\n" : "Extensions extraction SUCCESS\n");

    int counter = X509v3_get_ext_count(allExt);
    printf("Extension[%d] count BEFORE = %d\n", idx, counter);

    do {
        tmpext = X509v3_get_ext(allExt, idx);
        X509v3_delete_ext(allExt, idx);
        X509_EXTENSION_free(tmpext);
        idx = X509v3_get_ext_by_NID(allExt, NID_ct_precert_poison, -1);
        printf("pass\n");
    } while (idx != -1);

    counter = X509v3_get_ext_count(allExt);
    printf("Extension count AFTER = %d\n", counter);

    if(X509_cmp( cert, certRef)){
        printf("Certificate modified\n\n");
    } else {
        printf("FAILED!!! \n");
    }

    cc = X509_get_ext_count(cert);
    printf("Extension count in cert AFTER = %d\n", cc);

    int len_inter;
    unsigned char *buf_inter = NULL;
    len_inter = i2d_X509(cert, &buf_inter);
    if(len_inter < 0){
        printf("INTERMEDIATE: failed conversion to DER\n");
        return -1;
    } else {
        printf("INTERMEDIATE: Successful conversion to DER[%d]\n", len_inter);
    }

    unsigned char *dersct;
    size_t lenSCTList = 0;
    char *b64SCTList = "BIF6AHgAdgCwzIPlpfl9a698CcwoSQSHKsfoixMsY1C3xv0m4WxsdwAAAWZ7z/DQAAAEAwBHMEUCIQDKJPPQhWqje1rQq+T06x0iNlLT7rX71k23VPZkhm/QCwIgfhwNK7izeq0fHAlu7HuYRjmvym51RRdlNWhd50LQdu4=";
    int b64Res = Base64Decode(b64SCTList, &dersct, &lenSCTList); //Decodes a base64 encoded string
    printf("size final SCT List: %ld\n", lenSCTList);

    STACK_OF(SCT) * scts = d2i_SCT_LIST(NULL, (const unsigned char **) &dersct, lenSCTList);
    if(scts==NULL){
        printf("Could not convert SCT List!");
        return -1;
    } 
    printf("SCT List converted !\n");
    ASN1_OCTET_STRING   *val   = ASN1_OCTET_STRING_new();
    ASN1_OCTET_STRING_set(val, dersct, (int)lenSCTList);
    X509_EXTENSION* extSCT = X509_EXTENSION_create_by_NID(NULL, NID_ct_precert_scts, 0, val);

    if(extSCT){
        printf("created extension\n");
    } else {
        printf("Failed to create extension\n");
        return -1;
    }

    if( X509_add_ext(cert, extSCT, -1)) {
        printf("Extension added\n");
       //  X509_EXTENSION_free(extSCT);

    } else {
        printf("failed to add extension\n");
        return -1;
    }
    int len_final;
    unsigned char *buf_final = NULL;
    len_final = i2d_X509(cert, &buf_final);
    if(len_final < 0){
        printf("FINAL: failed conversion to DER\n");
        return -1;
    } else {
        printf("FINAL: Successful conversion to DER[%d]\n", len_final);
    }

    BIO *Cout = BIO_new(BIO_s_mem());
    PEM_write_bio_X509(Cout, cert);
    char* data;
    const long len = BIO_get_mem_data(Cout, &data);
    
    cc = X509_get_ext_count(cert);
    printf("Extension count in cert AFTER = %d\n", cc);
    printf("\ndata[%ld]: \n%s\n\n", len, data);
    BIO_free_all(Cout);



    int my_idx2 = X509_get_ext_by_NID(cert, NID_ct_precert_poison, -1);
    X509_EXTENSION* extPoison2 = X509_get_ext(cert, my_idx2);

    if(!extPoison2){
        printf("failed last extension[%d] extract \n ", my_idx2);
        return -1;
    } else {
        printf("Succeeded last extension extract[%d]\n ", my_idx2);
    }


    return 0;
}

此代码似乎有效,所有步骤都很好,问题是,当我将其保存到文件和 运行 命令时显示的最终证书:

openssl x509 -in cert.pem -noout -text

和原来的precertificate一样,都是毒扩展,没有SCT。 即使比较两个文件,它们也是相同的。 我哪里错了?

你的主要问题是 X509 X509_CRL X509_REQ 当通过解析输入 创建时,即不是从头开始构建 OpenSSL 保存 tbs 编码并重用它 输出(以及消化和比较,这就是你的 X509_cmp 失败的原因)即使你已经更改了该编码中的一些字段,除非你签署更改( s) 无论如何你必须使结果对象有效。简而言之,您需要在更改后调用 X509_sign() 或扩展形式 X509_sign_ctx()

修复后您遇到了另一个问题:它确实删除了毒分机并添加了一个包含垃圾的 SCT 分机,因为您对 d2i_SCT_LIST 的不必要调用已经更改了您使用的指针。删除它,再加上你其他不需要的东西,会产生以下代码(使用我自己的预证书和密钥)来生成外观正确的证书,当然你提供的 SCT 对其无效:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/asn1.h>
#include <openssl/ct.h>
#include <openssl/pem.h>
#include <openssl/x509.h>

int main(int argc, char **argv) {
    // size_t lenCert = 0, lenCert2 = 0;
    // char *filePEM  = "testpem/precert3.cert.pem";
    // replace mystery routines by one PEM:
    FILE *infile = fopen(argv[1],"r"); if(!infile) exit(1);
    X509 *cert = PEM_read_X509 (infile, NULL, NULL, NULL);
    fclose(infile);
    X509 *certRef = X509_dup(cert);

    if(!cert || cert==NULL){ // redundant, and too late
        printf("Failed parsing\n");
        return -1;
    }

#if 0 // useless
    unsigned char *buf_init = NULL;
    int len_init = i2d_X509(certRef, &buf_init);
#endif

    //--printf("size certificate: %ld\n", lenCert);
    //--X509_EXTENSION *tmpext;
    const STACK_OF(X509_EXTENSION) *allExt = X509_get0_extensions(cert);
    const STACK_OF(X509_EXTENSION) *allExt2 = X509_get0_extensions(certRef);
    int my_idx = X509v3_get_ext_by_NID(allExt, NID_ct_precert_poison, -1);
    int idx = my_idx;
#if 0
    int cc = X509_get_ext_count(cert);
    printf("Extension count in cert BEFORE = %d\n", cc);

    //--printf((allExt==NULL) ? "Extensions extraction FAILED\n" : "Extensions extraction SUCCESS\n");
#endif

#if 0 // useless 
    int counter = X509v3_get_ext_count(allExt);
    printf("Extension[%d] count BEFORE = %d\n", idx, counter);
#endif

#if 0
    do { 
        X509_EXTENSION * tmpext = X509v3_get_ext(allExt, idx);
#endif
        X509v3_delete_ext(allExt, idx);
#if 0
        X509_EXTENSION_free(tmpext);
        idx = X509v3_get_ext_by_NID(allExt, NID_ct_precert_poison, -1);
        printf("pass\n");
    } while (idx != -1);
    printf("Extension count AFTER = %d\n", X509v3_get_ext_count(allExt));
#endif

#if 0
    if(X509_cmp( cert, certRef)){
        printf("Certificate modified\n\n");
    } else {
        printf("CENSORED \n");
    }
    printf("Extension count in cert AFTER = %d\n", X509_get_ext_count(cert));
#endif

#if 0 // useless
    unsigned char *buf_inter = NULL;
    int len_inter = i2d_X509(cert, &buf_inter);
#endif

    unsigned char *dersct;
    size_t lenSCTList = 0;
    char *b64SCTList = "BIF6AHgAdgCwzIPlpfl9a698CcwoSQSHKsfoixMsY1C3xv0m4WxsdwAAAWZ7z/DQAAAEAwBHMEUCIQDKJPPQhWqje1rQq+T06x0iNlLT7rX71k23VPZkhm/QCwIgfhwNK7izeq0fHAlu7HuYRjmvym51RRdlNWhd50LQdu4=";
    // replace mystery routine
    dersct = malloc(strlen(b64SCTList)); // more than needed but convenient
    lenSCTList = EVP_DecodeBlock(dersct, (unsigned char*)b64SCTList, strlen(b64SCTList));
    printf("size final SCT List: %ld\n", lenSCTList);

#if 0 // useless and harmful
    STACK_OF(SCT) * scts = d2i_SCT_LIST(NULL, (const unsigned char **) &dersct, lenSCTList);
    if(scts==NULL){
        printf("Could not convert SCT List!");
        return -1;
    } 
    printf("SCT List converted !\n");
#endif
    ASN1_OCTET_STRING   *val   = ASN1_OCTET_STRING_new();
    ASN1_OCTET_STRING_set(val, dersct, (int)lenSCTList);
    free(dersct); // added
    X509_EXTENSION* extSCT = X509_EXTENSION_create_by_NID(NULL, NID_ct_precert_scts, 0, val);

#if 0
    if(extSCT){
        printf("created extension\n");
    } else {
        printf("Failed to create extension\n");
        return -1;
    }
#endif

    if( X509_add_ext(cert, extSCT, -1)) {
        printf("Extension added\n");
       //  X509_EXTENSION_free(extSCT);

    } else {
        printf("failed to add extension\n");
        return -1;
    }
#if 0 // useless
    unsigned char *buf_final = NULL;
    int len_final = i2d_X509(cert, &buf_final);
#endif

    // added
    FILE * keyfile = fopen(argv[2],"r"); if(!keyfile) exit(2);
    EVP_PKEY * signkey = PEM_read_PrivateKey (keyfile, NULL, NULL, NULL);
    fclose(keyfile);
    if( X509_sign(cert,signkey,EVP_sha256())<=0 ) exit(9);

    BIO *Cout = BIO_new(BIO_s_mem());
    PEM_write_bio_X509(Cout, cert);
    char* data;
    const long len = BIO_get_mem_data(Cout, &data);
    
    printf("Extension count in cert AFTER = %d\n", X509_get_ext_count(cert));
    printf("\ndata[%ld]: \n%s\n\n", len, data);
    // added
    FILE *outfile = fopen(argv[3],"w"); if(!outfile) exit(3);
    fwrite(data,1,len,outfile); fclose(outfile);
    BIO_free_all(Cout);

#if 0 // useless
    int my_idx2 = X509_get_ext_by_NID(cert, NID_ct_precert_poison, -1);
    X509_EXTENSION* extPoison2 = X509_get_ext(cert, my_idx2);

    if(!extPoison2){
        printf("failed last extension[%d] extract \n ", my_idx2);
        return -1;
    } else {
        printf("Succeeded last extension extract[%d]\n ", my_idx2);
    }
#endif

    return 0;
}

但是,修改由 get0 返回的值——以及 'discarding' 上的 const —— 不是好的风格,并且 可能 在未来的某些实施中失败。直接在 cert.

上使用 X509_get_ext_by_NIDX509_delete_ext 会更安全也更简单