9.1.2. Técnicas critográficas

Estudiaremos bajo este epígrafe dos aspectos relacionados con la criptografía:

  • Las funciones de resumen o funciones hash.

  • El cifrado en sí y cuáles son los algoritmos que se usan para ello.

9.1.2.1. Funciones hash

Las funciones hash o funciones digest o funciones resumen son funciones que dada una entrada generan siempre una salida de la misma longitud. Lo deseable, a efectos de nuestro tema, es que:

  • sean deterministas, esto es, que para una misma entrada siempre generen la misma salida.

  • minimicen las colisiones, esto es, que minimicen la posibilidad de obtener una misma salida para dos entradas distintas.

  • sean de un solo sentido, esto es, que, dada una salida, sea difícil encontrar una entrada cuya salida sea la especificada.

Entre las funciones de hash más conocidas se encuentran:

MD5

Genera un resumen de 128 bits representado habitualmente por 32 dígitos hexadecimanels. Es muy común usarlo como comprobación de la integridad de ficheros. Su seguridad está en entredicho, ya que presenta problemas de colisiones.

SHA

Es una familia de funciones hash que ha ido sufriendo diversas revisiones: SHA-0, SHA-1, SHA-2 y SHA-3. Tanto SHA-0 como SHA-1 generan resúmenes de 160 bits. Ambas se consideran inseguras y se desaconseja su uso. para paliar esto, se desarrolló SHA-2, que implementa funciones hash de 224, 256, 364 y 512 bits. Se las conoce habitualmente como SHA-224, SHA-256, SHA-364 y SHA-512. SHA-3, por su parte, cambia internamente los algoritmos, pero esta pensada para generar resúmenes de la misma longitud que SHA-2.

9.1.2.2. Algoritmos de cifrado

Ya se ha comentado que los algoritmos modernos de cifrado se basan en el secreto de la clave y no en el secreto del propio algoritmo, por lo que nos centraremos en los algoritmos basados en clave. Atendiendo a ella hay dos tipos de algoritmos.

De clave simétrica

También conocidos como de clave privada o de clave única. Son aquellos en que se usa la misma clave para cifrar y descifrar.

De clave asimétrica

También conocidos como de clave pública o de doble clave. Son aquellos que emplean una pareja de claves, de manera que si una se usa para cifrar. la otra sirve para descifrar los mensajes cifrados con la primera.

Es común, además, que las técnicas de cifrado usen una combinación de un algoritmo de clave simétrica y otro de clave asimétrica, lo cual se conoce como cifrado híbrido.

9.1.2.2.1. Cifrado simétrico

En este tipo de cifrado se usa la misma clave tanto para cifrar como para descifrar. Cuando las dos partes se encuentran alejadas, ese, precisamente, es su punto débil, ya que exige que la clave su encuentre en ambos extremos con anterioridad a la comunicación, lo cual sólo puede lograrse:

  • Encontrándose físicamente la ambas partes para intercambiar en mano la clave.

  • Transmitiendo la clave en claro, lo cual puede provocar que se intercepte y sea inútil el cifrado posterior.

Obviando lo anterior, eL factor decisivo en la robustez de la clave es su longitud: cuanto más larga sea, más dificil es averiguarla por fuerza bruta. Por ejemplo, una clave de 8 bits sólo puede tener 28, o sea, 256 valores distintos, lo que implica que podría adivinarse con un máximo de 256 intentos.

Algunos algoritmos de cifrado simétrico muy conocidos son:

DES

Considerado actualmente inseguro para la capacidad computacional de los ordenadores modernos, ya que tiene una longitud de sólo 56 bits. Su sucesor directo es el 3DES (Triple DES) que usa el triple de longitud para la clave (168 bits), aunque también se ha ido desplazando en favor de otros algoritmos.

AES

Sustituyó al algoritmo anterior y tiene una longitud de 256 bits. Es más rápido y, además, no se le conocen vulneralidades. Es uno de los algoritmos más ampliamente utilizados.

Existen otros algoritmos como Twofish o Blowfish.

9.1.2.2.2. Cifrado asimétrico

Se basa en el uso de una pareja de claves relacionadas entre sí, de modo que lo que se haya cifrado con una es posible descifrarlo con la otra, pero no con ella misma. Los algoritmos está diseñados para que el interesado genere una de estas parejas de claves, de manera que cada una de ellas realice un papel diferente:

  • Una es la clave privada que debe mantenerla en secreto su propietario.

  • Otra es la clave pública, pensada para que el propietario la comunique a los demás.

De este modo, cuando se desea enviar un mensaje secreto a un destinatario, lo que se hace es usar la clave pública de éste, ya que tal mensaje sólo podrá ser descifrado usando su clave privada correspondiente que sólo está en posesión del destinatario. El cifrado con la clave pública, pues. garantiza la privacidad.

Por otra parte, si un emisor usa su clave privada para cifrar, el mensaje podrá descifrase con su clave pública correspondiente. Como la clave pública es, eso, pública, no sirve para salvaguardar ninguna privacidad, pero sí para asegurar que el emisor del mensaje es el dueño de las claves. Afinaremos esto más adelante.

Los algoritmos más conocidos de cifrado asimétrico son:

RSA

Es el primer sistema criptográfico de clave asimétrica y aún se considera válido (esto es, suficientemente seguro) tanto para cifrar y como firmar digitalmente. Por lo general, utiliza claves con una longitud entre 1024 y 4096 bits.

DSA

Es otro sistema criptográfico de clave asimétrica que en la actualidad se considera débol, por lo que se desaconseja su uso para cifrar información1. Sus claves tiene una longitud entre 512 y 1024 bits.

Variantes de este sistema que mejoran la fortaleza de las claves y, por tanto permiten su uso para cifrado, son:

  • ECDSA, que usa criptografía de curva elíptica.

  • EdDSA, que usa también criptografía de curva elíptica. y se desarrolló para mejorar la velocidad de cifrado sin sacrificar velocidad. Un tipo particular de este sistema es Ed22519, que permiten usar las últimas versiones de:program:openssh.

La desventaja fundamental del cifrado asimétrico frente al simétrico es su costo computacional: para un mismo nivel de seguridad requiere claves más largas y, además, para una misma longitud de clave es más costoso en tiempo.

9.1.2.2.3. Cifrado híbrido

El cifrado híbrido consiste en el uso de técnicas de cifrado simétrico y asimétrico para sortear:

  • La debilidad intrínseca de tener que la clave simétrica en ambos extremos de la comunicación.

  • El mayor costo computacional del cifrado asimétrico.

Para ello realiza una primera fase de la comunicación utilizando cifrado asimétrico que sirve estrictamente para intercambiar una clave simétrica de forma segura, que se denomina clave de sesión. Una vez que la clave se encuentra en ambos extremos se pasa a realizar un cifrado simétrico usando esta clave de sesión.

9.1.2.3. Herramientas prácticas

9.1.2.3.1. Generación de resúmenes

Para la generación de resúmenes hash de ficheros y flujos de texto, existen dos órdenes fundamentales: md5sum y la familia de comandos shasum (sha256sum, sha384sum, sha512sum), que comparten una misma interfaz, con lo que es indiferente ilustrar el uso de una u otra orden.

La generación del resumen puede hacerse del siguiente modo:

$ echo "Hola, caracola" > saludo1.txt
$ md5sum saludo1.txt
b18a245aba5384920d7f6a488d725181  saludo1.txt

Nota

Si se incluyen varios ficheros como argumento, se calculará el resumen de cada uno de ellos.

También es posible calcular el resumen de un flujo de datos:

$ echo "Hola, caracola" | sha256sum
d261be2aa264d38cad717fa8493dacc0b3f33f949869d39ecf7611689fb617ad  -

Habitualmente, los resúmenes generados se almacenan, a fin de que puedan servir más adelante para comprobar la integridad de los ficheros:

$ md5sum saludo1.txt saludo2.txt > saludos.md5

Con posterioridad, podrá comprobarse la integridad del siguiente modo2:

$ LC_ALL=C md5sum -c saludos.md5
saludo1.txt: OK
saludo2.txt: OK

Nota

Si se almacenó el resumen de un flujo de datos (obsérvese que aparece un «-«, en vez de el nombre del fichero), puede comprobarse que otro flujo es el mismo pasándolo a través de la entrada estándar:

$ echo "Hola, caracola" | md5sum >  flujo.md5
$ echo "Hola, caracola" | LC_ALL=C md5sum -c flujo.md5
-: OK

9.1.2.3.2. Generación de claves: GnuPG

GnuPG es una aplicación que implementa el estándar OpenPGP y que, en consecuencia, muy comúnmente se usa como plugin de clientes de correo para el cifrado o la firma de mensajes. Por ahora, sin embargo, usaremos el ejecutable que proporciona, gpg, para probar la generación de claves.

Nota

En caso de que hagamos pruebas en una máquina virtual es conveniente aumentar la entropía del sistema para mejorar y acelerar la creación de claves asimétricas. Para lograrlo puede hacerse lo siguiente3:

$ cat /proc/sys/kernel/random/entropy_avail  # Entropía inicial
234
# apt install rng-tools
# echo 'HRNGDEVICE=/dev/urandom' >> /etc/default/rng-tools
# echo 'RNGDOPTIONS="--fill-watermark=90%"' >> /etc/default/rng-tools
# invoke-rc.d rng-tools start
# cat /proc/sys/kernel/random/entropy_avail
2159

9.1.2.3.2.1. Instalación

Basta con instalar el paquete:

# apt install gnupg

Como configuración podemos usar la siguiente:

$ mkdir -m 700 ~/.gnupg
$ cat > ~/.gnupg/gpg.conf
keyserver hkps://sks-keyservers.net:443
no-greeting
armor

personal-digest-preferences SHA512
cert-digest-algo SHA512
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
personal-cipher-preferences AES256 TWOFISH CAMELLIA256 3DES

Con la que seleccionamos cuáles son nuestros algoritmos de cifrado y hash preferidos. Además, con armor guardamos el texto cifrado como caracteres imprimibles.

Si ejecutamos la orden:

$ gpg --version

podremos consultar cuáles son los algoritmos de cifrado, hash y compresión que usa el programa.

9.1.2.3.2.2. Cifrado simétrico

El cifrado simétrico es muy apropiado para el cifrado de ficheros. Para cifrar el texto, basta con lo siguiente:

$ echo 'Hola, caracola!!!' > /tmp/saludo.txt
$ gpg -c /tmp/saludo.txt

La orden genera el fichero cifrado /tmp/saludo.txt.asc con esta pinta:

-----BEGIN PGP MESSAGE-----

jA0ECQMCgmqtVcUnh0H80lAB2H6YjrKdXR2P2I9a0JRDKpoQhEJc//dnzA550ged
Q2DYgVpYgaL3Se26CAwii54xhZfUijWnGg7pPSKc7Zd81TLvQm75MA6IbsDPGHEN
eQ==
=5YjK
-----END PGP MESSAGE-----

El fichero podría haberse enviado a otro fichero usando la opción --output:

$ gpg -c -o /tmp/otrofichero.asc /tmp/saludo.txt

Nota

Si se usa como nombre de fichero -, la salida será la estándar.

Nota

El formato del fichero cifrado, sigue el estándar marcado por OpenPGP, pero también podríamos haber generado un fichero cifrado que use caracteres no imprimibles habiendo añadido --no-armor. En este caso, la extensión añadida es .gpg en vez de .asc.

Si hemos ejecutado las ordenes anteriores, habremos comprobado que se pide de forma interactiva la clave simétrica de cifrado. Si queremos ejecutar la orden de forma no interactiva podemos hacer lo siguiente:

$ gpg --batch --passphrase 'contraseñadificil' -c /tmp/saludo.txt

o bien:

$ printf 'contraseñadificil' | gpg --batch --passphrase-fd 0 -c /tmp/saludo.txt

Advertencia

Aunque recordemos que eso guardará en el historial la contraseña y es muy discutible su seguridad, por lo que al menos deberíamos asegurarnos de que tal cosa no sucede.

Para descifrar, podemos no usar la opción -d:

$ gpg -qd /tmp/saludo.txt.asc
Hola, caracola!!!

Nota

La opción -q ejecuta la orden en modo silencioso.

Hay algo, sin embargo, extraño: ¿por qué no se nos pide la clave anteriormente suministrada durante la operación de cifrado para descifrar? La razón es que gpg levanta automáticamente un demonio que se encarga de recordar claves. Si por alguna razón se desea pararlo, puede hacerse:

$ gpgconf --kill gpg-agent

Nota

Si nuestra intención es consultar cuáles es el cifrado sin descifrar en absoluto, podemos hacer:

$ gpg --list-only -d saludo.txt.asc

9.1.2.3.2.3. Cifrado asimétrico

Para la generación de una pareja de claves basta hacer:

$ gpg --gen-key --default-new-key-algo rsa3072

que nos pedirá el nombre de su propietario y la dirección de correo electrónico, ya que al estar este software orientado a la firma de mensajes de correo, esta dirección se usará como identificador para las claves generadas. Además, se nos pedirá una clave simétrica con la que cifrar la clave privada. Esta contraseña deberá consignarse cada vez que la clave privada tenga que usarse y es una simple medida de seguridad para evitar que, si la clave privada cae en manos ajenas, el ladrón tenga fácil usurpar la identidad del legítimo propietario. Se han añadido, además, dos datos relevantes: el tiempo de vigencia de la clave (un año), que de forma predeterminada es eterno, y el tipo de algoritmo4. La orden no sólo genera las claves, sino que las almacena en el anillo de claves (todo dentro de ~/.gnupg), con lo que podremos consultar su existencia listando cuáles son las claves públicas almacenadas:

$ gpg --list-keys --keyid-format short
/home/usuario/.gnupg/pubring.kbx
--------------------------------
pub   rsa3072/B0B83042 2019-11-08 [SC] [caduca: 2021-11-07]
      F08A6107385FE48775100943E3DCBB0AB0B83042
uid      [  absoluta ] Licenciado Cebadilla (cuenta de pruebas) <xxxx@gmail.com>

Nota

Obsérvese que la clave, tal como se ha generado, sólo sirve para firmar y no para cifrar (no aparece E dentro de los corchetes). Si nuestra intención es usarla también para cifrar entonces deberemos añadir una subclave para cifrado:

$ gpg  --quick-add-key F08A6107385FE48775100943E3DCBB0AB0B83042

También podemos comprobar las claves privadas:

$ gpg --list-secret-keys

Por ahora sólo veremos una y una respectivamente. Lo habitual es que dispongamos de una única clave privada y muchas públicas, ya que podemos importar a nuestro repositorio claves públicas ajenas.

En versiones modernas de gpg existe la opción --full-gen-key que pregunta interactivamente otras opciones como el propio algoritmo o el tiempo de vigencia. En cualquier caso, si se quiere alterar algún parámetro, como el tiempo de vigencia, puede usarse la opción --edit-key:

$ gpg --edit-key xxxx@gmail.com

Importación/exportación local de claves

Como debemos compartir nuestra clave pública con el resto de usuarios y, a su vez, recibir de éstos sendas claves públicas, GnuPG provee de mecanismos para la importación y exportación de claves.

Para exportar una clave pública del repositorio, podemos hacer:

$ gpg --export xxxx@gmail.com > clave.asc

Nota

La selección de la clave puede hacerse con cualquier parte de la identificación que se usó al crear la clave (el correo electrónico es una de ellas). Si no especificamos ninguna clave en concreto, se exportaran todas.

Si, además, queremos exportar la clave privada, podemos añadirla al fichero anterior:

$ gpg --export-secret-keys xxxx@gmail.com >> clave.asc

El proceso inverso de importar claves es también sencillo:

$ gpg --import clave.asc

orden que importará todas las claves contenidas en el fichero5.

Nota

Si la importación se hace del siguiente modo:

$ gpg --import --import-options import-show --dry-run clave.asc

se muestran los datos de la clave o claves que se importarán, pero al incluir también -dry-run no se hará efectiva la importación, con lo que el resultado es que tenemos un método para consultar las claves contenidas en un fichero.

Importación/Exportación remota de claves

Hasta ahora, hemos importado y exportado claves a o desde ficheros. Ahora bien, existen servidores PGP que almacenan claves públicas y que permiten importarlas lo que facilita el intercambio de claves. Dependiendo de cuál sea el servidor Puede accederse a través de distintos protocolos. El fichero de configuración de configuración define un servidor que soporta un protocolo seguro por el puerto 443 (lo que puede ayudarnos si estamos dentro de una red que restringe el acceso a internet):

$ gpg --send-keys B0B83042

Advertencia

En versiones modernas, para poder hacer esta exportación remota es necesario que se encuentre instalado el paquete dirmngr.

La importación de claves, por su parte, puede hacerse así:

$ gpg --recv-keys 00188366

si se conoce el ID y, si no es así. es posible buscar la clave usado alguna porción de la cadena de identificación (p.e. el correo electrónico):

$ gpg --search-keys xxxx@gmail.com

Revocación

Es posible que deseemos anular una clave antes de que esta expire por algún motivo. Para ello debemos generar una revocación e importarla a nuestro anillo de claves:

$ gpg --gen-revoke xxxx@gmail.com | gpg --import

[...]

Para revocar también esta clave en el servidor público al que exportamos esta clave con anterioridad, basta con exportar la clave ahora revocada de nuevo:

$ gpg --send-keys B0B83042

Cifrado

Para cifrar un mensaje con la clave pública de alguien a fin de que sólo éste sea capaz de descifrarlo puede hacerse:

$ gpg -er su_correo@dominio.com -o - fichero.txt > fichero.txt.asc

o bien, si se desea codificar lo remitido por la entrada estándar:

$ echo "Esto es un secreto" | gpg -er su_correo@dominio.com > secreto.asc

Nota

-o - permite que la salida cifrada vaya a la salida estándar, ya que de lo contrario se escribirá en un fichero que se llamará igual que el original adjuntando el prefijo .asc (o .gpg si se usa la opción --no-armor). Si el mensaje original procedía de la entrada estándar, se dirige directamente a la salida estándar y, en consecuencia, no es necesario.

Para descifrar la clave en un sistema que tenga disponible la clave privada corespondiente a la pública con la que se firmó, basta con:

$ gpg -qd secreto.asc
Esto es un secreto

9.1.2.3.2.4. Resúmens

Aunque no está pensado para ello, también pueden obtenerse el resumen de un fichero:

$ gpg --print-md sha512 fichero,txt

e incluso hay una opción que calcula los resúmenes según todos los algoritmos disponibles:

$ gpg --print-mds fichero,txt

Ver también

Hay un extenso tutorial del uso de GnuPG en la wiki de Archlinux.

9.1.2.3.3. Generación de claves: OpenSSL

OpenSSL es otra herramienta que permite generar y utilizar claves.

Advertencia

La utilidad de tratar esta herramienta, más que en la de las claves en sí para lo cual nos basta la anterior, está en que puede manejar certificados digitales. incluidos los emitidos por la FNMT, y, en consecuencia, obtener de ellos las claves pública y privada que contienen.

9.1.2.3.3.1. Cifrado simétrico

Una forma de llevarlo a cabo es esta:

# openssl enc -aes256 -pbkdf2 -a -in fichero.txt -out fichero.txt.enc

que utiliza AES-256 para el proceso y genera un cifrado en base64 gracias a la opción -a (equivalente a armor en gpg). Para descrifrar basta con añadir la opción -d y, obviamente, utilizar como entrada el texto cifrado:

# openssl enc -aes256 -a -d -in fichero.txt.enc -out fichero-recuperado.txt

Nota

Tanto -in como -out son opcionales y, si no se especifican, se entenderá que la entrada es la entrada estándar y la salida la salida estándar.

Nota

La clave simétrica para el cifrado/descifrado se pide de forma interactiva. Puede proporcionarse en la propia orden incluyendo la opción -pass:

# openssl enc -aes256 -pbkdf2 -a -pass pass:clave-secreta -in fichero.txt -out fichero.txt.enc

9.1.2.3.3.2. Cifrado asimétrico

Es obvio que para poder llevarlo es necesario previamente generar un par de claves:

# openssl genrsa -aes128 -passout pass:clave-secreta -out privkey.pem 4096
# openssl rsa -in private.pem -passin pass:clave-secreta -pubout -out pubkey.pem

Las órdenes generan un par de claves RSA (privkey.pem y pubkey.pem) de 4096 bits. Para cifrar la clave privada se usa AES 128.

Con ellas podemos cifrar y descifrar pequeños ficheros:

# echo "Hola" > saludo.txt
# openssl rsautl -encrypt -inkey public.pem -pubin -in saludo.txt -out saludo.enc
# openssl rsautl -decrypt -inkey private.pem -passin pass:clave-secreta -in texto.enc
Hola

En realidad, las variantes más interesantes son cuando se tiene un certificado digital, cuyo concepto trataremos más adelante, y se pretende firmar un documento, que también trataremos.

Para esto supongamos que disponemos de un certificado en formato PKCS12 llamado cert.p12, el cual traducimos a un formato manipulable por openssl:

# openssl pkcs12 -in cert.p12 -nocerts -out cert.key
# openssl pkcs12 -in cert.p12 -clcerts -nokeys -out cert.pem

El fichero PKCS12 debe estar cifrando con una clave pública por lo que en ambos casos se requerirá la clave con la que se cifró. Además, la primera orden extrae la clave privada, por lo que se requerirá una clave para su cifrado (véase en la página de openssl-pkcs12 las opciones -passin y -passout para proporcionarlas en la propia orden). Con estos ficheros ya tenemos separadas las clave pública y privada, pero con la salvedad de que hay en ellos datos del certificado por lo que las órdenes son ligeramente distintas:

# echo "Hola" > saludo.txt
# openssl rsautl -encrypt -inkey cert.pem -certin -in saludo.txt -out saludo.enc
# openssl rsautl -decrypt -inkey cert.key -passin pass:clave-secreta -in texto.enc
Hola

En concreto, la opción -certin, que sustituye a -pubin.

Nota

Para firmar un fichero, puede consultarse esta respuesta de stackoverflow.

9.1.2.3.3.3. Resúmenes

Es posible también utilizar funciones de hash:

# openssl dgst -sha256 fichero.txt
SHA256(fichero.txt)= d42650b1b27a8da408495394242e10d06f6238d15ec1c76f8f942bbdf26d419d

Notas al pie

1

De hecho, OpenSSH, para su versión 7, deshabilitó el uso de DSA.

2

Se fuerza a que la orden se ejecute en inglés, para que el resultado se exprese como OK o FAILED. En castellano, la leyenda se expresa de forma muy farragosa.

3

Usar, sin embargo, el propio kernel para la generación de la entropía no es muy recomendable. Debería usarse el dispositivo hardware (/dev/hwrng), pero en la máquina virtual es posible que no esté. Para qemu véase su wiki

4

El algoritmo elegido utiliza una clave RSA de 2048 bits tanto para cifrado como para firmado. Otro posible algoritmo es ed25519.

5

En nuestro caso, sería una clave pública y su correspondiente privada.