OpenSSL y Criptografía Asimétrica – Práctica

Publicado por Diego Córdoba en

Hoy aprenderemos a integrar los conceptos de seguridad vistos anteriormente incorporando OpenSSL y Criptografía Asimétrica.

Este artículo forma parte de la Serie sobre Criptografía Aplicada publicada en este blog. Pueden visitar el índice de la serie para acceder a todo el contenido.

Para los que llegan a esta seroe justo en este artículo, les recomiendo pasarse por dos anteriores:

Dicho esto, vamos al grano!

Confidencialidad con OpenSSL y criptografía asimétrica

Como sabemos, el cifrado asimétrico requiere de dos claves: una pública y una privada.

Entre los algoritmos más conocidos de criptografía asimétrica los tenemos a RSA y DSA.

RSA es un algoritmo de cifrado asimétrico especializado en encriptación y desencriptación de datos, mientras que DSA (Digital Signature Algorithm) se utiliza generalmente para firma digital, aunque sus claves también pueden combinarse con el algoritmo ElGamal para lograr cifrado y descifrado.

Veamos cómo podemos lograr privacidad utilizando RSA.

Primero y principal, necesitamos crear nuestras claves: pública y privada. Existen varios métodos con los cuales OpenSSL nos permite trabajar con claves. Podemos crear la clave pública y privada simplemente, podemos además proteger la clave privada con una contraseña de uso, podemos crear un certificado digital que incorpore la clave pública y metadatos de validez, etc.

Por el momento veremos el caso más simple: crear clave pública y privada básicas.

Para crear la clave privada se utiliza el subcomando genrsa de OpenSSL. Ubiquémonos en el criptosistema de Alice y Bob estudiado con anterioridad. Supongamos que Alice le quiere enviar a Bob un archivo cifrado. Para ello Bob deberá generar su par de claves asimétricas, y hacerle llegar la clave pública a Alice.

criptosistema criptografia asimétrica openssl privacidad

Generación de claves RSA

Veamos cómo crear la clave privada_Bob.pem, de 2048 bits, y qué contiene:

openssl genrsa -out privada_Bob.pem 2048
cat privada_Bob.pem 
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCxZz4jjgCWWOyd
Rmer6SViC09Xj3E3x4SWThYjaP/yXBtxrIZGy6R0HUF/E2WD0OmTuQ1QZjusEByc
8SIeTA1vTKy+VTrlsTubndtYAlIvdZpiIEyFPIJYQe+tWKhXRknRSS8T1HvRhHEr
kGbSBKS7l3aCVH2ETYLWeuXMANZWwrFEKd5UON/Zwk/msLCbS8aXCcdQ9UoXsL3I
0yzo0Fa9+ajKRCCUUyniCatzHXvMw1HocU/sIjDDVTSliLqRom4COMXT3SQsdhZ0
B7fOAL+fCQceswrCQo5TvsLR/cSgAmRam+Gk3vRtys0EVdk2JcJJjAriLCs1UVKZ
nmILcwV9AgMBAAECggEADgUR+tbX0onsByDfhA08lFM+/bU7FhSpNISuyLUdVw6y
54iiIvmogTmnqa6DfQeXUgKTnAk8SYSLcla3IsxRWnylgTQ0n57iTlHLGhky3AfP
/9W8HmJyDZ4GXj1trloCbuz48Lj/4Qa1t+LlUU8nIqEdbz7UnY5Pmvoi63ycc8Y0
7idFqFeFjqr3Soiq246paIpKU6VC87kR5wSlePnQZteqsS+UvlpsI8NHzYVoogte
GFZcMUZv2Vhq1370pZ4K5V0FvAb1a0yCF4uwg2Lba6NtbVeN8jh4CYFXnyXgZ0FZ
h+32quzo2BRpnvUy4GVjI+FY2ug+KtsQ7BXVu72T2QKBgQDc3HfxYFbKE1LkJgns
Zc5GVVNcK6J0BJoEbsEdaRfnCdoFDO79qdeitHUJpmBVaIM2kDV+9eIklU++sxNJ
IcBvgVVafx6Ja96R56arnIxF2hrYiWccEvnib+WGPfbvCJ/BV7zuaZFXP21R50oj
K1sBe8dGcC5fR7eP3wtbfJ+seQKBgQDNoMMS+DMA8bh9aSQlXGbHNoUIwAqOKFd7
GvLVuVCLcx9UN5qcZqAH7ckMFGx50OfPJCnZAxIC+OKqoRkRRXCBvU5HHDbNv9Ek
1A99DuxvRBqT0rljiAFSXWXj3igMJBgln47Vuobsv5WVigTJeZvhmTCxsxIlF2SP
+JsqXWHYJQKBgFUX1FZ5Sbb9jvg796LO7pSGl2FxpluQ0k5W5ayRKxZm40sC2ql8
4IndgSJAqej+JV/GW/YYghKaPGk60XlhuaS8bcg6JefIWa5ZVAvWeu0GxUUcW0+j
uAcYGZSJFzStW+/AABjTL54v38yokhXOxHdj2zk2S4eGOCEETHnpIBPxAoGBAMGy
6W+qHCmMifShIWqMQ0jvY5wdgxPsjpOpjAyl92iVNnLXui4QQjmV1egqKRELKAIp
l74UNVhVaPfAr2NknRmp4N39huaEBZiCpmBwuWoKplqPixFmIZCb6IohUGo3agOu
7RcKEWVdB6SJEf9ULfHyozsRa0tDYPh6AHiE5NF9AoGAURg2BdFk3LzSKrD9Ikuz
r/L0dZ+eixDMkMgyVgRZ6FYCySIYzmD6ezIS9IOXc5c2p5iL0I1i/0CvytPud07M
G1Xkk4Hfpeuk4GrdebmJUCxk3h/G/Y1fBoJ7LGgPDsJDQ5nuouy0YqR+xtFxpdZc
xk0ytfYr5DgH2EOHCdeDOqs=
-----END PRIVATE KEY-----

Esa clave privada NUNCA tienen que compartirla con NADIE (yo en este ejemplo la muestro con fines educativos, pero es una clave que voy a descartar luego de escribir este post).

Ahora, a partir de la clave privada podemos generar una clave pública. Recordemos que el par es unívoco: sólo una pública por privada, y sólo una privada por pública, y además, no se puede generar una clave privada a partir de una pública.

openssl rsa -in privada_Bob.pem -out publica_Bob.pem -outform PEM -pubout
cat publica_Bob.pem 
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsWc+I44AlljsnUZnq+kl
YgtPV49xN8eElk4WI2j/8lwbcayGRsukdB1BfxNlg9Dpk7kNUGY7rBAcnPEiHkwN
b0ysvlU65bE7m53bWAJSL3WaYiBMhTyCWEHvrVioV0ZJ0UkvE9R70YRxK5Bm0gSk
u5d2glR9hE2C1nrlzADWVsKxRCneVDjf2cJP5rCwm0vGlwnHUPVKF7C9yNMs6NBW
vfmoykQglFMp4gmrcx17zMNR6HFP7CIww1U0pYi6kaJuAjjF090kLHYWdAe3zgC/
nwkHHrMKwkKOU77C0f3EoAJkWpvhpN70bcrNBFXZNiXCSYwK4iwrNVFSmZ5iC3MF
fQIDAQAB
-----END PUBLIC KEY-----

Bien, con esto ya tenemos las dos claves necesarias para que alguien nos encripte algún archivo y podamos desencriptarlo.

Encriptando usando OpenSSL y criptografía asimétrica

Ya tenemos las dos claves, pública y privada, ahora vamos a poder lograr la confidencialidad usando cifrado asimétrico en una comunicación.

Bob debería enviarle a Alice su clave pública, entonces Alice podría cifrar un dato, llamémosle archivo.txt, y enviarlo cifrado a Bob.

Finalmente Bob podría desencriptar dicho archivo utilizando su clave privada, par de la pública que utilizó Alice para encriptarlo.

Veamos el ejemplo práctico. Supongamos que Alice y Bob son dos directorios en el sistema operativo (para ahorrar comunicación remota, y simplificar los comandos). Bob tendrá los dos archivos de clave, y Alice tendrá solamente el archivo de texto plano para encriptar:x

Bob le envía su clave pública a Alice:

Ahora Alice encripta el archivo utilizando la clave pública de Bob. Supongamos que el archivo cifrado se llama archivo.txt.rsa. Para ello, Alice debe ejecutar el comando openssl utilizando el subcomando pkeyutl, de manera similar a esta:

openssl pkeyutl -encrypt -inkey publica_Bob.pem -pubin -in archivo.txt -out archivo.txt.rsa

Aquí le especificamos la clave de entrada a utilizar para encriptar, publica_Bob.pem, el archivo en texto, archivo.txt, y el archivo que se generará como salida, archivo.txt.rsa.

El resultado se ve en la siguiente captura:

Dicho archivo cifrado no contiene datos legibles, por lo que Alice ahora podrá enviarlo a Bob sin correr riesgos de que se filtre información si un atacante como Eva se encuentra escuchando en el canal de comunicación.

Luego de enviar dicho archivo, el escenario será algo como esto:

Desncriptando usando OpenSSL y criptografía asimétrica

Ahora Bob puede desencriptar el archivo y encontrar su contenido original utilizando el subcomando pkeyutl de openssl de esta forma:

openssl pkeyutl -decrypt -inkey privada_Bob.pem -in archivo.txt.rsa -out desencriptado.txt

Este comando desencriptará el archivo archivo.txt.rsa utilizando la clave privada de Bob, privada_Bob.txt, y generando un nuevo archivo, desencriptado.txt, con el mismo contenido que el archivo.txt original escrito por Alice.

Todo el proceso resumido en un GIF:

criptosistema criptografia asimétrica openssl privacidad

Autenticidad e Integridad con OpenSSL y criptografía asimétrica

Ahora veremos cómo lograr la autenticidad y la integridad de un archivo enviado haciendo uso de OpenSSL.

Si recordamos en el post conceptual, es el receptor del mensaje quien puede verificar estas dos características, y lo realiza mediante una combinación de cifrado asimétrico y funciones hash, en lo que se denomina firma digital.

Como mencionamos antes, el algoritmo más común para realizar firmas digitales con openssl es DSA.

Veamos un ejemplo simple en el que sólo pueda verificarse autenticidad e integridad.

Supongamos que Alice quiere enviarle el archivo en texto plano, archivo.txt, a Bob, y Bob quiere verificar que el emisor fue realmente Alice.

Para ello, Alice debe firmar el mensaje. Si recordamos, la firma consiste en calcular un hash del contenido original, y luego encriptarlo con su clave privada. Bob podrá verificar la firma utilizando la clave pública de Alice.

Recordemos la imagen del criptosistema con los pasos necesarios.

criptosistema criptografia asimétrica openssl autenticidad integridad

Generación de claves DSA

Alice debe generar su par de claves DSA. Primero debemos crear un archivo de parámetros DSA que OpenSSL utilizará como entrada para generar la clave privada:

openssl dsaparam 2048 < /dev/random > dsaparam.pem

Esto creará el archivo dsaparam.pem, que usaremos como entrada para crear la clave privada de esta forma:

openssl gendsa -out privadaDSA_Alice.pem dsaparam.pem

Esto creará el archivo de clave privada DSA. Con el siguiente comando creamos la clave pública a partir de la privada:

openssl dsa -in privadaDSA_Alice.pem -pubout -out publicaDSA_Alice.pem

Así, ya tenemos el par de claves, y el archivo de texto plano original. El archivo de parámetros DSA no será necesario de acá en adelante, y puede eliminarse.

Cálculo del hash

El siguiente paso es crear un archivo con el HASH del archivo original, para luego cifrarlo utlizando la clave privada. Existen varias formas de crear este hash, mas o menos automatizadas. Veamos una bastante simple para generar hash SHA-3 de 512 bits.

sha3-512sum < archivo.txt| awk '{print $1}' > archivo.sha3_512

Este comando calculará el hash SHA-3-512 del archivo archivo.txt, y lo almacenará dentro de un nuevo archivo llamado archivo.sha3_512.

Generación de la firma digital

Acto seguido, Alice debe encriptar dicho archivo de hash utilizando OpenSSH y su clave privada, generando un archivo de firma digital llamado, digamos, archivo.txt.signature:

openssl dgst -sign privadaDSA_Alice.pem archivo.sha3_512 > archivo.txt.signature

Con esto, los archivos generados hasta el momento son los siguientes:

Alice ahora puede enviar a Bob el archivo en texto plano, archivo.txt, junto con la firma digital, archivo.txt.signature, y su clave pública, publicaDSA_Alice.pem, para que Bob pueda verificar su autenticidad.

Verificación de la firma digital

Ahora Bob podrá verificar la firma digital generando un nuevo archivo hash que utilice la misma función resumen usada por Alice:

sha3-512sum < archivo.txt| awk '{print $1}' > archivo.sha3_512

Y podrá utilizar OpenSSL para verificar la firma digital del archivo original mediante su función hash, de esta manera:

openssl dgst -verify publicaDSA_Alice.pem -signature archivo.txt.signature archivo.sha3_512      
Verified OK

Así, como la clave pública es par de la privada de Alice, y solamente Alice la posee, Bob verifica que la firma efectivamente fue generada por Alice. Si el archivo fuera modificado en el canal, la firma sería errónea y no garantizaría la autenticidad del archivo.

Aquí un ejemplo, modifiqué el archivo original enviado por Alice simulando cambios de un atacante, recalcué el archivo de hash, y el resultado de verificar la firma falla:

openssl dgst -verify publicaDSA_Alice.pem -signature archivo.txt.signature archivo.sha3_512
Verification failure

Por supuesto, esto también garantiza la integridad del archivo original.

Veamos todo el proceso resumido en un GIF:

criptosistema criptografia asimétrica openssl autenticidad integridad

Claves privadas protegidas con password

OpenSSL es una herramienta enorme, y permite varias variantes para realizar diferentes tareas. Luego analizaremos, por ejemplo, cómo trabajar con certificados digitales utilizando OpenSSL, certificados que incorporan la generación automática de las claves pública y privada.

Además, podemos crear una clave privada protegida con contraseña, de modo que, quien vaya a utilizar dicha clave deberá typear la contraseña para poder «desbloquearla«.

Recordemos que la clave privada representa nuestra identidad dentro del sistema, y quien la posea podrá hacerse pasar por nosotros. Añadirle contraseña de uso crea una capa adicional de seguridad que un atacante deberá vulnerar.

Veamos un ejemplo de su creación:

$ openssl genrsa -aes128 -passout pass:supersecreto -out privada.pem 2048
$ openssl rsa -in privada.pem -passin pass:supersecreto -pubout -out publica.pem

Encriptar un archivo utilizando la clave pública es exactamente igual que antes, sin embargo, para desencriptarlo openssl nos va a pedir la contraseña necesaria para desbloquear la clave privada.

Combinando todo…

Como se comentó, en estos dos ejemplos vimos cómo verificar privacidad por un lado, y autenticidad junto con integridad por el otro. Podríamos realizar una verificación completa de las tres características combinando herramientas.

Por ejemplo, podríamos haber generado la firma utilizando la clave privada de Alice, y luego encriptar el archivo original mediante la clave pública de Bob previo a su envío.

También podrían combinarse mecanismos simétricos y asimétricos, por ejemplo, encriptar el archivo original, previo a su envío, utilizando un cifrador simétrico como AES, utilizando una clave pre-compartida.

OpenSSL y criptografía asimétrica: Conclusiones

Hemos aprendido algunos comandos básicos de OpenSSL para poder generar claves asimétricas RSA y DSA, encriptar, desencriptar, calcular y verificar firmas digitales.

En la realidad las claves públicas no se suelen enviar directamente como los archivos de texto que creamos en estos ejemplos, sino que se suelen empaquetar en lo que se denomina certificado digital x509.

Más adelante en esta serie aprenderemos a gestionar certificados x509, y entenderemos qué es y cómo funciona la infraestructura de clave pública, o PKI (Public Key Infrastructure).

Espero que les resulte interesante!

Hasta la próxima!


¿Preguntas? ¿Comentarios?

Si tenés dudas, o querés dejarnos tus comentarios y consultas, sumate al grupo de Telegram de la comunidad JuncoTIC!
¡Te esperamos!


Diego Córdoba

- Ingeniero en Informática - Mg. Teleinformática - Tesis pendiente - Docente universitario - Investigador