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:
-
x509 certificate o x509(certificados x509)
-
Certificate Signing Request o CSR (petición de firma de certificado).
Esto esta definido como un objeto PKCS10.
-
Certificate Revokation List o CRL (lista de certificados rechazados)
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:
-
Identificación de la clave de la autoridad
-
Atributos de la clase
-
Políticas de certificación
-
Restricciones de uso de la clave
-
Alias del titular
-
Alias de la empresa(issuer)
-
Atributos del titular
-
Identidad de la autoridad certificadora
-
Fotos (¿y por qué no?, cualquier información puede
incluirse)
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.
-
Selfsigned:Si lo aplicamos sobre un certificado se ponen los datos
del titular como datos de la empresa(issuer), es decir, se autofirma, cambia
la clave pública a la dada y firma con la clave privada del titular;
cambia las fechas de validez: la de comienzo se pone a la fecha actual
y la de final de validez se ha de fijar mediante un parámetro de
días. Las extensiones se mantienen.Si lo aplicamos sobre una petición
de firma de un certificado, entonces crea un certificado autofirmado usando
la clave privada con el nombre del titular de la petición.
-
CA:Especifica que se use el certificado de una autoridad certificadora
(supestamente nosotros lo somos). En los datos de la empresa(issuer) se
ponen los del certificado de la CA y se firma con la clave de la 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:
x509_cmp.c
-
Funciones para comparar, comprobar y obtener información de elementos
de objetos x509, tales como nombre del sujeto, de la empresa, número
de serie, clave pública, etc.
x509_d2.c
-
Carga o define el camino y los nombres de los ficheros a utilizar.
x509_def.c
-
Funciones para obtener los valores establecidos por defecto.
x509_err.c
-
Define los errores con sus textos de salida de error asociados.
x509_ext.c
-
Obtiene certificados o sus extensiones mediante diferentes métodods
de búsqueda (NID, OBJ, etc), creación o modificación.
Estas funciones sólo son válidas para las versiones 3.0 de
SSL pues es cuando se añaden campos de extensiones, y se aplica
a certificados x509, x509 rechazados o CRLs.
x509_lu.c
-
funciones de búsqueda de los certificados por diferentes parámetros
(Subject, issuer serial, fingerprint, alias...) Los certificados estan
almacenados en forma de pila, por lo que también se añaden
funciones de comparación y añadido a la pila.
x509_obj.c
-
Paso de objetos x509 a tiras de caracteres.
x509_r2x.c
-
Paso de elementos x509_REQ (certificados x509 para firmar) a elementos
x509 (previa comprobación de claves).
x509_req.c
-
Paso de elementos x509 a elementos x509_REQ.
x509_set.c
-
Funciones para la asignación de datos
-
versión
-
número de serie
-
nombre de la compañía
-
nombre personal
-
Periodos de validez
-
clave pública
x509_txt.c
-
Texto a mostrar para los direfentes códigos de error.
x509_v3.c
-
Añadidos de información para la versión 3 del SSL,
usados en las funciones del x509_ext.c
x509_vfy.c
-
Funciones para realizar diversas comparaciones sobre los objetos x509
-
comprobar nombres, tiempo...
-
ajustar tiempo
-
obtener parámetros de la clave pública
-
añadir certificados x509 o CRL
-
consulta y modificación en el acceso a los objetos x509
-
verificación de certificados
x509_name.c
-
Funciones para la obtención y modificación de nombres.
x509_rset.c
-
Modificación de valores en un CSR tales como versión, nombre
o clave pública.
x509_type.c
-
Obtención del tipo de certificado con sus características
(codificado como un entero).
by_file.c y by_dir.c
-
Operaciones para leer/escribir objetos x509 mediante entrada estándar
o mediante BIO. Para más información sobre el BIO se puede
consultar la información proporcionada por Vicente
Roca en http://spisa.act.uji.es/~roca
, o si prefieres la entrada estándar(junto con algo también
de BIO) mira la página de Mario
Prats en http://spisa.act.uji.es/~mario
x_all.c
-
Mención aparte merece el fichero x_all.c, puesto que es un conjunto
de funciones que actuan como "macro" para facilitar la tarea de los programadores
(Eh! Esos somos nosotros?!) pues simplifican mucho el paso de parámetros
concretos a las funciones pasando grandes estrucuras de datos generales
y ya cogiendo en su interior lo que es necesario.
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
-
int X509_issuer_and_serial_cmp(X509 *a, X509 *b)
Compara los números de serie y la empresa(issuer) de dos certificados
x509.
-
int X509_CRL_cmp(X509_CRL *a,X509_CRL *b)
Compara dos listas CRL por la empresa(issuer) observando la misma procedencia.
-
X509_NAME *X509_get_issuer_name(X509 *a)
Devuelve el nombre de la empresa(issuer) en un puntero a X509_NAME.
-
int X509_set_issuer_name(X509 *x, X509_NAME *name)
Permite poner el nombre de la empresa (issuer) que esta en un formato
X509_NAME.
-
X509_NAME *X509_get_subject_name(X509 *a)
Devuelve el nombre del titular del certificado en un puntero a X509_NAME.
-
int X509_set_subject_name(X509 *x, X509_NAME *name)
Permite poner el nombre al titular del certificado en formato X509_NAME.
-
int X509_REQ_set_subject_name(X509_REQ *x, X509_NAME *name)
Permite poner el nombre del titular a un CSR.
-
ASN1_INTEGER *X509_get_serialNumber(X509 *a)
Devuelve el número de serie en un puntero ASN1_INTEGER.
-
int X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial)
Permite asignar el número de serie al certificado en formato
ASN1_INTEGER.
-
int X509_set_version(X509 *x, long version)
Permite fijar la versión del certificado (long).
-
int X509_REQ_set_version(X509_REQ *x, long version)
Permite poner la versión del CSR.
-
int X509_set_notBefore(X509 *x, ASN1_UTCTIME *tm)
int X509_set_notAfter(X509 *x, ASN1_UTCTIME *tm)
Fija las fechas límites mínima y máxima de validez
del certificado.
-
int X509_set_pubkey(X509 *x, EVP_PKEY *pkey)
Permite cambiar la clave pública del certificado.
-
int X509_REQ_set_pubkey(X509_REQ *x, EVP_PKEY *pkey)
Permite cambiar la clave pública del CSR.
-
int X509_check_private_key(X509 *x,EVP_PKEY *k)
Compara los tipos de la clave pública y la clave privada.
-
int X509_STORE_set_default_paths(X509_STORE *ctx)
Inicializa los caminos por defecto. Válido para STDIO.
-
int X509_STORE_load_locations(X509_STORE *ctx, const char *file,const
char *path)
Indica los caminos a utilizar. Válido para STDIO.
-
const char *X509_get_default_private_dir(void)
Obtiene el camino por defecto al directorio donde se guardan las claves
privadas.
-
const char *X509_get_default_cert_dir(void)
const char *X509_get_default_cert_file(void)
Devuelven los valores por defecto del directorio donde se guardan los
certificados y el nombre por defecto de un certificado.
-
X509 *X509_REQ_to_X509(X509_REQ *r, int days, EVP_Pkey *pkey)
X509_REQ *X509_to_X509_REQ(X509 *x, EVP_PKEY *pkey, EVP_MD *md)
Permiten obtener objetos X509 a partir de un X509_REQ y viceversa.
-
int X509_STORE_add_cert(X509_STORE *ctx, X509 *x)
Añade un certificado al conjunto de certificados guardados.
-
int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x)
Añade un objeto CRL al conjunto ya almacenado.
-
int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type)
Permite cargar el certificado del fichero file en la estructura de
certificados almacenados.
-
int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type)
Permite cargar el CRL del fichero file en la estructura de almacenamiento
de objetos X509.
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:
-
openssl req -new - x509 -out cert.pem -newkey rsa:1024 -nodes -keyout
key.pem
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:
-
openssl x509 -in cert.pem -noout -text
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:
-
C = País
-
ST = Estado / Provincia
-
L = Localidad
-
O = Organización
-
OU = Departamento
-
CN = Nombre del titular
-
Email = Dirección de correo electrónico
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:
-
long X509_get_version(X509 *x)
-
ASN1_INTEGER* X509_get_serialNumber(X509 *x)
-
long ASN1_INTEGER_get(ASN1_INTEGER *serial)
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:
-
ASN1_UTCTIME * X509_get_notBefore(X509 *x)
-
ASN1_UTCTIME * X509_get_notAfter(X509 *x)
-
time_t ASN1_UTCTIME_get(ASN1_UTCTIME * tm);
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:
-
X509_NAME * X509_get_subject_name (X509 * x)
-
X509_NAME * X509_get_issuer_name (X509 * x)
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:
-
X509_set_version(X509 *x, long version)
-
X509_set_serialNumber(X509 *x, ASN1_INTEGER* num_serie)
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:
-
X509_gmtime_adj(X509_get_notBefore(X509 * x),long segundos)
-
X509_gmtime_adj(X509_get_notAfter(X509 * x),long segundos)
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:
-
EVP_PKEY * X509_get_pubkey(X509 * x)
-
int X509_set_pubkey(X509 * x, EVP_PKEY * pkey)
-
int X509_check_private_key(X509 * x, EVP_PKEY * pkey)
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:
-
X509 *X509_REQ_to_X509(X509_REQ *r, int days, EVP_Pkey *pkey)
-
X509_REQ *X509_to_X509_REQ(X509 *x, EVP_PKEY *pkey, EVP_MD *md)
-
X509_print_fp(pf,X509 * x)
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:
-
int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
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:
-
int X509_NAME_add_entry_by_txt(X509_NAME * nombre, char *
campo, int tipo, unsigned char * bytes, int tamaño, int localizacion,
int set)
La secuencia completa para crear un certificado desde el inicio es el siguiente:
-
Iniciación de las variables oportunas (variable de clave pública,
certificado, clave rsa, estructuras de nombres, ficheros, etc.).
-
Generación de un par de claves RSA del tamaño que queramos
(1024 bits suele ser una buena elección).
-
Asignación del par RSA a la estructura de clave pública EVP_PKEY.
-
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).
-
Obtención y relleno de la estructura de nombres del certificado,
tanto para el subject como para el issuer (si lo hacemos autofirmado).
-
Firma del certificado.
-
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