2013-04-30 4 views
18

Это очень похоже на другие вопросы, но те, на которые я смотрел, либо не отвечают, либо не задают один и тот же вопрос. У меня есть самоподписанный сертификат ЦС и два других сертификата, подписанных с этим сертификатом ЦС. Я совершенно уверен, что сертификаты являются правильными, потому что «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, создавая новый контекст и добавляя дочерний cert для проверки в контекст с указателем на хранилище, которое содержит CA. Я, очевидно, делаю что-то неправильно, но я не уверен, что.

Любые идеи?

Обновление: Я знаю, что могу сохранить эти сертификаты на диске и использовать что-то вроде X509_LOOKUP_file или что-то подобное. Я ищу решение, которое не трогает диск без необходимости.

+0

Я столкнулся с той же проблемой - вы нашли какое-либо решение? –

+0

@ koch.trier нет, к сожалению нет. На данный момент я поставил его на задний план и сосредоточился на других вещах. Я все еще ищу ответ здесь. – clemej

+0

Возможный дубликат [x509 проверка сертификата в C] (http://stackoverflow.com/questions/2756553/x509-certificate-verification-in-c) – jww

ответ

12

Вы можете использовать обычные процедуры проверки (см. How do you verify a public key was issued by your private CA?), как и функция -verify в OpenSSL. Вам нужно создать метод поиска (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 <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 */ 

С программой 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

+6

Этот код является «правильным», но все это совершенно бесполезно! Центральным вызовом в этом коде является «X509_STORE_add_cert», который является точно таким же вызовом API, который первоначально использовался OP. Он был завернут, хотя в грудах запутывающего gloop, скрывая его внутри функции X509_load_cert_buf', а затем вызывал это очень косвенно с помощью 'X509_LOOKUP_load_buf'. Этот код не дает никакого преимущества по сравнению с исходным кодом OP, который просто называется 'X509_STORE_add_cert'. –

+0

Извините, что я предоставил «бесполезный» код! То, что я хотел сделать, это дублировать функцию проверки openssl, которая работает, как и мой код. Я не пытался оптимизировать его лучше, чем код OpenSSL. –

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.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

+0

Я не нашел функцию X590_STORE_add_cert() в OpenSSL, откуда она взялась? Обычно вы хотите использовать функцию SSL_CTX_load_verify_locations() с указанием пути к файлу PEM для проверки цепи сертификатов. – Paul

+0

http://www.umich.edu/~x509/ssleay/x509_store.html. Но вот втирание. Эти файлы не находятся на диске. Они уже хранятся в структурах x509. Мне не нужно писать их на диск, чтобы проверить их. Я? – clemej

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. Таким образом, устраняя необходимость решения г-на Эд, как видно выше.

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 (устройство) Эмитент : signature-ca Subject: device

Я хотел проверить сертификат устройства. Мой эквивалент ca.pem (wrt OP) содержал root-ca и sign-ca.

Функция X509_verify_cert нуждается в полной цепочке сертификатов до корня (root-ca & подписи-ca) в X509_store.

Ниже приведен код, который работает для меня. Проверки на возвращаемые значения были опущены, чтобы опустить код вниз.

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 не являются нулевыми, когда используются.