2013-04-30 12 views
18

Das ist sehr ähnlich zu anderen Fragen, aber die, die ich mir angesehen habe, haben entweder keine Antwort oder stellen nicht die gleiche Frage. Ich habe ein selbstsigniertes CA-Zertifikat und zwei weitere Zertifikate, die mit diesem CA-Zertifikat signiert sind. Ich bin ziemlich sicher, dass die Zertifikate korrekt sind, weil ‚openssl überprüfen‘ funktioniert:Programmatische Überprüfung der Zertifikatskette mit OpenSSL API

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

(Die oben aus dem Speicher ist, ich habe sie nicht vor mir, so kann es leicht ausgeschaltet werden).

Jetzt möchte ich die Zertifikate programmatisch überprüfen. Ich habe eine Nutzenfunktion mit Pseudo-Code unten:

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

Mein Problem ist, dass der obige Code gibt immer ‚failed Aussteller-Zertifikat zu finden.‘ Was habe ich falsch gemacht? Ich glaube, dass ich einen neuen Speicher anlege, den cacert hinzufüge, einen neuen Kontext schaffe und das zu überprüfende Kind-Zertifikat dem Kontext mit einem Zeiger auf den Speicher hinzufüge, der die CA enthält. Ich mache offensichtlich etwas falsch, aber ich bin mir nicht sicher was.

Irgendwelche Ideen?

Update: Ich bin mir bewusst, dass ich diese Zertifikate auf der Festplatte speichern und etwas wie X509_LOOKUP_file oder etwas ähnliches verwenden kann. Ich suche nach einer Lösung, die die Festplatte nicht unnötig berührt.

+0

Ich stehe vor dem gleichen Problem - haben Sie schon eine Lösung gefunden? –

+0

@ koch.trier nein, leider nicht. Ich habe es vorerst auf Eis gelegt und mich auf andere Dinge konzentriert. Ich bin immer noch auf der Suche nach einer Antwort hier. – clemej

+0

mögliches Duplikat von [x509 Zertifikatsüberprüfung in C] (http://stackoverflow.com/questions/2756553/x509-certificate-verification-in-c) – jww

Antwort

12

Sie können die normalen Validierungsroutinen verwenden (siehe How do you verify a public key was issued by your private CA?), wie die Funktion -verify in OpenSSL. Sie müssen eine Suchmethode (X509_LOOKUP_METHOD) wie X509_LOOKUP_file() erstellen, die jedoch mit einer Zeichenfolge anstelle eines Dateinamens arbeitet. Der Code für X509_LOOKUP_buffer() lautet wie folgt.

Header-Datei 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-Programm 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; 
} 

Routine in C++, die die obigen Routinen aufruft:

#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

Das ist eine Menge Arbeit, um einen ganz neuen Callback-Mechanismus zu definieren. Aber es scheint auch der einzige Weg zu sein. Bonuspunkte für den umfangreichen Beispielcode. – clemej

+6

Dieser Code ist "korrekt", aber alles ist völlig nutzlos! Der zentrale Aufruf in diesem Code ist 'X509_STORE_add_cert', der genau derselbe API-Aufruf ist, den das OP ursprünglich verwendet hat. Es wurde jedoch in Haufen von verschleierndem Gloop eingepackt, indem es in der 'X509_load_cert_buf'-Funktion versteckt wurde und dann sehr indirekt mit' X509_LOOKUP_load_buf' aufgerufen wurde. Dieser Code bietet keinen Vorteil gegenüber dem ursprünglichen Code des OPs, der direkt 'X509_STORE_add_cert' genannt wird. –

+0

Sorry, dass ich "nutzlosen" Code zur Verfügung gestellt habe! Was ich tun wollte, war, die openssl Verify-Funktion zu duplizieren, was genauso funktioniert wie der MY-Code. Ich habe nicht versucht, es besser als OpenSSL-Code zu optimieren. –

2

Bitte nehmen Sie sich einen Blick auf SSL_CTX_load_verify_locations() Funktion: 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.

Sie können ein CA-Zertifikat-Datei erzeugen beide ca.pem server.pem enthält:

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

und legen Sie dann CAfile Variable-zu-Punkt zu CAfile.pem Datei.

Hoffe es hilft!

+1

Ich bin verwirrt, warum müsste ich einen Speicherort (Datei oder Verzeichnis) angeben, wenn ich die Zertifikate explizit mit add_cert lade? – clemej

+0

Ich habe keine X590_STORE_add_cert() - Funktion in OpenSSL gefunden, woher kommt es? Normalerweise möchten Sie die Funktion SSL_CTX_load_verify_locations() mit einem Pfad zur PEM-Datei verwenden, um die Zertifikatskette zu überprüfen. – Paul

+0

http://www.umich.edu/~x509/ssleay/x509_store.html. Aber hier ist der Haken. Diese Dateien befinden sich nicht auf der Festplatte. Sie sind bereits in x509-Strukturen im Speicher. Ich sollte sie nicht wirklich auf die Festplatte schreiben müssen, nur um sie zu überprüfen. – clemej

3

A möglich Antwort (nicht die Rep-Punkte, um einen Kommentar hinzuzufügen, tut mir leid): die Manpage SSL_CTX_load_verify_locations(3) sagt

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

(Failure Pars ihnen übereinstimmen, nicht meine.)

Welche, dass als Alternative zu SSL_CTX_load_verify_locations(3) zu bedeuten scheint, sollte es möglich sein SSL_CTX_add_extra_chain_cert(3) oder SSL_CTX_use_certificate(3) zu verwenden - die beide eine X509 * arg nehmen. Damit entfällt die oben erwähnte Lösung von Herrn Ed.

2

Ich denke, können Sie „X509_STORE_set_verify_cb“ verwenden, um einen Rückruf zu fügen, den tatsächlichen Fehler zu identifizieren:

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

Wenn wir den Fehlercode wissen, dass es schwierig ist, das eigentliche Problem zu erraten. Der Code sieht ansonsten OK aus.

2

Ich bin selbst auf dieses Problem gestoßen und habe mit Code begonnen, der dem OP sehr nahe kommt. Meine Zertifikatskette enthält 3 Zertifikate: Zertifikat 1 (root-ca) Aussteller: root-ca Betreff: root-ca Zertifikat 2 (signing-ca) Aussteller: root-ca Betreff: signing-ca Zertifikat 3 (Gerät) Emittent : signing-ca Betreff: Gerät

Ich wollte das Gerätezertifikat verifizieren. Mein ca.pem-Äquivalent (mit OP) enthielt das root-ca und signing-ca.

Die Funktion X509_verify_cert benötigt die gesamte Zertifikatskette bis zum Stammverzeichnis (root-ca & signing-ca) im X509_store.

Unten ist mein Code, der für mich funktioniert. Checks auf Rückgabewerte wurden weggelassen, um den Code zu lehnen.

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

Ich musste keine Lookup-Methoden erstellen. Der Schlüssel für mich war das Durchlaufen meiner Zertifikate von meinem String im Speicher, so dass ich alle Zertifikate hatte, die ich brauche, um die Kette zu vervollständigen. Der String entspricht dem, was ich in openssl für die Option -CAfile eingegeben hätte.

Stellen Sie außerdem sicher, dass Ihre X509-Zeiger nicht null sind, wenn sie verwendet werden.

Verwandte Themen