tls - Verifique programáticamente la cadena de certificados utilizando la API de OpenSSL
verificar certificado ssl openssl (5)
Esto es muy similar a otras preguntas, pero las que he examinado no tienen una respuesta o no hacen la misma pregunta. Tengo un certificado de CA autofirmado y otros dos certificados que están firmados con ese certificado de CA. Estoy bastante seguro de que los certificados son correctos, porque ''openssl Verify'' funciona:
$ openssl verify -CAfile ca.pem server.pem
server.pem: OK
(Lo anterior es de memoria, no los tengo delante de mí, por lo que puede estar un poco apagado).
Ahora quiero verificar los certificados programáticamente. Tengo una función de utilidad con pseudocódigo a continuación:
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 */
}
Mi problema es que el código anterior siempre devuelve ''no se pudo encontrar el certificado del emisor''. ¿Qué he hecho mal? Creo que estoy creando una nueva tienda, agregando el cacert, creando un nuevo contexto, y agregando el certificado de niño para ser verificado al contexto con un puntero a la tienda que contiene la CA. Obviamente estoy haciendo algo mal, pero no estoy seguro de qué.
¿Algunas ideas?
Actualización: soy consciente de que puedo guardar estos certificados en el disco y usar algo como X509_LOOKUP_file o algo similar. Estoy buscando una solución que no toque el disco innecesariamente.
Consulte la función SSL_CTX_load_verify_locations ()
: http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html
SSL_CTX_load_verify_locations () especifica las ubicaciones para ctx, en las que se encuentran los certificados de CA para fines de verificación. Los certificados disponibles a través de CAfile y CApath son de confianza.
Puede generar un archivo de certificado de CA que contenga tanto 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
Y luego configure la variable CAfile
para que apunte al archivo CAfile.pem
.
Espero eso ayude !
Creo que puedes usar "X509_STORE_set_verify_cb" para agregar una devolución de llamada para identificar el error real:
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 menos que sepamos el código de error, es difícil adivinar el problema real. El código de lo contrario se ve bien.
Encontré este problema yo mismo y comencé con un código muy cercano al OP. Mi cadena de certificados incluyó 3 certificados: Certificado 1 (raíz-ca) Emisor: raíz-ca Asunto: raíz-ca Certificado 2 (firma-ca) Emisor: raíz-ca Asunto: firma-ca Certificado 3 (dispositivo) Emisor: firma- Ca Asunto: dispositivo
Quería verificar el certificado del dispositivo. Mi equivalente de ca.pem (wrt OP) contenía la raíz-ca y la firma-ca.
La función X509_verify_cert necesita toda la cadena de certificados hasta la raíz (raíz-ca y firma-ca) en la tienda X509.
A continuación se muestra mi código que funciona para mí. Se omitieron los controles de los valores de retorno para reducir el código.
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;
}
No necesitaba crear ningún método de búsqueda. La clave para mí era hacer un bucle a través de mis certificados de mi cadena en la memoria, así que tenía todos los certificados que necesitaba para completar la cadena. La cadena es equivalente a lo que habría alimentado en openssl verificar para la opción -CAfile.
Además, asegúrese de que sus punteros X509 no sean nulos cuando se utilicen.
Puede usar las rutinas de validación normales (consulte ¿Cómo verifica que su CA privada emitió una clave pública? ), Como lo hace la función -verify en OpenSSL. Debe crear un método de búsqueda (X509_LOOKUP_METHOD) como X509_LOOKUP_file (), pero que funciona con una cadena de caracteres en lugar de un nombre de archivo. El código para X509_LOOKUP_buffer () es el siguiente.
Archivo de cabecera por_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 */
El programa 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;
}
Rutina en C ++ que llama a las rutinas anteriores:
#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);
}
Una posible respuesta (no tiene puntos de representación para agregar un comentario, lo siento): la página del SSL_CTX_load_verify_locations(3)
de 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).
(Si no coinciden con los de ellos, no con los míos).
Lo que parece significar que, como alternativa a SSL_CTX_load_verify_locations(3)
, debería ser posible usar SSL_CTX_add_extra_chain_cert(3)
o SSL_CTX_use_certificate(3)
- ambos de los cuales tienen una X509 *
arg. De este modo, se evita la necesidad de una solución del Sr. Ed como se ve arriba.