使用OpenSSL API以编程方式validation证书链

这与其他问题非常相似,但我所看到的问题要么没有答案,要么也不会问同样的问题。 我有一个自签名CA证书,以及使用该CA证书签名的另外两个证书。 我很确定证书是正确的,因为’openssl verify’有效:

$ openssl verify -CAfile ca.pem server.pem server.pem: OK 

(以上是从记忆中,我没有它们在我面前,所以它可能稍微偏离)。

现在我想以编程方式validation证书。 我有一个实用函数,下面是伪代码:

 int verify_cert(X509 *cert, X509 *cacert) { int ret; X509_STORE *store; X509_STORE_CTX *ctx; store = X509_STORE_new(); X590_STORE_add_cert(store, cacert); ctx = X509_STORE_CTX_new(); X509_STORE_CTX_init(ctx, store, cert, NULL); ret = X590_verify_cert(ctx); /* check for errors and clean up */ } 

我的问题是上面的代码总是返回“找不到发行者证书”。 我做错了什么? 我相信我正在创建一个新的商店,添加cacert,创建一个新的上下文,并使用指向包含CA的商店的指针将要validation的子证书添加到上下文中。 我显然做错了什么,但我不确定是什么。

有任何想法吗?

更新:我知道我可以将这些证书保存到磁盘并使用类似X509_LOOKUP_file之类的东西。 我正在寻找一种不会不必要地接触磁盘的解决方案。

您可以使用常规validation例程(请参阅如何validation私有CA是否发布了公钥? ),就像OpenSSL中的-verify函数一样。 您需要创建一个查找方法(X509_LOOKUP_METHOD),如X509_LOOKUP_file(),但它使用字符串而不是文件名。 X509_LOOKUP_buffer()的代码如下。

头文件by_buffer.h:

 /* File: by_buffer.h */ #ifndef BY_BUFFER_H #define BY_BUFFER_H #include  #ifdef __cplusplus extern "C" { #endif #define X509_L_BUF_LOAD 1 #define X509_LOOKUP_load_buf(x,name,type) \ X509_LOOKUP_ctrl((x),X509_L_BUF_LOAD,(name),(long)(type),NULL) X509_LOOKUP_METHOD *X509_LOOKUP_buffer(void); #ifdef __cplusplus } #endif #endif /* BY_BUFFER_H */ 

c程序by_buffer.c:

 /* by_buffer.c - copied and modified from crypto/x509/by_file.c */ /* Copyright (C) - should be the same as for OpenSSL */ #include "by_buffer.h" #include  #include  #include  #include "../crypto/cryptlib.h" #include  #include  #include  #include  static int by_buffer_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, char **ret); X509_LOOKUP_METHOD x509_buffer_lookup= { "Load buffer into cache", NULL, /* new */ NULL, /* free */ NULL, /* init */ NULL, /* shutdown */ by_buffer_ctrl, /* ctrl */ NULL, /* get_by_subject */ NULL, /* get_by_issuer_serial */ NULL, /* get_by_fingerprint */ NULL, /* get_by_alias */ }; X509_LOOKUP_METHOD *X509_LOOKUP_buffer(void) { return(&x509_buffer_lookup); } static int by_buffer_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, char **ret) { int ok=0; char *certBuf; switch (cmd) { case X509_L_BUF_LOAD: if (argl == X509_FILETYPE_DEFAULT) { X509err(X509_F_BY_FILE_CTRL,X509_R_LOADING_DEFAULTS); } else { if(argl == X509_FILETYPE_PEM) ok = (X509_load_cert_crl_buf(ctx,argp, X509_FILETYPE_PEM) != 0); else ok = (X509_load_cert_buf(ctx,argp,(int)argl) != 0); } break; } return(ok); } int X509_load_cert_buf(X509_LOOKUP *ctx, const char *certBuf, int type) { int ret=0; BIO *in=NULL; int i,count=0; X509 *x=NULL; if (certBuf == NULL) return(1); in=BIO_new(BIO_s_mem()); if(in==NULL) goto err; if (type == X509_FILETYPE_PEM) { for (;;) { x=PEM_read_bio_X509_AUX(in,NULL,NULL,NULL); if (x == NULL) { if ((ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) && (count > 0)) { ERR_clear_error(); break; } else { X509err(X509_F_X509_LOAD_CERT_FILE, ERR_R_PEM_LIB); goto err; } } i=X509_STORE_add_cert(ctx->store_ctx,x); if (!i) goto err; count++; X509_free(x); x=NULL; } ret=count; } else if (type == X509_FILETYPE_ASN1) { x=d2i_X509_bio(in,NULL); if (x == NULL) { X509err(X509_F_X509_LOAD_CERT_FILE,ERR_R_ASN1_LIB); goto err; } i=X509_STORE_add_cert(ctx->store_ctx,x); if (!i) goto err; ret=i; } else { X509err(X509_F_X509_LOAD_CERT_FILE,X509_R_BAD_X509_FILETYPE); goto err; } err: if (x != NULL) X509_free(x); if (in != NULL) BIO_free(in); return(ret); } int X509_load_crl_buf(X509_LOOKUP *ctx, const char *certBuf, int type) { int ret=0; BIO *in=NULL; int i,count=0; X509_CRL *x=NULL; if (certBuf == NULL) return(1); //in=BIO_new(BIO_s_file_internal()); in=BIO_new(BIO_s_mem()); if(in==NULL) goto err; if (type == X509_FILETYPE_PEM) { for (;;) { x=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL); if (x == NULL) { if ((ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) && (count > 0)) { ERR_clear_error(); break; } else { X509err(X509_F_X509_LOAD_CRL_FILE, ERR_R_PEM_LIB); goto err; } } i=X509_STORE_add_crl(ctx->store_ctx,x); if (!i) goto err; count++; X509_CRL_free(x); x=NULL; } ret=count; } else if (type == X509_FILETYPE_ASN1) { x=d2i_X509_CRL_bio(in,NULL); if (x == NULL) { X509err(X509_F_X509_LOAD_CRL_FILE,ERR_R_ASN1_LIB); goto err; } i=X509_STORE_add_crl(ctx->store_ctx,x); if (!i) goto err; ret=i; } else { X509err(X509_F_X509_LOAD_CRL_FILE,X509_R_BAD_X509_FILETYPE); goto err; } err: if (x != NULL) X509_CRL_free(x); if (in != NULL) BIO_free(in); return(ret); } int X509_load_cert_crl_buf(X509_LOOKUP *ctx, const char *certBuf, int type) { STACK_OF(X509_INFO) *inf; X509_INFO *itmp; BIO *in; int i, count = 0; if(type != X509_FILETYPE_PEM) return X509_load_cert_buf(ctx, certBuf, type); in = BIO_new(BIO_s_mem()); if(!in) { X509err(X509_F_X509_LOAD_CERT_CRL_FILE,ERR_R_SYS_LIB); return 0; } BIO_write(in, certBuf, strlen(certBuf)); inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL); BIO_free(in); if(!inf) { X509err(X509_F_X509_LOAD_CERT_CRL_FILE,ERR_R_PEM_LIB); return 0; } for(i = 0; i < sk_X509_INFO_num(inf); i++) { itmp = sk_X509_INFO_value(inf, i); if(itmp->x509) { X509_STORE_add_cert(ctx->store_ctx, itmp->x509); count++; } if(itmp->crl) { X509_STORE_add_crl(ctx->store_ctx, itmp->crl); count++; } } sk_X509_INFO_pop_free(inf, X509_INFO_free); return count; } 

C ++中的例程调用上述例程:

 #include "by_buffer.h" static int check(X509_STORE *ctx, const char *certBuf); static X509 *load_cert(const char *certBuf); int validateKey(const char *rsaKeyCA, const char *rsaCertificate) { int ret=0; X509_STORE *cert_ctx=NULL; X509_LOOKUP *lookup=NULL; cert_ctx=X509_STORE_new(); if (cert_ctx == NULL) goto end; OpenSSL_add_all_algorithms(); lookup=X509_STORE_add_lookup(cert_ctx,X509_LOOKUP_buffer()); if (lookup == NULL) goto end; if(!X509_LOOKUP_load_buf(lookup,rsaKeyCA,X509_FILETYPE_PEM)) goto end; lookup=X509_STORE_add_lookup(cert_ctx,X509_LOOKUP_hash_dir()); if (lookup == NULL) goto end; X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT); ret = check(cert_ctx, rsaCertificate); end: if (cert_ctx != NULL) X509_STORE_free(cert_ctx); return ret; } static X509 *load_cert(const char *certBuf) { X509 *x=NULL; BIO *cert; if ((cert=BIO_new(BIO_s_mem())) == NULL) goto end; BIO_write(cert, certBuf, strlen(certBuf)); x=PEM_read_bio_X509_AUX(cert,NULL, NULL, NULL); end: if (cert != NULL) BIO_free(cert); return(x); } static int check(X509_STORE *ctx, const char *certBuf) { X509 *x=NULL; int i=0,ret=0; X509_STORE_CTX *csc; x = load_cert(certBuf); if (x == NULL) goto end; csc = X509_STORE_CTX_new(); if (csc == NULL) goto end; X509_STORE_set_flags(ctx, 0); if(!X509_STORE_CTX_init(csc,ctx,x,0)) goto end; ////// See crypto/asn1/t_x509.c for ideas on how to access and print the values //printf("X.509 name: %s\n", x->name); i=X509_verify_cert(csc); X509_STORE_CTX_free(csc); ret=0; end: ret = (i > 0); if (x != NULL) X509_free(x); return(ret); } 

我自己遇到了这个问题,并开始使用非常接近OP的代码。 我的证书链包括3个证书:证书1(root-ca)发行人:root-ca主题:root-ca证书2(签名-CA)发行人:root-ca主题:签署-c证书3(设备)发行人:签署 – ca主题:设备

我想validation设备证书。 我的ca.pem等价物(wrt OP)包含root-ca和signing-ca。

X509_verify_cert函数需要整个证书链一直到X509_store中的根(root-ca和signing-ca)。

以下是我的代码,对我有用。 省略了对返回值的检查以使代码向下倾斜。

 int getIssuerCert(X509_STORE *x509_store){ STACK_OF(X509_INFO) *inf; X509_INFO *itmp; BIO *in; int i, count = 0; in = BIO_new(BIO_s_mem()); BIO_write(in, issuerCertStr, strlen(issuerCertStr)); //string containing root-ca & signing-ca inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL); if(in != NULL) BIO_free(in); for(i = 0; i < sk_X509_INFO_num(inf); i++) { itmp = sk_X509_INFO_value(inf, i); if(itmp->x509) { X509_STORE_add_cert(x509_store, itmp->x509); count++; } if(itmp->crl) { X509_STORE_add_crl(x509_store, itmp->crl); count++; } } sk_X509_INFO_pop_free(inf, X509_INFO_free); return 0; } int verify_cert(){ int ret = 0; X509 *devCert = NULL; X509_STORE *x509_store = NULL; X509_STORE_CTX *x509_store_ctx = NULL; OpenSSL_add_all_algorithms(); devCert = getDeviceCert(); // Returns X509 pointer x509_store = X509_STORE_new(); X509_STORE_set_verify_cb(x509_store, verify_cb); X509_STORE_set_flags(x509_store, 0); x509_store_ctx = X509_STORE_CTX_new(); X509_STORE_CTX_init(x509_store_ctx, x509_store, devCert, NULL) X509_STORE_CTX_set_purpose(x509_store_ctx, X509_PURPOSE_ANY); ret = X509_verify_cert(x509_store_ctx); if(x509_store_ctx != NULL) X509_STORE_CTX_free(x509_store_ctx); if(x509_store != NULL) X509_STORE_free(x509_store); if(devCert != NULL) X509_free(devCert); EVP_cleanup(); return ret; } 

我不需要创建任何查找方法。 对我来说,关键是从内存中的字符串循环我的证书,所以我拥有完成链所需的所有证书。 该字符串相当于我将用于选项-CAfile的opensslvalidation。

此外,确保使用时X509指针不为空。

一个可能的答案(没有rep点添加评论,抱歉): SSL_CTX_load_verify_locations(3)的联机帮助页说,

 When building its own certificate chain, an OpenSSL client/server will try to fill in missing certificates from CAfile/CApath, if the certificate chain was not explicitly specified (see SSL_CTX_add_extra_chain_cert(3), SSL_CTX_use_certificate(3). 

(没有匹配他们的,不是我的。)

这似乎意味着,作为SSL_CTX_load_verify_locations(3)的替代方案,应该可以使用SSL_CTX_add_extra_chain_cert(3)SSL_CTX_use_certificate(3) – 两者都采用X509 * arg。 因此,如上所述,无需Ed先生的解决方案。

请查看SSL_CTX_load_verify_locations ()函数: http : //www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html

SSL_CTX_load_verify_locations()指定ctx的位置,用于validation目的的CA证书位于该位置。 通过CAfile和CApath可用的证书是可信的。

您可以生成包含ca.pem server.pem的CA证书文件:

  #!/bin/sh rm CAfile.pem for i in ca.pem server.pem ; do openssl x509 -in $i -text >> CAfile.pem done 

然后将CAfile变量设置为指向CAfile.pem文件。

希望能帮助到你 !

我想,您可以使用“X509_STORE_set_verify_cb”添加回调来识别实际错误:

 static int verify_cb(int ok, X509_STORE_CTX *ctx) { if (!ok) { /* check the error code and current cert*/ X509 *currentCert = X509_STORE_CTX_get_current_cert(ctx); int certError = X509_STORE_CTX_get_error(ctx); int depth = X509_STORE_CTX_get_error_depth(ctx); printCert(currentCert); printf("Error depth %d, certError %d", depth, certError) } return(ok); } int verify_cert(X509 *cert, X509 *cacert) { int ret; X509_STORE *store; X509_STORE_CTX *ctx; store = X509_STORE_new(); X509_STORE_set_verify_cb(store, verify_cb); X590_STORE_add_cert(store, cacert); ctx = X509_STORE_CTX_new(); X509_STORE_CTX_init(ctx, store, cert, NULL); ret = X590_verify_cert(ctx); /* check for errors and clean up */ } 

除非我们知道错误代码,否则很难猜出实际问题。 否则代码看起来不错。