Infraestructura de llave pública (PKI)

2019-08-30 OpenSSL
Tags   OpenSSL 1.1

En post anteriores hemos visto la criptografía de llave pública como sistema criptográfico de dos llaves para asegurar la confidencialidad y autenticidad de mensajes. El día de hoy iremos un poco más allá, y daremos solución a un problema que tal vez hayas descubierto a lo largo de nuestra serie de posts sobre criptografía. Si aún no conoces de que va todo esto, en los siguientes enlaces podrás encontrar toda la información necesaria para estar al corriente de lo que veremos en el post del día de hoy.

- Criptografía asimétrica en OpenSSL - Llave pública
- Criptografía asimétrica en OpenSSL - Llave privada
- Criptografía asimétrica en OpenSSL - Llave encriptada

El Ataque del hombre en el medio

Uno de los problemas más importantes de la criptografía de llave pública es la distribución de las llaves.  Esto es así, ya que se parte del hecho de que los participantes (receptor, emisor) confían uno al otro sus llaves públicas y que lo hacen mediante un canal seguro. Supongamos que Emily ha enviado su llave pública a Steave vía correo electrónico. Qué pasaría si alguién (el interceptor) logra interceptar dicho mensaje y reemplaza la llave pública de Steave con su propia llave pública ?. En el sentido práctico, sería mucho más sencillo hacerse con las credenciales para entrar al correo de Emily y que el interceptor enviara el mensaje a Steave con su llave pública. La idea es que imagines un escenario similar de suplantación de identidad. Cuando Emily envíe un mensaje a Steave en realidad utilizará la llave pública del interceptor, que a su vez descifrará el mensaje y lo reencriptará con la llave pública de Steave para enviárselo a él finalmente. Cuando Steave envíe un mensaje encriptado e Emily sucederá lo mismo, el interceptor descrifrará el mensaje y lo reencriptará para enviárselo finalmente a Emily. Este tipo de ataque es el denominado "Man in the middle attack". Nota que Emily en realidad se está comunicando con Steave pero sin confidencialidad. Una de las soluciones a este problema es PKI.

PKI (Public key infrastructure)

La solución al problema de saber si una llave pública pertenece o no a una persona es la PKI. La idea de esta solución es contar con una entidad de confianza que haga el trabajo de certificar que una llave pública pertenece a una persona dada. Esta persona estará identificada con algunos datos útiles como nombre y dirección. La entidad de confianza emitirá un certificado que contiene entre otras cosas:

- Información necesaria de la persona (nombre, dirección, fecha de nacimiento, ...)
- La llave pública de la persona
- La fecha de creación del certificado
- La fecha de revocación del certificado
- La firma digital de toda esta información emitida por la PKI

Con este esquema de infraestructura la próxima vez que Emily quiera enviar un mensaje a Steave preguntará por su certificado (el cual incluye su llave pública) del cual puede validar su firma digital emitida por la PKI y su fecha de revocación. Si esta validación pasa entonces Emily puede enviar de manera segura y confidencial un mensaje a Steave y viceversa.

Configuración de una PKI

Lo primero que debemos hacer es encontrar el archivo de configuración de ejemplo de OpenSSL el cuál podría estar en /etc/ssl/openssl.cnf dependiendo de la distribución de Linux que tengas instalada. Para verificar en dónde podría estar dicho archivo de configuración podemos ejecutar el siguiente comando.

openssl version -a

Este comando arrojará un resultado similar al siguiente:

OpenSSL 1.1.1  11 Sep 2018
built on: Thu Jun 20 17:36:28 2019 UTC
platform: debian-amd64
options:  bn(64,64) rc4(16x,int) des(int) blowfish(ptr) 
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-cn9tZy/openssl-1.1.1=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPADLOCK_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2
OPENSSLDIR: "/usr/lib/ssl"
ENGINESDIR: "/usr/lib/x86_64-linux-gnu/engines-1.1"
Seeding source: os-specific

La antepenúltima línea en este caso contiene el valor del OPENSSDIR en el cuál estará el archivo de configuración, certificados y otros archivos importantes. 

El archivo de configuración de openssl contiene una configuración por defecto. Para efectos de pruebas, no deberías cambiar nada. Sin embargo, es necesario que todos los directorios que se listan en la configuración del archivo openssl.cnf estén creados.

mkdir pki && cd pki
mkdir -p demoCA/private
mkdir demoCA/certs
mkdir demoCA/crl
mkdir demoCA/newcerts

Una vez verificado que todos los directorios estén creados podemos proceder con la creación de la PKI.

Creación de la PKI

Para crear la PKI vamos a hacer uso del estándar -x509 que define cómo debe estar codificada la información de un certificado. Todo comienza con la entidad de confianza y un certificado raíz los cuales podemos generar ejecutando el siguiente comando.

openssl req -new -x509 -keyout cakey.pem -out cacert.pem

Este comando creará la llave privada cakey.pem y el certificado asociado cacert.pem el cuál incluye la llave pública de la entidad de confianza. Debido a que estamos creando un certificado se nos solicitará algunos datos de información similares a los siguientes:

Generating a RSA private key
...................+++++
.................................................................................+++++
writing new private key to 'cakey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CO
State or Province Name (full name) [Some-State]:Antioquia
Locality Name (eg, city) []:Medellin
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Pleets
Organizational Unit Name (eg, section) []:Tech Dept.
Common Name (e.g. server FQDN or YOUR name) []:https://pleets.org
Email Address []:example@pleets.org

El certificado creado anteriormente es el certificado de la entidad de confianza (CA - Certificate authority) con el cuál se firmarán los demás certificados (certificados de usuario). Si has revisado la configuración de OpenSSL tanto la llave privada como el certificado deben estar en la carpeta demoCA.

mv cakey.pem demoCA/private
mv cacert.pem demoCA

También es necesario crear el archivo index.txt y el archivo serial los cuales contendrán la base de datos de archivos y el consecutivo serial actual respectivamente.

touch demoCA/index.txt
touch demoCA/index.txt.attr
echo "C001" > demoCA/serial

Creación de un certificado de usuario

Antes de realizar esta firma, es decir, que la entidad de confianza nos entregue un certificado, cada usuario debe crear una solicitud de certificado la cuál se puede hacer con el siguiente comando:

openssl req -new -keyout userkey.pem -out usercert-req.pem

Este comando creará la llave privada userkey.pem y el certificado asociado usercert-req.pem. Por supuesto, por ser un certificado el que queremos crear, nuevamente se solicitarán los datos que un certificado debería tener:

Generating a RSA private key
..............................+++++
....................................+++++
writing new private key to 'userkey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CO
State or Province Name (full name) [Some-State]:Antioquia
Locality Name (eg, city) []:Medellin
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Pleets         
Organizational Unit Name (eg, section) []:Tech     
Common Name (e.g. server FQDN or YOUR name) []:Dario Rivera
Email Address []:example@gmail.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:    
An optional company name []:

Toda esta información es enviada a la entidad de confianza para certificación o puede ser autofirmada por nuestra propia entidad de confianza. En ambos casos es recomendado revisar nuevamente la información del certificado con el siguiente comando.

openssl req -text -in usercert-req.pem -noout

Para firmar este certificado con la entidad de confianza debemos utilizar el siguiente comando:

openssl ca -in usercert-req.pem -out usercert.pem

Si todo ha salido bien, la salida de este comando debería ser similar a la siguiente:

Using configuration from /usr/lib/ssl/openssl.cnf
Enter pass phrase for ./demoCA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 49153 (0xc001)
        Validity
            Not Before: Aug 30 05:38:48 2019 GMT
            Not After : Aug 29 05:38:48 2020 GMT
        Subject:
            countryName               = CO
            stateOrProvinceName       = Antioquia
            organizationName          = Pleets
            organizationalUnitName    = Tech
            commonName                = Dario Rivera
            emailAddress              = fermius.us@gmail.com
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Comment: 
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier: 
                89:3D:33:63:A9:76:31:9D:0D:47:11:8D:41:A2:74:C4:EE:19:BB:02
            X509v3 Authority Key Identifier: 
                keyid:09:15:1A:A3:42:C4:79:4E:A0:30:F2:47:C6:5A:C5:55:16:5D:CC:0E

Certificate is to be certified until Aug 29 05:38:48 2020 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Lo cual indica que se ha generado el certificado usercert.pem de manera correcta. Para convertir este certificado a un formato instalable en el navegador podemos utilizar el siguiente comando:

openssl pkcs12 -export -in usercert.pem -inkey userkey.pem > usercert.p12

Finalmente a modo de test observa que si hubiésemos colocado un organizationName distinto al inicial (Pleets) hubiésemos tenido la siguiente respuesta:

Using configuration from /usr/lib/ssl/openssl.cnf
Enter pass phrase for ./demoCA/private/cakey.pem:
Check that the request matches the signature
Signature ok
The organizationName field is different between
CA certificate (Pleets) and the request (ENCOM)

Lo cual indica que un certificado no puede firmar certificados para organizaciones diferentes a la suya. Espero que hayas aprendido bastante con este post. Hasta la próxima.

Acerca de Darío Rivera

Author

Ingeniero de desarrollo en PlacetoPay , Medellín. Darío ha trabajado por más de 6 años en lenguajes de programación web especialmente en PHP. Creador del microframework DronePHP basado en Zend y Laravel.

Sólo aquellos que han alcanzado el éxito saben que siempre estuvo a un paso del momento en que pensaron renunciar.