Creación de una PKI en Ubuntu

Author
By Darío Rivera
Posted on 2021-02-08 in Ubuntu

Una PKI (public key infraestructure) es la solución a un problema denominado "El ataque del hombre en el medio".  Puedes averiguar más sobre este problema en nuestro post Infraestructura de Llave Pública. En este post, veremos cómo crear una PKI fácilmente en Ubuntu.

Configuración de una PKI en openssl

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. Para verificar en dónde está realmente este 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, si deberás verificar que exista una sección [ ca ] similar a la siguiente:

####################################################################
[ ca ]
default_ca	= CA_default		# The default ca section

####################################################################
[ CA_default ]

dir		= ./demoCA		# Where everything is kept
certs		= $dir/certs		# Where the issued certs are kept
crl_dir		= $dir/crl		# Where the issued crl are kept
database	= $dir/index.txt	# database index file.
#unique_subject	= no			# Set to 'no' to allow creation of
					# several certs with same subject.
new_certs_dir	= $dir/newcerts		# default place for new certs.

certificate	= $dir/cacert.pem 	# The CA certificate
serial		= $dir/serial 		# The current serial number
crlnumber	= $dir/crlnumber	# the current crl number
					# must be commented out to leave a V1 CRL
crl		= $dir/crl.pem 		# The current CRL
private_key	= $dir/private/cakey.pem# The private key

Este archivo en primer lugar nos indica que la CA (Autoridad de Certificación) por defecto está definida en la sección CA_default. En esta sección verás que todos los archivos apuntan hacia ./demoCA, en este caso /usr/lib/ssl/demoCA. Sin embargo, esta carpeta así como las que residen dentro de ella no están creadas. Para esto, puedes correr el siguiente comando y crear toda esta estructura de directorios.

mkdir -p demoCA/certs
mkdir demoCA/crl
mkdir demoCA/newcerts
mkdir demoCA/private

También necesitarás crear el archivo de la base de datos de certificados emitidos/revocados y el archivo serial que llevará el consecutivo del último certificado firmado.

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

Una hecho esto podemos proceder con la creación de la autoridad de certificación.

Creación de la Autoridad de Certificación

Para crear la autoridad de certificación 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 demoCA/private/cakey.pem -out demoCA/cacert.pem

Este comando creará la llave privada cakey.pem y también el certificado autofirmado cacert.pem el cuál incluye la llave pública de la autoridad de certificación. 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 con el cuál se firmarán los demás certificados (certificados de usuario). También es posible crear autoridades de certificación intermedias que firmarán a su vez los certificados de usuarios (esta configuración no la veremos en este post).

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 []:

Es importante que veas la diferencia entre un CSR (certificate signing request) y un certificado en sí. Lo que hicimos anteriormente fue crear una solicitud de firma de certificado (CSR) que deberá firmar la CA.

Toda esta información es enviada a la entidad de confianza para certificación, en este caso tu propia CA. Antes de firmar es recomendado revisar nuevamente la información del certificado para ver que todo está correcto. Esto lo puedes hacer 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.


Si te ha gustado este artículo puedes invitarme a tomar una taza de café


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.