Vérifier par programme la chaîne de certificates à l’aide de l’API OpenSSL

Cela ressemble beaucoup à d’autres questions, mais celles que j’ai examinées n’ont pas de réponse ou ne posent pas tout à fait la même question. J’ai un certificate d’auto-signature auto-signé et deux autres certificates signés avec ce certificate d’autorité de certificateion. Je suis à peu près sûr que les certificates sont corrects, car ‘openssl verify’ fonctionne:

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

(Ce qui précède est de mémoire, je ne les ai pas devant moi, donc ça risque d’être légèrement décalé).

Maintenant, je veux vérifier les certificates par programme. J’ai une fonction d’utilité avec pseudocode ci-dessous:

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

Mon problème est que le code ci-dessus renvoie toujours «Échec de la recherche du certificate d’émetteur». Qu’est ce que j’ai mal fait? Je crois que je crée un nouveau magasin, ajoute le cacert, crée un nouveau contexte et ajoute le certificate enfant à vérifier dans le contexte avec un pointeur sur le magasin qui contient l’autorité de certificateion. De toute évidence, je fais quelque chose de mal, mais je ne sais pas quoi.

Des idées?

Mise à jour: Je sais que je peux sauvegarder ces certificates sur le disque et utiliser quelque chose comme X509_LOOKUP_file ou quelque chose de similaire. Je cherche une solution qui ne touche pas le disque inutilement.

Vous pouvez utiliser les routines de validation normales (voir Comment vérifier qu’une clé publique a été émise par votre autorité de certificateion privée? ), Comme le fait la fonction -verify dans OpenSSL. Vous devez créer une méthode de recherche (X509_LOOKUP_METHOD) semblable à X509_LOOKUP_file (), mais fonctionnant avec une chaîne de caractères au lieu d’un nom de fichier. Le code pour X509_LOOKUP_buffer () est le suivant.

Fichier d’en-tête 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 */ 

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

Routine en C ++ qui appelle les routines ci-dessus:

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

J’ai moi-même rencontré ce problème et commencé avec un code très proche de l’OP. Ma chaîne de certificates comprenait 3 certificates: Certificat 1 (racine-ca) Émetteur: root-ca Sujet: racine-ca Certificat 2 (signature-ca) Émetteur: root-ca Sujet: signature-ca Certificat 3 (périphérique) Émetteur: signature- ca Sujet: appareil

Je voulais vérifier le certificate de l’appareil. Mon équivalent ca.pem (par rapport à OP) contenait la racine-ca et la signature-ca.

La fonction X509_verify_cert nécessite la totalité de la chaîne de certificates jusqu’à la racine (root-ca & signature-ca) du magasin X509.

Ci-dessous, mon code qui fonctionne pour moi. Les contrôles sur les valeurs de retour ont été omis pour affiner le code.

 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)); //ssortingng 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; } 

Je n’avais pas besoin de créer de méthodes de recherche. La clé pour moi consistait à parcourir en mémoire mes certificates à partir de ma chaîne, de sorte que je disposais de tous les certificates nécessaires pour compléter la chaîne. La chaîne est équivalente à celle que j’aurais introduite dans openssl verify pour l’option -CAfile.

Assurez-vous également que vos pointeurs X509 ne sont pas nuls lorsqu’ils sont utilisés.

Une réponse possible (ne pas avoir les points de SSL_CTX_load_verify_locations(3) pour append un commentaire, désolé): la page de SSL_CTX_load_verify_locations(3) de SSL_CTX_load_verify_locations(3) indique,

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

(Si vous ne faites pas correspondre les parens, pas les miens.)

Ce qui semble vouloir dire que, au SSL_CTX_load_verify_locations(3) de SSL_CTX_load_verify_locations(3) , il devrait être possible d’utiliser SSL_CTX_add_extra_chain_cert(3) ou SSL_CTX_use_certificatee(3) – les deux prenant un X509 * arg. Ceci évite ainsi la nécessité de la solution de M. Ed, comme indiqué ci-dessus.

Veuillez jeter un coup d’œil à la fonction SSL_CTX_load_verify_locations () : http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html

SSL_CTX_load_verify_locations () spécifie les emplacements de ctx, où sont situés les certificates de l’autorité de certificateion à des fins de vérification. Les certificates disponibles via CAfile et CApath sont approuvés.

Vous pouvez générer un fichier de certificate d’autorité de certificateion contenant les deux fichiers 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 

Et puis définissez la variable CAfile pour qu’elle pointe vers le fichier CAfile.pem .

J’espère que ça aide !

Je pense que vous pouvez utiliser “X509_STORE_set_verify_cb” pour append un rappel afin d’identifier l’erreur réelle:

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

À moins de connaître le code d’erreur, il est difficile de deviner le problème. Le code a l’air OK sinon.