anterior índice siguiente

Obtener datos de un certificado

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:

La tercera función se usa para obtener un valor "manejable" del dígito obtenido por la segunda función. Como todo se hace usando convenios ASN1 la obtención de los datos puede ser un poco complicada, aunque se supone que así están mejor controlados. Veamos el resultado de estos comandos
// 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.


anterior índice siguiente