2013-04-30 112 views
18

這與其他問題非常相似,但我所查看的問題要麼沒有答案,要麼沒有提出相同的問題。我有一個自簽名的CA證書和兩個使用該CA證書籤名的證書。我相當肯定的證書是正確的,因爲「OpenSSL的核實」的工作原理:使用OpenSSL API以編程方式驗證證書鏈

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

(以上是從內存中,我沒有讓他們在我面前,所以它可能會稍微偏離)。

現在我想以編程方式驗證證書。我有一個效用函數下面僞代碼:

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的商店的指針添加到上下文中。我很明顯做錯了什麼,但我不確定是什麼。

任何想法?

更新:我知道我可以將這些證書保存到磁盤並使用類似X509_LOOKUP_file或類似的東西。我正在尋找一種不會不必要地接觸磁盤的解決方案。

+0

我面臨同樣的問題 - 你有沒有找到任何解決方案呢? – 2013-05-13 09:53:16

+0

@ koch.trier不,不幸的不是。現在我已經把它放在了後臺,專注於其他事情。我仍然在這裏尋找答案。 – clemej 2013-05-14 02:08:44

+0

可能重複的[x509證書驗證在C](http://stackoverflow.com/questions/2756553/x509-certificate-verification-in-c) – jww 2014-03-03 05:21:16

回答

12

您可以使用正常的驗證例程(請參閱How do you verify a public key was issued by your private CA?),例如OpenSSL中的-verify函數。您需要創建一個像X509_LOOKUP_file()一樣的查找方法(X509_LOOKUP_METHOD),但它使用字符串而不是文件名。 X509_LOOKUP_buffer()的代碼如下。

頭文件by_buffer.h:

/* File: by_buffer.h */ 

#ifndef BY_BUFFER_H 
#define BY_BUFFER_H 

#include <openssl/x509.h> 

#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 <stdio.h> 
#include <time.h> 
#include <errno.h> 

#include "../crypto/cryptlib.h" 
#include <openssl/lhash.h> 
#include <openssl/buffer.h> 
#include <openssl/pem.h> 
#include <openssl/err.h> 

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); 
} 
+2

這是一個非常多的工作來定義一個全新的回調機制。但它似乎也是唯一的方法。廣泛的示例代碼的獎勵積分。 – clemej 2013-06-10 22:33:26

+6

這段代碼是「正確的」,但它都是完全沒用的!此代碼中的中央調用是「X509_STORE_add_cert」,與OP最初使用的API調用完全相同。它已經被封裝在'X509_load_cert_buf'函數中,然後通過'X509_LOOKUP_load_buf'以非常間接的方式調用它。這個代碼在OP的原始代碼中沒有任何優勢,它直接簡單地稱爲「X509_STORE_add_cert」。 – 2014-11-04 15:10:19

+0

對不起,我提供了「無用的」代碼!我想要做的就是複製openssl verify函數,這和我的代碼一樣。我沒有試圖優化它比OpenSSL的代碼更好。 – 2014-12-29 20:52:07

2

請看一看SSL_CTX_load_verify_locations()功能:http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html

SSL_CTX_load_verify_locations() specifies the locations for ctx, at which CA certificates for verification purposes are located. The certificates available via CAfile and CApath are trusted.

可以生成同時包含CA證書文件ca.pem server.pem:

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

然後設置CAfile變量指向到CAfile.pem文件。

希望它有幫助!

+1

我很困惑,爲什麼當我使用add_cert顯式加載證書時,需要指定一個位置(文件或目錄)? – clemej 2013-04-30 12:15:52

+0

我在OpenSSL中找不到任何X590_STORE_add_cert()函數,它來自哪裏?通常,您希望使用SSL_CTX_load_verify_locations()函數和PEM文件的路徑來驗證證書鏈。 – Paul 2013-04-30 12:51:07

+0

http://www.umich.edu/~x509/ssleay/x509_store.html。但這是蹭。這些文件不在磁盤上。他們已經在x509結構中記憶。我不應該真的需要將它們寫到磁盤上來驗證它們。是嗎? – clemej 2013-04-30 13:36:34

3

可能答案(沒有要點添加評論,對不起):聯機幫助頁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先生如上所述的解決方案的需要。

2

我認爲,你可以使用「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 */ 
} 

除非我們知道錯誤代碼是很難猜測的實際問題。代碼看起來沒問題。

2

我自己遇到了這個問題,並開始使用非常接近OP的代碼。我的證書鏈包括3個證書: 證書1(root-ca)頒發者:root-ca主題:root-ca 證書2(簽署ca)頒發者:root-ca主題:簽署ca 證書3 :簽署ca主題:設備

我想驗證設備證書。我的ca.pem等價(包括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; 
} 

我不需要創建任何查找方法。對我來說,關鍵在於循環訪問內存中的字符串中的證書,因此我擁有了完成該鏈所需的所有證書。該字符串等同於我將輸入openssl驗證選項-CAfile的內容。

另外,確保您的X509指針在使用時不爲空。