2013-04-30 23 views
18

Questo è molto simile ad altre domande, ma quelle che ho visto o non hanno una risposta o non pongono la stessa domanda. Ho un certificato CA autofirmato e altri due certificati firmati con quel certificato CA. Sono abbastanza sicuro i certificati siano corrette, perché 'OpenSSL verificare' funziona:Verificare la catena di certificati in modo programmatico utilizzando l'API OpenSSL

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

(Quanto sopra è a memoria, io non li ho davanti a me, quindi potrebbe essere un po 'fuori).

Ora voglio verificare i certificati in modo programmatico. Ho una funzione di utilità con pseudocodice di seguito:

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 */ 
} 

Il mio problema è che il codice precedente restituisce sempre 'non è riuscito a trovare il certificato emittente'. Cosa ho fatto di sbagliato? Credo di creare un nuovo negozio, aggiungere il cacert, creare un nuovo contesto e aggiungere il certificato figlio da verificare al contesto con un puntatore allo store che contiene la CA. Ovviamente sto facendo qualcosa di sbagliato, ma non sono sicuro di cosa.

Qualche idea?

Aggiornamento: Sono consapevole di poter salvare questi certificati su disco e utilizzare qualcosa come X509_LOOKUP_file o qualcosa di simile. Sto cercando una soluzione che non tocchi il disco inutilmente.

+0

Sto affrontando lo stesso problema: hai trovato ancora qualche soluzione? –

+0

@ koch.trier no, sfortunatamente no. L'ho messo nel dimenticatoio per ora e mi sto concentrando su altre cose. Sto ancora cercando una risposta qui. – clemej

+0

possibile duplicato di [verifica del certificato x509 in C] (http://stackoverflow.com/questions/2756553/x509-certificate-verification-in-c) – jww

risposta

12

È possibile utilizzare le normali routine di convalida (vedere How do you verify a public key was issued by your private CA?), come fa la funzione -verify in OpenSSL. È necessario creare un metodo di ricerca (X509_LOOKUP_METHOD) come X509_LOOKUP_file(), ma che funziona con una stringa di caratteri anziché con un nome file. Il codice per X509_LOOKUP_buffer() è il seguente.

intestazione del file 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 */ 

Il by_buffer.c programma 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; 
} 

di routine in C++ che chiama le routine di cui sopra:

#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

Questo è un sacco di lavoro per definire un meccanismo di callback completamente nuovo. Ma sembra anche essere l'unico modo. Punti bonus per il codice di esempio completo. – clemej

+6

Questo codice è "corretto" ma tutto è completamente inutile! La chiamata centrale in questo codice è 'X509_STORE_add_cert', che è esattamente la stessa chiamata API che l'OP stava usando originariamente. È stato avvolto in pile di oscurità nascosta nascondendolo all'interno della funzione 'X509_load_cert_buf', che poi lo chiamava in modo molto indiretto usando' X509_LOOKUP_load_buf'. Questo codice non offre alcun vantaggio sul codice originale dell'OP che ha chiamato direttamente 'X509_STORE_add_cert'. –

+0

Mi dispiace che ho fornito il codice "inutile"! Quello che volevo fare era duplicare la funzione di verifica di openssl, che funziona, così come il mio codice. Non ho fatto alcun tentativo di ottimizzarlo meglio del codice di OpenSSL. –

2

Si prega di dare un'occhiata a SSL_CTX_load_verify_locations() funzione: 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.

è possibile generare un file di certificato CA contenente sia server.pem ca.pem:

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

e quindi impostare CAfile variabile per puntare al file CAfile.pem.

Spero che aiuti!

+1

Sono confuso, perché dovrei specificare un percorso (file o dir) quando sto caricando i certificati esplicitamente usando add_cert? – clemej

+0

Non ho trovato alcuna funzione X590_STORE_add_cert() in OpenSSL, da dove proviene? Di solito si desidera utilizzare la funzione SSL_CTX_load_verify_locations() con un percorso al file PEM per verificare la catena di certificati. – Paul

+0

http://www.umich.edu/~x509/ssleay/x509_store.html. Ma ecco il problema. Questi file non sono su disco. Sono già in memoria nelle strutture x509. Non avrei davvero bisogno di scriverli sul disco solo per verificarli .. vero? – clemej

3

A possibile risposta (non ho i punti di rep per aggiungere un commento, mi dispiace): il pagina di manuale di SSL_CTX_load_verify_locations(3) dice,

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

(fallimento del match parentesi loro, non mio.)

che sembra significare che, in alternativa al SSL_CTX_load_verify_locations(3), dovrebbe essere possibile utilizzare SSL_CTX_add_extra_chain_cert(3) o SSL_CTX_use_certificate(3) - entrambi i quali prendere un ARG X509 *. Evitando così la necessità della soluzione del signor Ed come visto sopra.

2

penso, è possibile utilizzare "X509_STORE_set_verify_cb" per aggiungere un callback per identificare l'errore effettivo:

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 */ 
} 

A meno che non conosciamo il codice di errore è difficile indovinare il problema reale. In caso contrario, il codice sembra OK.

2

Ho riscontrato questo problema e ho iniziato con codice molto vicino all'OP. La mia catena di certificati includeva 3 certificati: Certificato 1 (root-ca) Emittente: root-ca Oggetto: root-ca Certificato 2 (firma-ca) Emittente: root-ca Oggetto: firma Certificato 3 (dispositivo) Emittente : signing-ca Oggetto: dispositivo

Volevo verificare il certificato del dispositivo. Il mio equivalente in ca.pc (wrt OP) conteneva root-ca e sign-ca.

La funzione X509_verify_cert richiede l'intera catena di certificati fino alla radice (root-ca & signing-ca) nello X509_store.

Di seguito è il mio codice che funziona per me. I controlli sui valori di ritorno sono stati omessi per minimizzare il codice.

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

Non ho bisogno di creare alcun metodo di ricerca. La chiave per me era il loop dei miei certificati dalla mia stringa in memoria, quindi avevo tutti i certificati necessari per completare la catena. La stringa equivale a ciò che avrei inserito in openssl verify per l'opzione -CAfile.

Inoltre, assicurarsi che i puntatori X509 non siano nulli quando vengono utilizzati.