Revista Tecnología

Cómo cifrar archivos con OpenSSL y con par de claves pública y privada. Varios métodos, paso a paso

Publicado el 04 julio 2016 por Gaspar Fernández Moreno @gaspar_fm

Cómo cifrar archivos OpenSSL claves pública privada. Varios métodos, pasoCómo cifrar archivos con OpenSSL y con par de claves pública y privada. Varios métodos, paso a paso

Si queremos aumentar la seguridad del cifrado de archivos con OpenSSL. En lugar de utilizar una contraseña plana como se indica en el post anterior podemos utilizar un par de claves pública y privada para este cifrado. De estar forma, por un lado podemos ahorrarnos (si queremos) el tener que recordar una contraseña segura, y por otro ganar en seguridad, ya que las contraseñas, al fin y al cabo terminan siendo poca cosa.

En este post comentaré varios métodos para hacer esto, con sus pros, sus contras y algún comentario personal. Aunque para completar el post, quiero poner el proceso completo, desde la creación de las claves y el descifrado de la información, así, si queréis, podéis copiar, pegar y probar todo esto.

Creando el certificado

Otras veces ya hemos creado un certificado, pero volveremos a crear otro certificado más para la ocasión. Para ello, primero generamos una clave privada que almacenaremos en el archivo privada.key:

$ openssl genrsa -out privada.key 4096

Si queremos cifrar dicha clave, y protegerla con contraseña podremos escribir:

$ openssl genrsa -des3 -out privada.key 4096

Esto nos pedirá una contraseña siempre que queramos descifrar mensajes.

Ahora vamos a crear la petición de firmado de nuestro certificado:

$ openssl req -new -key privada.key -out certif.csr

... o si queremos que no nos pregunte tanto:

$ openssl req -new -key privada.key -out certif.csr -subj "/C=ES/ST=Spain/L=Malaga/O=Home/CN=poesiabinaria"

Por último, lo firmamos, en este caso será autofirmado, si disponemos de un certificado para firmarlo, échale un ojo a esto, donde verás muchas opciones para trabajar con OpenSSL (en este ejemplo, para hacer las pruebas, lo firmamos para 10 años (3650 días):

$ openssl x509 -req -days 3650 -in certif.csr -signkey privada.key -out certif.crt

Ya tenemos nuestro certificado en certif.crt y nuestra clave privada en privada.key

Cifrado con SMIME

es un estándar de cifrado de correo electrónico muy utilizado por gran variedad de clientes de correo
S/MIME. Aunque en la transmisión de correo electrónico se utilizan caracteres seguros para la transmisión de archivos, pero a la hora de obtener un archivo cifrado no es necesario esto por lo que podemos obtener el binario resultante del cifrado.
Este cifrado, al utilizar un par de claves diferentes para cifrar y descifrar se trata de un cifrado asimétrico. Más adelante, hablaremos de un cifrado simétrico, que utiliza una misma clave para codificar y decodificar la información.
Como ya tenemos el certificado para cifrar el archivo, y tenemos un archivo para cifrar, datos.tar.bz2 (aunque podría ser el tipo de archivo que queramos), procedemos al cifrado:

$ openssl smime -encrypt -binary -aes-256-cbc -in datos.tar.bz2 -out datos.tar.bz2.enc -outform DER certif.crt

Crearemos el archivo datos.tar.bz2.enc con la información cifrada. Del mismo modo, para descifrar el archivo hacemos:

$ openssl smime -decrypt -aes-256-cbc -in datos.tar.bz2.enc -inform DER -out datos.tar.bz2.dec -inkey privada.key

En este caso, en lugar de descifrar con el certificado, debemos hacerlo con la clave privada, de esta forma, nosotros seremos los únicos capaces de ver el archivo.

Con este método podremos cifrar archivos de varios Megabytes de tamaño, el problema es que tanto para cifrar como descifrar, el archivo debe entrar por completo en memoria, por lo que, a mayor tamaño de archivo, mayor necesidad de memoria RAM. Podemos utilizarlo para transmitir archivos pequeños, pero no, por ejemplo, para cifrar una copia de seguridad (de varios Gb) o veremos un problema como este:

Error reading S/MIME message
140464226838176:error:07069041:memory buffer routines:BUF_MEM_grow_clean:malloc failure:buffer.c:159:
140464226838176:error:0D06B041:asn1 encoding routines:ASN1_D2I_READ_BIO:malloc failure:a_d2i_fp.c:255:

Cifrando mensajes pequeños con clave pública

Otra cosa que podemos hacer es cifrar pequeños mensajes de unos pocos bytes, dependiendo del tamaño de nuestra clave. Si nuestra clave es de 4096bit podremos cifrar archivos de algo menos de 512bytes (4096/8=512). Y generar claves, aunque parece algo sencillo, una clave de 8192 (4096*2) puede unas tardar unas 6 veces más que una de 4096 y una de 16384 (8192*2=4096*4) puede llegar a tardar unas 6 veces más que la de 8192 por lo que generar una clave (aproximadamente, el tiempo puede variar muchísimo).
Con esto quiero decir que generar una clave de varios gigas, puede que no sea una buena idea.

Aunque, para archivos pequeños o incluso otras claves, nos puede servir perfectamente. Para ello, lo primero que tenemos que hacer es extraer una clave pública a partir de la clave privada:

$ openssl rsa -in privada.key -pubout -out publica.key

Ahora, teniendo un archivo datos.raw que queremos cifrar, hacemos:

$ openssl rsautl -pubin -encrypt -in datos.raw -out datos.enc -inkey publica.key

Y cuando necesitemos descifrarlo:

$ openssl rsautl -decrypt -inkey privada.key -in datos.enc -out datos.dec

Cifrado simétrico

Otra alternativa, tal y como hicimos en el anterior post , es utilizar un cifrado simétrico, es decir, la clave es la misma para cifrar y descifrar, pero esta vez, en lugar de ser un password que introducimos por teclado puede ser una clave muy grande, por ejemplo, una cadena aleatoria que almacenamos en un archivo.

Lo primero que tenemos que hacer es generar dicha cadena (clave) aleatoria:

$ openssl rand 1024 > simetrica.key

Esta clave puede ser grande (yo suelo usarlo de 10Kb) y no tarda demasiado en generarse, podríamos utilizar un dispositivo hardware o un generador de números aleatorios que nos proporcione más entropía, ya depende de nosotros.

Tras ello, procedemos a cifrar nuestro archivo original, volvemos al archivo .tar.bz2 aunque, ahora sí que podemos cifrar archivos muy grandes (de varios Gb), eso sí, es un poco más inseguro que el cifrado asimétrico:

$ openssl aes-256-cbc -in largefile.tar.bz2 -pass file:simetrica.key -out largefile.tar.bz2.enc

Y para descifrarlo:

openssl aes-256-cbc -d -in largefile.tar.bz2.enc -pass file:simetrica.key -out largefile.tar.bz2.dec

Este proceso no es excesivamente costoso, en mi caso, el proceso openssl consumía unos 4Mb de memoria, la carga de procesador tampoco es tan grande, ya que está constantemente leyendo y escribiendo.

Comprobando que el proceso funciona

Está muy bien que cifremos y descifremos, pero, si sois como yo, que no os fiáis de que funcione todo esto, y tampoco es plan de dejar las comprobaciones para última hora.

Podemos comprobar que el original y el decodificado es el mismo, con diff:

$ diff datos.raw datos.dec

Aunque para esta tarea necesitamos tener el decodificado y el original, lo que muchas veces no es posible. Otra cosa que podemos hacer es generar el hash del fichero original, tenemos un montón de formas de hacerlo (MD5, SHA, Whirlpool...), además, aunque el algoritmo tenga colisiones descubiertas, lo estamos utilizando para comprobar que el archivo está bien formado, por lo que podemos utilizar libremente un MD5, un SHA-1, si queremos estar 100% seguros siempre podemos crear dos hashes con dos algoritmos diferentes y comparar los dos.
Los hashes tenemos que crearlos con el original y volver a crearlos con el archivo decodificado, si ambos coinciden, podemos concluir que los archivos son iguales.

$ openssl dgst -md5 datos.raw
MD5(datos.raw)= 2ad26a209b86051e707882a67e0d6c8f

$ openssl dgst -md5 datos.dec
MD5(datos.dec)= 2ad26a209b86051e707882a67e0d6c8f

Esto también es buena idea si los ficheros los vamos a transmitir por red, de esta forma si ha habido algún problema en la transmisión podremos datnos cuenta.

Mezclando varios métodos para incrementar la seguridad

Una técnica que podemos utilizar es cifrar el archivo grande de forma simétrica con una clave aleatoria, y luego cifrar dicha clave aleatoria (única para esta operación) de forma asimétrica con una clave pública, de formas que sólo pueda ser descifrado con nuestra clave privada. Esto no es más que mezclar los dos métodos de arriba, no estoy haciendo nada nuevo, sólo recapitulando:

$ openssl rand 1024 > simetrica.key
$ openssl dgst -sha256 largefile.tar.bz2
SHA256(largefile.tar.bz2)= 34c6744d6a91de31def7669c53f20d13610fb6e3405d6789f85ab03671f65719
$ openssl aes-256-cbc -in largefile.tar.bz2 -pass file:simetrica.key -out largefile.tar.bz2.enc
$ openssl smime -encrypt -binary -aes-256-cbc -in simetrica.key -out simetrica.enc -outform DER certif.crt

Para descifrar el archivo necesitaremos largefile.tar.bz2.enc (los datos) y simetrica.enc (la clave). Ahora lo desciframos de la siguiente manera:

$ openssl smime -decrypt -aes-256-cbc -inform DER -in simetrica.enc -out simetrica.dec -inkey privada.key
$ openssl aes-256-cbc -d -in largefile.tar.bz2.enc -pass file:simetrica.dec -out largefile.tar.bz2.dec
$ openssl dgst -sha256 largefile.tar.bz2.dec
SHA256(largefile.tar.bz2-dec)= 34c6744d6a91de31def7669c53f20d13610fb6e3405d6789f85ab03671f65719

Y, por supuesto, podemos hacer un script que automatice el proceso, eso ya os lo dejo a vosotros Cómo cifrar archivos OpenSSL claves pública privada. Varios métodos, paso

Cómo cifrar archivos con OpenSSL y con par de claves pública y privada. Varios métodos, paso a paso

Foto principal: Evan Dennis


Volver a la Portada de Logo Paperblog