← Blog

GPG - Cifrado y Firma Digital

Cifrado simétrico y asimétrico con GPG: generación de claves, encriptación, desencriptación y conceptos de RSA.

GPG

Theory

gpg is the OpenPGP (Pretty Good Privacy) part of the GNU Privacy Guard (GnuPG). It is a tool to provide digital encryption and signing services using the OpenPGP standard. gpg features complete key management and all the bells and whistles you would expect from a full OpenPGP implementation.

The gpg utility has a lot of options, but fortunately for us, encrypting and decrypting are easy to do and only require that you know three options for quick use: Create or encrypt (-c), decrypt (-d), and extract and decrypt (no option).


Practical: Working with GnuPG

1. Preparar un entorno seguro (opcional pero recomendado)

Para aprender y experimentar sin afectar tu configuración principal:

mkdir -p ~/gpg-lab
chmod 700 ~/gpg-lab
export GNUPGHOME=~/gpg-lab

Todo comando de gpg en esta terminal usará ~/gpg-lab como directorio base.


2. Encrypt a file (Symmetric Encryption)

Para cifrado simétrico (con contraseña):

echo "This is an encryption test" > file1.txt
gpg -c file1.txt
  • -c indica cifrado simétrico (solo se usa una contraseña).
  • Se te pedirá una passphrase para proteger el archivo.
  • El archivo resultante será file1.txt.gpg.

Puedes verificar el tipo de archivo:

file file1.txt.gpg

Puedes renombrar el archivo sin afectar su contenido:

mv file1.txt.gpg testfile01.doc
file testfile01.doc

Nota: Aunque cambies la extensión, GPG sigue reconociendo el archivo como PGP cifrado.


3. Decrypt a file (Symmetric Decryption)

Para recuperar el contenido original:

gpg -d testfile01.doc > recovered.txt
  • -d o --decrypt extrae el contenido.
  • Te pedirá la passphrase si el gpg-agent no la tiene en caché.
  • Verifica el archivo recuperado:
cat recovered.txt

Concepto clave: gpg-agent guarda la contraseña temporalmente en memoria. Si no quieres que la cachee, mata el agente antes de descifrar:

gpgconf --kill gpg-agent

Conceptos clave:

  • El cifrado simétrico usa una contraseña compartida.
  • El cifrado asimétrico usa clave pública para cifrar y clave privada para descifrar.
  • Puedes combinar ambos métodos: cifrar un archivo con una clave pública y proteger la clave privada con passphrase.

1. file1.txt.gpg

  • Es el archivo cifrado generado por GPG.
  • Contiene los datos originales de file1.txt, pero cifrados.
  • Si se usó -c, está protegido con contraseña simétrica; si se usó cifrado con clave pública, está protegido con la clave pública del destinatario.
  • No es legible directamente; necesita gpg -d o la clave correcta para descifrarlo.

2. private-keys-v1.d/

  • Es un directorio que almacena tus claves privadas en GPG.
  • Cada clave privada está en un archivo separado, usualmente en un formato seguro (cifrado con tu passphrase).
  • Solo el propietario del directorio debe tener acceso (permisos 700 o más restrictivos).
  • Nunca se debe compartir fuera de entornos seguros.

3. pubring.kbx

  • Es el keyring de claves públicas.
  • Contiene todas las claves públicas que has importado o generado, usadas para cifrar datos o verificar firmas.
  • Formato moderno .kbx (KeyBox) reemplaza al antiguo pubring.gpg.
  • Puedes exportar estas claves para compartirlas con otros.

4. random_seed

  • Es un archivo que guarda datos de entropía aleatoria usados por GPG.
  • GPG lo utiliza para mejorar la generación de claves y cifrados simétricos.
  • Se actualiza automáticamente al generar claves o cifrar/descifrar.
  • Es un archivo interno; no se modifica directamente.

4. Asymmetric Encryption (Cifrado con clave pública/privada)

1. Generar un par de claves (pública/privada)

gpg --full-generate-key
  • Tipo de clave: RSA y RSA (opción 1)
  • Tamaño: 4096 bits (recomendado)
  • Validez: según tu criterio
  • Nombre, correo y comentario opcional
  • Passphrase: protege tu clave privada

Esto generará tu clave privada y la clave pública correspondiente, que quedan almacenadas en ~/.gnupg/private-keys-v1.d/ y ~/.gnupg/pubring.kbx.


2. Exportar la clave pública

Para enviar a un destinatario o usar para cifrar:

gpg --export -a "Nombre del Usuario" > mypublickey.asc
  • -a exporta en formato ASCII (legible).
  • El archivo mypublickey.asc puede compartirse libremente.

3. Cifrar un archivo con la clave pública

echo "Este es un mensaje secreto" > message.txt
gpg --encrypt --recipient "Nombre del Usuario" message.txt


gpg --import mypublickey.asc
gpg --list-keys

/home/haise/gpg-lab/pubring.kbx
-------------------------------
pub   rsa4096 2025-08-29 [SC] [expires: 2025-08-31]
      2B4D6CEFE2B5AF96B064D893E903090BE0D3E713
uid           [ultimate] test (test) <test@test.test>
sub   rsa4096 2025-08-29 [E] [expires: 2025-08-31]

gpg --encrypt --recipient "test (test) <test@test.test>" message.txt

  • Se genera un archivo message.txt.gpg.
  • Solo el propietario de la clave privada correspondiente puede descifrarlo.

4. Descifrar con la clave privada

Si recibes message.txt.gpg:

gpg --decrypt message.txt.gpg > decrypted.txt
  • Te pedirá la passphrase de la clave privada si no está en caché.
  • El contenido original queda en decrypted.txt.

Conceptos clave

  • Cifrado asimétrico: usa clave pública para cifrar y clave privada para descifrar.
  • Seguridad: la clave privada nunca debe compartirse.
  • Compatibilidad: puedes cifrar para varios destinatarios usando varias opciones --recipient.

Resumen corto de criptografía

Existen dos grandes familias de criptografía: la de llave privada y la de llave pública. Históricamente, la criptografía de llave privada (simétrica) apareció primero; el criptosistema de llave pública (asimétrica) llegó después como respuesta al problema de tener que compartir una clave secreta de forma segura entre dos partes.

Criptografía clásica

Los cifrados clásicos por carácter (o monoalfabéticos) reemplazan cada letra del alfabeto por otra letra. Dos ejemplos típicos son el shift cipher y los affine ciphers. Su debilidad principal es que son vulnerables al análisis de frecuencia de letras en el texto cifrado, ya que ciertas letras aparecen con más frecuencia que otras en cualquier idioma. Una forma de mitigar esto es agrupar y reemplazar bloques de letras en lugar de letras individuales, dando lugar a los llamados block ciphers.

Criptografía de llave pública

En este modelo, cada persona tiene una clave para cifrar (pública) y otra distinta para descifrar (privada). El destinatario ("recipient") comparte su clave pública abiertamente, pero guarda su clave privada en secreto.

RSA

RSA es el algoritmo de llave pública más utilizado. Cifra mensajes mediante exponenciación modular, donde el módulo es el producto de dos números primos grandes.

Para cifrar un mensaje basta con conocer el módulo nn y el exponente público ee; no hace falta conocer los dos primos que forman nn. Para descifrar, en cambio, sí se necesitan esos dos primos, ya que son la base para calcular el exponente privado dd. Esta asimetría —fácil de cifrar con datos públicos, imposible de descifrar sin los primos— es lo que hace seguro al sistema.

En los ejemplos didácticos se usan primos pp y qq pequeños para que los cálculos sean manejables a mano; en la práctica real, cada primo tiene 300 dígitos o más.

En el sistema RSA, cada persona tiene una clave de cifrado (n,e)(n, e), donde n=pqn = pq es el módulo (resultado de multiplicar los dos primos grandes) y ee es un exponente relativamente primo a (p1)(q1)(p-1)(q-1). Con primos de unos 300 dígitos cada uno, nn termina teniendo alrededor de 600 dígitos, un tamaño en el que factorizarlo de vuelta en pp y qq resulta computacionalmente inviable incluso para una computadora potente.

Proceso de encriptación:

  1. El mensaje en texto plano MM se traduce a una secuencia de enteros de dos dígitos: A = 01, B = 02, y así sucesivamente, hasta obtener una secuencia m1,m2,,mkm_1, m_2, \dots, m_k.
  2. Estos números se concatenan en una sola cadena de dígitos.
  3. La cadena se divide en bloques de igual longitud, de 2N2N dígitos cada uno (donde 2N2N es el mayor número par de dígitos que garantiza que el bloque, convertido a número, siga siendo menor que nn). Si el último bloque queda incompleto, se rellena con letras ficticias, típicamente X, para completar la longitud.
  4. Cada bloque mim_i se transforma en un bloque de texto cifrado cic_i aplicando:
ci=miemodnc_i = m_i^e \bmod n

¿Por qué se usa el exponente e?

En RSA, la clave pública es el par (n,e)(n, e). El exponente ee es el valor usado en la exponenciación modular al cifrar:

cme(modn)c \equiv m^e \pmod{n}

Su rol es ser parte de la función de encriptación: transforma el mensaje mm en el texto cifrado cc. Para que el esquema funcione, ee debe elegirse de modo que tenga un inverso multiplicativo respecto a φ(n)\varphi(n), es decir:

gcd(e,φ(n))=1\gcd(e, \varphi(n)) = 1

donde φ(n)=(p1)(q1)\varphi(n) = (p-1)(q-1) es la función totiente de Euler. Ese inverso multiplicativo es dd, el exponente privado, y satisface:

ed1(modφ(n))e \cdot d \equiv 1 \pmod{\varphi(n)}

En otras palabras: ee permite encriptar, y gracias a esta relación matemática, dd permite desencriptar.

En la práctica se suele escoger un ee pequeño y fijo (como 65537), porque acelera la operación de encriptación, siempre que cumpla la condición de ser coprimo con (p1)(q1)(p-1)(q-1).


¿Por qué aparece (p-1)(q-1)?

La fórmula φ(n)=(p1)(q1)\varphi(n) = (p-1)(q-1) proviene de la función totiente de Euler, que cuenta cuántos enteros menores que nn son coprimos con nn.

Para un número primo pp, todos los números menores que pp son coprimos con pp, así que φ(p)=p1\varphi(p) = p - 1. Como n=pqn = pq con pp y qq primos distintos, la propiedad multiplicativa del totiente da:

φ(pq)=φ(p)φ(q)=(p1)(q1)\varphi(pq) = \varphi(p) \cdot \varphi(q) = (p-1)(q-1)

Este valor se usa porque es la base para aplicar el teorema de Euler, que garantiza que:

mφ(n)1(modn)si gcd(m,n)=1m^{\varphi(n)} \equiv 1 \pmod{n} \quad \text{si } \gcd(m, n) = 1

Este resultado es justamente lo que hace posible que cifrar y descifrar sean operaciones inversas entre sí.


Resumen del proceso de RSA

Generación de claves:

  1. Escoger dos primos grandes p,qp, q.
  2. Calcular n=pqn = p \cdot q.
  3. Calcular φ(n)=(p1)(q1)\varphi(n) = (p-1)(q-1).
  4. Elegir ee tal que 1<e<φ(n)1 < e < \varphi(n) y gcd(e,φ(n))=1\gcd(e, \varphi(n)) = 1.
  5. Calcular dd como el inverso modular de e(modφ(n))e \pmod{\varphi(n)}.
  6. La clave pública es (n,e)(n, e); la clave privada es (n,d)(n, d).

Encriptación (con la clave pública):

c=memodnc = m^e \bmod n

Desencriptación (con la clave privada):

m=cdmodnm = c^d \bmod n

Esto funciona porque ed1(modφ(n))e \cdot d \equiv 1 \pmod{\varphi(n)}, lo que garantiza que descifrar recupera exactamente el mensaje original.


Ejemplo rápido (mini-RSA, inseguro en la práctica)

Escogemos p=7p = 7, q=11q = 11. Entonces:

  • n=711=77n = 7 \cdot 11 = 77
  • φ(n)=(71)(111)=610=60\varphi(n) = (7-1)(11-1) = 6 \cdot 10 = 60
  • Escogemos e=7e = 7 (coprimo con 60)
  • Calculamos dd como el inverso de 7 mod 60: d=43d = 43, porque 743=3011(mod60)7 \cdot 43 = 301 \equiv 1 \pmod{60}
  • Clave pública: (77,7)(77, 7); clave privada: (77,43)(77, 43)
  • Mensaje: m=20m = 20

Encriptación:

c=207mod77=51c = 20^7 \bmod 77 = 51

Desencriptación:

m=5143mod77=20m = 51^{43} \bmod 77 = 20

Conexión con los bloques de 2N dígitos

Los mensajes (letras) se convierten en números y luego se agrupan en bloques de longitud calculada de forma que el bloque, convertido a número, siempre sea menor que nn. De lo contrario, la operación modular se rompe: si un bloque representa un número mayor o igual a nn, la reducción módulo nn ya no distingue ese valor de otros equivalentes, y se pierde información del mensaje original. Por eso se calcula ese límite de "2N dígitos" antes de cifrar.


Explicación detallada del proceso de RSA (cifrado)

1. Traducción del mensaje a números

Cada letra se representa con 2 dígitos: A = 00, B = 01, ..., J = 09, K = 10, ..., Z = 25. Esto garantiza que todas las letras tengan la misma longitud.

Ejemplo: "STOP" → S = 18, T = 19, O = 14, P = 15 → 1819 1415

2. Agrupación en bloques

El mensaje traducido a números se concatena en una cadena de dígitos, que luego se divide en bloques de 2N2N dígitos. Ese 2N2N es el número par de dígitos más grande tal que un bloque compuesto solo por "25" repetidos (el valor máximo posible de una letra) siga siendo menor que nn.

En el ejemplo, n=2537n = 2537. El número 2525 (4 dígitos) es menor que 2537, pero 252525 (6 dígitos) ya es mayor. Por lo tanto, los bloques deben tener 4 dígitos: "STOP" → 1819 1415

3. Encriptación de cada bloque

Cada bloque mim_i se encripta con:

ci=mie(modn)c_i = m_i^e \pmod{n}

Con clave pública (n=2537,e=13)(n = 2537, e = 13) y bloques m1=1819m_1 = 1819, m2=1415m_2 = 1415:

  • 181913mod2537=20811819^{13} \bmod 2537 = 2081
  • 141513mod2537=21821415^{13} \bmod 2537 = 2182

Resultado cifrado: 2081 2182


Explicación de RSA - Desencriptación

1. Clave de desencriptación

Para descifrar se necesita el exponente dd, el inverso modular de ee respecto a (p1)(q1)(p-1)(q-1).

Con p=43p = 43, q=59q = 59:

  • (p1)(q1)=4258=2436(p-1)(q-1) = 42 \cdot 58 = 2436
  • e=13e = 13
  • dd satisface 139371(mod2436)13 \cdot 937 \equiv 1 \pmod{2436}, así que d=937d = 937

2. Proceso de desencriptación

Para cada bloque cc se aplica:

m=cd(modn)m = c^d \pmod{n}

Esto funciona porque de1(modφ(n))de \equiv 1 \pmod{\varphi(n)}, lo que asegura que la potencia cdc^d deshace exactamente la operación de cifrado mem^e. Esto se demuestra formalmente con el teorema de Euler y, en implementaciones reales, se acelera con el teorema chino del resto (CRT).

3. Ejemplo de desencriptación

Mensaje recibido: 0981 0461, con clave (n=2537,e=13,d=937)(n = 2537, e = 13, d = 937).

  • 981937mod2537=0704981^{937} \bmod 2537 = 0704
  • 461937mod2537=1115461^{937} \bmod 2537 = 1115

Reconstrucción del mensaje:

  • 0704 → 07 04 → H, E → "HE"
  • 1115 → 11 15 → L, P → "LP"
  • Mensaje final: HELP

Resumen conceptual

  • Encriptación: letras → números → bloques → c=memodnc = m^e \bmod n.
  • Desencriptación: bloques cifrados → m=cdmodnm = c^d \bmod n → números → letras.
  • El sistema funciona porque ee y dd son inversos modulares respecto a (p1)(q1)(p-1)(q-1), el teorema de Euler garantiza la reversibilidad, y el teorema chino del resto asegura que la congruencia se cumple simultáneamente en pp y en qq.

El módulo n

En RSA, el módulo nn se define como:

n=pqn = p \cdot q

donde pp y qq son dos números primos grandes, elegidos al inicio del proceso de generación de claves.

Rol del módulo n:

  1. Tamaño del espacio de mensajes. El mensaje, convertido en un número entero mm, debe ser menor que nn. Por eso, al convertir texto a números, se divide en bloques que no excedan el valor de nn.
  2. Base de las operaciones modulares. Tanto el cifrado como el descifrado se hacen "módulo nn", lo que asegura que los resultados siempre estén en el rango de 0 a n1n - 1.
  3. Seguridad. La seguridad de RSA descansa en la dificultad de factorizar nn en sus dos factores primos pp y qq. Aunque nn es público, obtener pp y qq es computacionalmente inviable cuando tienen cientos o miles de bits. Si un atacante lograra factorizar nn, podría calcular φ(n)=(p1)(q1)\varphi(n) = (p-1)(q-1) y de ahí obtener el exponente secreto dd.

Ejemplo práctico:

Con p=43p = 43, q=59q = 59:

n=4359=2537n = 43 \cdot 59 = 2537

La clave pública es (n,e)=(2537,13)(n, e) = (2537, 13) y la clave privada es (n,d)=(2537,937)(n, d) = (2537, 937). El módulo n=2537n = 2537 es el valor común en ambas claves: es público y se usa tanto para cifrar como para descifrar. Lo que permanece secreto no es nn, sino su factorización en pp y qq.

En resumen: el módulo nn en RSA es el producto de dos primos grandes, determina el rango numérico en el que trabajan los mensajes cifrados, y es la base de la seguridad del sistema: su factorización es el problema matemático difícil que lo protege.