简体   繁体   English

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

[英]Modifying extension list in X509 certificate using OpenSSL in C

I am tring to insert signed sct into a precertificate that has a poison extension.我正在尝试将已签名的 sct 插入到具有毒扩展名的预证书中。 So I first remove the poison extension, then add the SCT.所以我首先删除毒扩展,然后添加 SCT。 This is what I've done:这就是我所做的:

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;
}

This code seems to work, all step are fine, problem is, the final certificate displayed, when I save it to a file and run the command:这段代码似乎工作,所有步骤都很好,问题是,当我将它保存到文件并运行命令时,显示的最终证书:

openssl x509 -in cert.pem -noout -text

it is the same as the original precertificate, it contains the poison extension and no SCT.它与原始预认证相同,包含毒扩展,没有 SCT。 Even comparing both files, they are identical.即使比较两个文件,它们也是相同的。 Where did I go wrong?我在哪里 go 错了?

Your main problem is that for X509 X509_CRL X509_REQ when created by parsing input ie not built up from scratch OpenSSL saves the tbs encoding and reuses it on output (and digesting and comparison, which is why your X509_cmp failed) even if you have changed some of the fields that go in that encoding, unless you sign the change(s) which you must to make the resulting object valid anyway.您的主要问题是,对于通过解析输入创建的X509 X509_CRL X509_REQ即不是从头开始构建OpenSSL 保存 tbs 编码并在 output 上重用它(以及消化和比较,这就是为什么你改变了一些X509_cmp失败) go 在该编码中的字段,除非您签署更改,否则您必须使生成的 object 仍然有效。 In short, you need to call X509_sign() or the extended form X509_sign_ctx() after making your changes.简而言之,您需要在进行更改后调用X509_sign()或扩展形式X509_sign_ctx()

After fixing that you have another problem: it does remove the poison ext and add an SCT ext -- which contains garbage, because your unnecessary call to d2i_SCT_LIST has changed the pointer you use.修复后你还有另一个问题:它确实删除了毒分机并添加了一个包含垃圾的 SCT 分机,因为你对d2i_SCT_LIST的不必要调用已经改变了你使用的指针。 Removing that, plus your other unneeded cruft, produces the following code that works (with a precert and key of my own) to produce a correct-looking cert, though of course the SCTs you provided aren't valid for it:删除它,加上你其他不需要的东西,会产生以下代码(使用我自己的预证书和密钥)来产生一个看起来正确的证书,当然你提供的 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;
}

However, modifying a value returned by get0 -- and 'discarding' the const on it -- is not good style, and might fail in some future implementation.但是,修改get0返回的值——并“丢弃”其上的const不是好的风格,并且可能在将来的某些实现中失败。 It would be safer and also simpler to use X509_get_ext_by_NID and X509_delete_ext directly on cert .直接在cert上使用X509_get_ext_by_NIDX509_delete_ext会更安全,也更简单。

Thanks @PhoenixBlue and @dave_thompson_085, if anyone wants to embeded json sct list which got from ct log server to x509 certificate, you can try SCT_new_from_base64 this method, which convert json sct to SCT* object, then you can use i2d_SCT_LIST and ASN1_OCTET_STRING_set methods to add sct list x509v3 extension. Thanks @PhoenixBlue and @dave_thompson_085, if anyone wants to embeded json sct list which got from ct log server to x509 certificate, you can try SCT_new_from_base64 this method, which convert json sct to SCT* object, then you can use i2d_SCT_LIST and ASN1_OCTET_STRING_set methods to添加 sct 列表 x509v3 扩展。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM