在 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_NID
和 X509_delete_ext
会更安全也更简单
我正在尝试将已签名的 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_NID
和 X509_delete_ext
会更安全也更简单