anterior | índice | siguiente |
Ya tenemos cargado nuestro certificado. ¿Y ahora qué?.
Nada, ya no hay más posibilidades...Es broma. Ya que hemos cargado
nuestro certificado sería interesante poder obtener algunos de sus
datos. Vamos por partes.
Un aspecto principal a la hora de trabajar con los certificados es
saber su número de versión (por si queremos saber si un certificado
incluye la foto del perro o no, será más fácil ver
si es una versión 3 o no, en vez de ir buscando por todo el certificado
la foto;-) ). Otro aspecto que puede ser interesante de cara a ser una
Autoridad Certificadora es conocer el número de serie de un certificado,
pues éstos no se pueden repetir dentro de la autoridad. Para ello
disponemos de dos funciones:
// En este ejemplo vamos a leer el número de serie de un certificado y a // obtener su numero de version. // La sintaxis es: // verserial nombre // // Ejemplo realizado por Jose Traver - jose@nisu.org #include <openssl/x509.h> #include <openssl/pem.h> #include <openssl/err.h> #include <openssl/asn1.h> #include <stdio.h> int main (int argc, char ** argv) { FILE * fp; X509 * x; long num_serie,version; ASN1_INTEGER * serial; // Abrimos el fichero if ((fp =fopen(argv[1],"r")) == NULL) { perror("ERROR al leer el fichero con el certificado"); exit(1); } // Leemos el certificado x = PEM_read_X509 (fp,NULL,NULL,NULL); // Obtenemos el numero de version. Este número esta en hexadecimal // y comienza a contar por 0. si obtenemos un 2 es que es la version 3. version=X509_get_version(x); // Obtenemos el numero de serie del certificado serial=X509_get_serialNumber(x); num_serie=ASN1_INTEGER_get(serial); printf("El certificado es de la version %d\n",version); printf("El numero de serie del certificado es %d\n",num_serie); fclose(fp); X509_free(x); } |
Ejemplo 3. Obtención del número de serie y de la versión de un X509
Otro aspecto a tener en cuenta es la validez de los certificados. La forma más sencilla (aunque no del todo efectiva) es usar las fecha de validación. Un certificado tiene dos fechas entre las cuales es válido. Ni antes ni después puede ser usado con seguridad. En un nivel superior, también entrarían en jugo las CRL o listas de certificados revocados y su compleja actualización, pero eso lo dejamos para mas adelante. De momento, las fechas de validez del certificado. Si queremos algo temporal, ajustamos el periodo de tiempo y ya está, incluso podemos dar validez a un certificado por unos
Hay dos campos para esto en el certificado llamados NotBefore y NotAfter. Estos dos campos, expresados en segundos y en estandar ASN1_UTCTIME. Estas dos funciones tienen la siguiente estructura:
Como todas las estructuras ASN1, manejarse con esta no es fácil. Se apoya en la estructura definida en la libreria comun <time.h> que se llama time_t, aunque tambien sería posible usar la estructura struct tm. Además parece haber una discrepancia entre las horas internas y las GMT. Podemos ver como obtener las fechas de validez del certificado en el siguiente ejemplo:
// En este ejemplo vamos a ver las fechas de validez de un certificado x509 // La sintaxis es: // verfechas nombre // // Ejemplo realizado por Jose Traver - jose@nisu.org #include <openssl/x509.h> #include <openssl/pem.h> #include <openssl/err.h> #include <stdio.h> #include <time.h> time_t ASN1_UTCTIME_get(const ASN1_UTCTIME *s) { struct tm tm; int offset; memset(&tm,'\0',sizeof tm); #define g2(p) (((p)[0]-'0')*10+(p)[1]-'0') tm.tm_year=g2(s->data); if(tm.tm_year < 50) tm.tm_year+=100; tm.tm_mon=g2(s->data+2)-1; tm.tm_mday=g2(s->data+4); tm.tm_hour=g2(s->data+6); tm.tm_min=g2(s->data+8); tm.tm_sec=g2(s->data+10); if(s->data[12] == 'Z') offset=0; else { offset=g2(s->data+13)*60+g2(s->data+15); if(s->data[12] == '-') offset= -offset; } #undef g2 return mktime(&tm)-offset*60; } int main (int argc, char ** argv) { FILE * fp; X509 * x; ASN1_UTCTIME * tm; time_t antes, despues; char * datos; // Abrimos el fichero if ((fp =fopen(argv[1],"r")) == NULL) { perror("ERROR al leer el fichero con el certificado"); exit(1); } // Leemos el certificado x = PEM_read_X509 (fp,NULL,NULL,NULL); // Leemos las fechas tm=X509_get_notBefore(x); antes=ASN1_UTCTIME_get(tm); datos=ctime(&antes); printf("Valido desde %s",datos); tm=X509_get_notAfter(x); despues=ASN1_UTCTIME_get(tm); datos=ctime(&despues); printf("Valido hasta %s",datos); fclose(fp); X509_free(x); } |
Ejemplo 4. Obtención de las fechas de validez de un X509
Bueno, pues vamos a ver uno de los aspectos más complejos a la hora de obtener información de un certificado X509: los datos personales. Con ello me refiero tanto a los datos personales del Issuer como del Subject. Antes hemos visto el código usado para definir cada campo, sólo que no se almacena por campos sino en una pila (espero que sea por tener una ordenación más estable y para que se ajuste al tamaño mínimo, por que si no es así...vaya forma de complicarse). Para ello vamos a utilizar las siguientes funciones:
No se asuste, porque de momento vamos a ver una forma sencilla de obtener los datos y mostrarlos por pantalla. Una posible implementación es la que se puede ver en el ejemplo 5:
// En este ejemplo vamos a ver los datos del Subject y del issuer de un // certificado x509. // La sintaxis es: // vernombres nombre // // Ejemplo realizado por Jose Traver - jose@nisu.org #include <openssl/x509.h> #include <openssl/pem.h> #include <openssl/err.h> #include <stdio.h> int main (int argc, char ** argv) { FILE * fp; X509 * x; X509_NAME * nombre; char * texto; // Abrimos el fichero if ((fp =fopen(argv[1],"r")) == NULL) { perror("ERROR al leer el fichero con el certificado"); exit(1); } // Leemos el certificado x = PEM_read_X509 (fp,NULL,NULL,NULL); // Nombre del subject nombre=X509_get_subject_name(x); texto=malloc(sizeof(x->cert_info)); texto=X509_NAME_oneline(nombre,texto,200); printf("Datos subject: %s\n",texto); // Nombre del issuer nombre=X509_get_issuer_name(x); texto=X509_NAME_oneline(nombre,texto,200); printf("Datos issuer: %s\n",texto); fclose(fp); X509_free(x); } |
Ejemplo 5. Obtención de los datos del subject e issuer de un X509
La estructura X509_NAME guarda la pila de datos del issuer o del subject en elementos individuales del tipo X509_NAME_ENTRY que están asociados al valor pertinente. Gracias a esta estructura es muy facil añadir nuevos campos al certificado y que su tamaño sea siemrpe el justo. También evitamos redefinir la estructura del X509_NAME.