INDICE GENERAL


¿Qué es el x509?

En la era de la comunicación por internet, los usuarios buscan la posibilidad de poder tener algo que acredite su identidad así como asegurar que lo que hacen no será modificado (integridad). Esto se consigue con los sistemas de criptografía de clave pública.

El mayor logro en la estandarización de la seguridad es la adopción del la certificación X509. El X509 aporta los aspectos seguros y especifica las estructuras de datos para las claves seguras y las listas de rechazo, así como las metodologías para manipular y certificar los certificados a nivel de sistema usando las CA.

En el modelo de información extendido por la ISO y la ITU como certificado básico (con información de la empresa(issuer) y el titular) para apoyar en muchos aspectos la manipulación de los certificados y las listas de certificados rechazables desde la perspectiva de las autoridades certificadoras.

Los elementos de información importantes no son las claves criptográficas sino los certificados X509 por los que se transmiten estas claves públicas y las listas de certificados rechazables aplicadas cuando lcertificados ya no son válidos. Generalmente los certificados suelen estar almacenados en formato PEM, que es la codificación DER en base 64 con cabecera y cola añadidos. Para más información sobre el almacenamiento de un certificado X509 respecto a la integración de claves y otras cosas puedes consultar el tutorial sobre PEM en http://spisa.act.uji.es/~mario


¿Para qué se utiliza el x509?

Un certificado X509 permite realizar varias acciones sobre él, o transformarlo. Puede usarse para ver la información sobre los certificados, convertir los certificados a otros tipos, firmar peticiones de certificado, o editar las opciones de confianza o aceptabilidad de un certificado.

En la librería openSSL existen varios archivos dedicados a crear, acceder o modificar los diferentes objetos relacionados con el x509. estos tipos de objetos son básicamente de tres tipos:

El X509 puede aparecer en dos formatos. Uno de ellos es la versión estándar, que es la definda en un principio. La otra consiste en añadir una sere de extensiones a los certificados para mejorar la información disponible. Entre la información añadida a esta segunda versión (versión v3) están: Las listas de certificados rechazables son una importante función de la política de seguridad. La posibilidad de rechazar claves o certificados en caso de ser perdidos, caducar temporalmente, cese del servicio para el que fueron creados, o el mal uso de su persona titular es esencial. Las listas de certificados rechazables están firmadas por la empresa certificadora, indicando los periodos de renovación, los certificados rechazados, etc. Esto puede presentar un grave problema a la hora de confiar enun certificado pues la actualización de estas listas es un tema complicado.

En la versión v3 también se añaden una serie de campos de información adicional en las listas: identidad de la clave de la autoridad, atributos de la empresa, número de CRL, razón del rechazo, fecha final, etc.

El X509 puede usarse para firmar certificados y peticiones en dos modos posibles. Estos modos son la autofirma mediante nuestra propia clave privada o el uso de un certificado "fiable" que actue como Autoridad Certificadora.

En cuanto a la fiabilidad de un certificado, podemos hablar de certificados fiables(trusted certificate) que es un certificado normal con alguna información adicional como los usos permitidos o prohibidos del certificado o los alias. Cuando un certificado está siendo verificado, por lo menos un certificado debe ser fiable. Por defecto, un certificado fiable debe estar almacenado localmente y con el Root como propietario.


El x509 bajo openSSL

Archivos de openSSL

Las diferentes funciones utilizadas para interactuar con los objetos x509 están agrupadas por sus acciones en diferentes archivos, por lo que cada archivo tiene una finalidad de trabajo.Más tarde veremos ejemplos con los que trabajar, pero de momento veamos el uso al que se destina cada fichero:

Macros y funciones de ayuda

El uso de funciones macro simplifica mucho la manipulación de objetos x509, por eso existen una serie de operaciones predefinidas que operan con mayor comodidad con los datos. Las funciones "macro" son utilizadas en el anteriormente citado x_all.c, que se pueden observar a continuación:
        int X509_verify(X509 *a, EVP_PKEY *r)
        int X509_REQ_verify(X509_REQ *a, EVP_PKEY *r)
        int X509_CRL_verify(X509_CRL *a, EVP_PKEY *r)
        int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *a, EVP_PKEY *r)
        int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
        int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md)
        int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md)
        int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md)
        X509_ATTRIBUTE *X509_ATTRIBUTE_dup(X509_ATTRIBUTE *xa)
        X509 *X509_dup(X509 *x509)
        X509_EXTENSION *X509_EXTENSION_dup(X509_EXTENSION *ex)
        X509 *d2i_X509_fp(FILE *fp, X509 **x509)
        int i2d_X509_fp(FILE *fp, X509 *x509)
        X509 *d2i_X509_bio(BIO *bp, X509 **x509)
        int i2d_X509_bio(BIO *bp, X509 *x509)
        X509_CRL *X509_CRL_dup(X509_CRL *crl)
        X509_CRL *d2i_X509_CRL_fp(FILE *fp, X509_CRL **crl)
        int i2d_X509_CRL_fp(FILE *fp, X509_CRL *crl)
        X509_CRL *d2i_X509_CRL_bio(BIO *bp, X509_CRL **crl)
        int i2d_X509_CRL_bio(BIO *bp, X509_CRL *crl)
        PKCS7 *PKCS7_dup(PKCS7 *p7)
        PKCS7 *d2i_PKCS7_fp(FILE *fp, PKCS7 **p7)
        int i2d_PKCS7_fp(FILE *fp, PKCS7 *p7)
        PKCS7 *d2i_PKCS7_bio(BIO *bp, PKCS7 **p7)
        int i2d_PKCS7_bio(BIO *bp, PKCS7 *p7)
        X509_REQ *X509_REQ_dup(X509_REQ *req)
        X509_REQ *d2i_X509_REQ_fp(FILE *fp, X509_REQ **req)
        int i2d_X509_REQ_fp(FILE *fp, X509_REQ *req)
        X509_REQ *d2i_X509_REQ_bio(BIO *bp, X509_REQ **req)
        int i2d_X509_REQ_bio(BIO *bp, X509_REQ *req)
        RSA *RSAPublicKey_dup(RSA *rsa)
        RSA *RSAPrivateKey_dup(RSA *rsa)
        RSA *d2i_RSAPrivateKey_fp(FILE *fp, RSA **rsa)
        int i2d_RSAPrivateKey_fp(FILE *fp, RSA *rsa)
        RSA *d2i_RSAPublicKey_fp(FILE *fp, RSA **rsa)
        int i2d_RSAPublicKey_fp(FILE *fp, RSA *rsa)
        RSA *d2i_RSAPrivateKey_bio(BIO *bp, RSA **rsa)
        int i2d_RSAPrivateKey_bio(BIO *bp, RSA *rsa)
        RSA *d2i_RSAPublicKey_bio(BIO *bp, RSA **rsa)
        int i2d_RSAPublicKey_bio(BIO *bp, RSA *rsa)
        DSA *d2i_DSAPrivateKey_fp(FILE *fp, DSA **dsa)
        int i2d_DSAPrivateKey_fp(FILE *fp, DSA *dsa)
        DSA *d2i_DSAPrivateKey_bio(BIO *bp, DSA **dsa)
        int i2d_DSAPrivateKey_bio(BIO *bp, DSA *dsa)
        X509_ALGOR *X509_ALGOR_dup(X509_ALGOR *xn)
        X509_NAME *X509_NAME_dup(X509_NAME *xn)
        X509_NAME_ENTRY *X509_NAME_ENTRY_dup(X509_NAME_ENTRY *ne)
        int X509_digest(X509 *data, EVP_MD *type, unsigned char *md,
        int X509_NAME_digest(X509_NAME *data, EVP_MD *type, unsigned char *md,
        int PKCS7_ISSUER_AND_SERIAL_digest(PKCS7_ISSUER_AND_SERIAL *data, EVP_MD *type,
        X509_SIG *d2i_PKCS8_fp(FILE *fp, X509_SIG **p8)
        int i2d_PKCS8_fp(FILE *fp, X509_SIG *p8)
        X509_SIG *d2i_PKCS8_bio(BIO *bp, X509_SIG **p8)
        int i2d_PKCS8_bio(BIO *bp, X509_SIG *p8)
        PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp,
        int i2d_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, PKCS8_PRIV_KEY_INFO *p8inf)
        PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *bp,
        int i2d_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, PKCS8_PRIV_KEY_INFO *p8inf)


Algunas funciones importantes



Ejemplos para comenzar

Veamos cómo trabajar con los certificados. Lo primero que podemos hacer, lógicamente, es crear un certificado. Vamos a crear un certificado vacío.
Ejemplo1
Un ejemplo del certificado creado puede ser este:

-----BEGIN CERTIFICATE-----
MCcwHTADBgEAMAIAADAEFwAXADACAAAwCDADBgEAAwEAMAMGAQADAQA=
-----END CERTIFICATE-----

Obsérvese como se cumple la especificación del formato PEM con la cabecera y el final marcando el verdadero certificado y todo con caracteres imprimibles.
Este certificado únicamente contiene las estructuras internas de las que está compuesto un objeto X509. Por eso, no tiene sentido tratar de leerlo.
Para seguir adelante, vamos a crear un certificado válido y perfectamente manejable mediante openssl:

Con esta instrucción hemos creado un certificado autofirmado que se almacena en el fichero cert.pem y un par de claves rsa.
Con openssl también es posible ver información del certificado que acabamos de crear. Para ello usamos: Aqui aparece mucha información, aunque de momento nos podemos fijar en los datos del Subject (titular) y del Issuer (certificador). Comprobamos que son los mismos. El X509 usa un sistema de códigos propio para indicar los datos: Estos campos corresponden a los necesarios en un certificado X509 versión 1. Sin embargo, a partir de la versión 3 ya es posible añadir todo tipo de campos extra en estos certificados, incluso la foto de su perro si le hace ilusión (sí, en serio, aunque requiere que el programa que use para leer los certificados lo soporte).

Bueno, pues ya hay un certificado totalmente funcional que hemos hecho con openssl. Pero, claro, queremos trabajar con él, asi que lo primero que tenemos que hacer es cargar el certificado.
Esto podemos realizarlo como en el Ejemplo 2.

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 el Ejemplo 3.

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 tiempoo y ya está, incluso podemos dar validez a un certificado por unos minutos tan sólo.
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 Ejemplo 4.

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

Bien, pues hemos visto como obtener datos de un certificado X509, pero ahora seguro que esta ansioso por poder manipularlos a su voluntad y que los cambios queden para la posteridad. Para ello será necesario cambiar los datos en el certificado a su antojo. Pues vamos a ver como podemos hacer esto. Usaremos las funciones siguientes:

Las dos funciones no necesitan mucha explicación. Es prácticamente el caso contrario de cuando leíamos estos datos. Podemos verlo en el Ejemplo 6.
Recordemos que con esto estamos cambiando los datos del certificado referentes a la parte del subject, pues la parte del issuer se considera como otro certificado al que no estamos accediendo, aunque sea autofirmado.
 

Esta vez usamos una variante respecto de lo que parece habitual. Aunque también se podrían utilizar las funciones X509_set_notBefore(X509 * x, ASN1_UTCTIME * tm) y X509_set_notAfter(X509 * x, ASN1_UTCTIME * tm), es mucho más comodo usar:

Podemos ver la implementación de esta funcionalidad en el Ejemplo 7. Esto lo que hace es añadir una cantidad de tiempo al que tiene el certificado actualmente, por supuesto en segundos (con valores positivos y negativos).

Uno de los datos fundamentales que posee un certificado es la clave pública del propietario. Esta clave pública es la que nos permitirá mandarle mensajes seguros pues la usamos para cifrar información que sólo puede ser descifrada con la clave privada que posee esa persona. Es el principio de criptografía de clave pública.
Para manejar este elemento contamos con dos funciones que nos añudan:

Así, podemos ver el resultado de obtener una clave de un certificado en el Ejemplo 8. Leemos un certificado, le extraemos su clave, escribiendo la clave. ¡Ojo! esa clave pública que hemos obtenido debe ir con la clave privada junto a la que fue generada para poder usarla con otros certificados (extaraño, no se me ocurren motivos), además de firmar el nuevo resultado. Si no se hace esto, la validez del certificado sería nula.

En cualquier caso, hemos de pensar que una personano se hace su certificado X509 (excepto si lo hace autofirmado, siendo esa persona mismo titular y autoridad certificadora). Es la Autoridad Certificadora quien recibe una petición de certificado y firma esa petición, creando un nuevo objeto x509 a partir de los datos de la petición y otros que posee la autoridad.
La Forma que tiene una persona de pedir un certificado es a través de un objeto PKCS 10 o también conocido como Petición de certificado (CSR, certificate signing request). Es misión de la autoridad certificadora asegurarse de los datos que certifica, de lo contrario sus certificados tendrían una validez pésima.
Para poder asegurarnos de que la clave del certificado es correcta o la hemos puesto bien, podemos usar la función X509_check_private_key, tal y como se puede ver en el Ejemplo 9, donde se lee un certificado X509 y su "supuesta" clave privada y se compara que sea la misma.
Para poder gestionar los objetos PKCS 10 y los X509, openssl proporciona dos funciones para pasar del uno al otro y viceversa. Estas funciones son:

La tercera función la utilizamos para mostrar por pantalla el certificado creado, pues al no estar completo el certificado , no puede ser mostrado por medio de la línea de comando mediante openssl. ¡Tranquilos, ya haremos un certificado completo dentro de muy poco!. En el caso de que tengamos una petición para crear un objeto X509 a partir de un objeto PKCS 10 podemos ver el Ejemplo 11. Sin embargo, también podemos hacer el paso contrario y obtener la petición de certificado a partir del propio certificado X509, como podemos ver en el Ejemplo 12. Ojo, porque el x509 del ejemplo 11 no es un certificado completo, pues faltan datos por rellenar en la estructura y firmarlo, pero el objeto PKCS 10 del ejemplo 12 si que es perfectamente válido, pues todos los campos de su estructura han sido rellenados a partir de los valores que ya exixten en el certificado X509.
Pero ya está bien de observar diferentes aspectos individuales de los certificados. Lo que queremos es hacer un certificado de pies a cabeza nosotros mismos, así que hemos de comenzar por tener una autoridad certificadora o ser nosotros mismos, es decir, generar un certificado autofirmado. Vamos a realizar esto último pues es lo más común si no queremos gastarnos el dinero en el certificado de una autoridad de pago para asegurar la confidencialidad de nuestro grupillo de amigos ;-). Una parte importante dentro del proceso de creación de un certificado es la firma que realiza la autoridad certificadora sobre el certificado, para ello se usa la siguiente función: donde EVP_MD * es el nombre de alguno de los algoritmos de firma usados (y cargados) por openssl. Por ejemplo, se pude firmar con EVP_md5().
A la hora de manipular nombres openssl utiliza una doble estructura, dentro del método ASN1. Por una parte, se usa la estructura X509_NAME como almacén de un conjunto de elementos básicos de nombres, los X509_NAME_ENTRY. La forma más sencilla de poner nombres sin tener que entrar en detalles de la libreria ASN1 es usa el método de inserción por texto. A partir de un nombre de elemento dentro del conjunto de X509_NAME, podemos asignarle un valor. Para ello usamos la siguiente función: La secuencia completa para crear un certificado desde el inicio es el siguiente:
  1. Iniciación de las variables oportunas (variable de clave pública, certificado, clave rsa, estructuras de nombres, ficheros, etc.).
  2. Generación de un par de claves RSA del tamaño que queramos (1024 bits suele ser una buena elección).
  3. Asignación del par RSA a la estructura de clave pública EVP_PKEY.
  4. Relleno de campos de número de versión, de serie y fechas de validez(desde el momento actual y por un año es una buena opción).
  5. Obtención y relleno de la estructura de nombres del certificado, tanto para el subject como para el issuer (si lo hacemos autofirmado).
  6. Firma del certificado.
  7. Escritura de la clave privada y del certificado.
Veamos, finalmente, como quedan todos estos pasos en el siguiente Ejemplo 10.
 



El autor del tutorial es JOSÉ TRAVER