# Laboratorio de Criptografía de Llave Pública
En este laboratorio aprenderemos cómo intercambiar mensajes de forma criptográficamente segura. Para ello, vamos a a utilizar [GNU Privacy Guard (GPG)](https://gnupg.org/). GPG es una aplicación que implementa diferentes algoritmos de cifrado (simétricos y asimétricos), funciones hash criptográficas y algoritmos de compresión, de acuerdo al estándar de cifrado OpenPGP.
El estándar de OpenPGP define varias cosas, por ejemplo:
* El formato en el que se deben de generar las llaves
* Cómo se verifica que se hayan creado correctamente
* Con qué formato generar mensajes cifrados
* Con qué formato se deben generar las firmas digitales
OpenPGP es agnóstico a los algoritmos específicos que se utilizan para generar las llaves. Por ejemplo, OpenPGP describe que tienes que generar un conjunto de llaves públicas y privadas (no importa si es con RSA, ElGamal, etc.), con qué longitud, en qué formato guardarla, cómo leerla, etc.
OpenPGP, a su vez, está basado en PGP (Pretty Good Privacy), un software de cifrado desarrollado por Phil Zimmermann, un activista de la privacidad digital, en 1991.
## I. Instalación y creación de llaves
En esta sección, vamos a descargar e instalar GPG para crear nuestro conjunto de llaves y publicarla en un servidor público de llaves.
### 1.1 Instalación de GPG
1. Descarga e instala GPG de https://gnupg.org.
2. Verifica que GPG esté instalado correctamente.
```
gpg --version
```

### 1.2 Creación de llaves
1. Crea tu conjunto de llaves.
Ejecuta el siguiente comando:
```
gpg --full-generate-key
```
...y crea un conjunto de llaves con las siguientes características:
- **Tipo de llave**: (1) RSA (autentificación/firmas) / RSA (cifrado/descifrado)
- **Longitud de la llave**: 4096 bits
- **Fecha de expiración**: Sin fecha de expiración
- **Nombre**: Tu nombre
- **Correo**: Tu correo del Tec
- **Passphrase**: `<Passphrase segura>`





2. Verifica que la llave se haya creado correctamente.
```
gpg --list-keys
```
Te debe aparecer en terminal un output similar al siguiente:
```
/Users/msalazar/.gnupg/pubring.kbx
----------------------------------
pub rsa4096 2021-02-28 [SC]
05AE91D601C118A85EB7C4F4313CE48FD535C88B
uid [ultimate] Satoshi Nakamoto <satoshi@vistomail.com>
sub rsa4096 2021-02-28 [E]
```


¡Felicidades 🥳 🎉🎉🎉, ya creaste tu conjunto de llaves públicas! 🙌
### 1.3 Exportación y publicación de llaves
Tus llaves públicas y privadas y otros datos y configuraciones están guardadas dentro de tu directorio de trabajo (usualmente `~/.gnupg`, `%APPDATA%/gnupg`). Para conocer la ubicación exacta de tu directorio Home, puedes ejecutar `gpg --version`.
Por seguridad, las llaves privadas guardadas en el llavero se encuentran cifradas con la contraseña que definiste.
Las llaves públicas se encuentran guardadas en una base de datos local, llamada keyring (o llavero) [para las personas curiosas: `pubring.kbx`]. 🤔 Suena parecido a una wallet, ¿no?
No podemos leer esa base de datos directamente (es decir, en texto plano), por lo tanto, necesitamos interactuar con ella para exportar nuestras llaves.
#### Exportando llaves públicas
La forma más simple de exportar una llave pública, es la siguiente:
```
gpg --export satoshi@vistomail.com
```

🤨 . No es demasiado legible. Exportémosla mejor a un archivo.
```
gpg --export satoshi@vistomail.com > snakamoto.pub
```

Mejor 🙌. Ya podemos compartir ese archivo con cualquier persona que necesite acceso a nuestra llave pública.
Sin embargo, existe una forma más amigable de compartir llaves: en texto plano. Para ello, vamos a codificar en formato ASCII nuestra llave pública utilizando un ASCII armor. Un ASCII armor es una función que convierte datos de binario a texto plano.
```
gpg --armor --export satoshi@vistomail.com
```
Esta función debió haber imprimido en tu terminal algo similar a lo siguiente:
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGA66DwBEADX3qyxRNsRBkeSGQLFwRk5
...
...
...
cSIQLDFrTnu9bTcAl2StO2pY0MdbYBk=QLod
-----END PGP PUBLIC KEY BLOCK-----
```
#### Publicando llaves públicas en un servidor de llaves públicas
Ahora vamos a publicar nuestra llave pública en un [servidor de llaves públicas](https://en.wikipedia.org/wiki/Key_server_(cryptographic)). Estos servidores son mantenidos por diferentes organizaciones, y son algunos de los lugares en donde podemos guardar nuestras llaves públicas (pero no el único).
1. Copia tu llave pública en formato ASCII armor (incluye la primera y última línea que indican el inicio de la llave.).
2. Accede a uno de los siguientes servidores de llaves:
- http://keys.gnupg.net
- http://pgp.mit.edu
- http://keyserver.ubuntu.com/
3 Utiliza la función de **Submit Key** para publicar tu llave en el servidor.

:point_down:

### 1.4 Descargando e importación de llaves
#### Importando un archivo ASCII
1. Entra a https://pgp.mit.edu o http://keys.gnupg.net.
2. Busca la llave asociada a mi correo electrónico (miguel.salazar@tec.mx).
3. Da click en la llave 4096R/3029802F (¡verifica que sea la correcta y no la del impostor!).
4. Copia el ASCII armor de mi llave.
5. Crea un archivo nuevo, llamado `msalazar.asc`.
6. Pega mi llave en el archivo y guarda los cambios.


Para importar una llave, ejecuta el siguiente comando:
```
gpg --import msalazar.asc
```

Ahora elimina mi llave que acabas de importar.
```
gpg --delete-keys miguel.salazar@tec.mx
```
### Importando una llave desde terminal
1. Vamos a importar mi llave pública desde la terminal 😎 . Ejecuta el siguiente comando:
```
gpg --keyserver pgp.mit.edu --search-keys miguel.salazar@tec.mx
```
(Si el servidor está muy lento, utiliza el servidor de keys.gnupg.net.)
2. ⚠️ Elige la llave que concuerde con mi huella digital `D8A3F32FEBAFD5EFF2EC88187DEBAC883029802F` (usualmente se despliegan los últimos 12 ó 16 dígitos. ¡Verifica que sea la correcta y no la del impostor! 🧟

3. También puedes importar mi llave utilizando mi huella digital (fingerprint):
```
gpg --keyserver pgp.mit.edu --recv-keys D8A3F32FEBAFD5EFF2EC88187DEBAC883029802F
```
4. Verifica que hayas importado mi llave pública correctamente:
```
gpg --list-public-keys
```
# II. Cifrado y firma de mensajes
Ahora vamos a intercambiar mensajes .
## 2.1. Cifrando un mensaje.
Ya que tienes mi llave pública, ahora puedes cifrar un mensaje con ella y asegurarte que sólo yo pueda leerlo.
1. Crea un archivo de text plano (ej. `hello.txt`).
2. Escribe un mensaje en el archivo y guárdalo.

3. Cifra el archivo:
```
gpg --encrypt hello.txt
```
En el prompt, especifica el ID (en este caso, el ID es el email) asociado con la llave pública con la que vamos a cifrar el mensaje:
```
* User ID: miguel.salazar@tec.mx
* Use this key anyway? y
<Enter> para no agregar más receptores y salir.
```
O puedes hacerlo con un solo comando:
```
gpg --encrypt -r miguel.salazar@tec.mx hello.txt
```

**Nota**: Podemos especificar IDs por medio de huellas digitales. En la captura de arriba, en verde estoy especificando la huella digital de la llave pública con la que quiero cifrar el mensaje y en rojo la llave pública del receptor del mensaje. Si solo tiene un conjunto de llaves creadas, no es necesario que utilices esas opciones.

Verifica que se haya creado un archivo con el mismo nombre + extensión `.gpg` (ej. `hello.txt.gpg`) en el mismo directorio.

De acuerdo a lo que vimos en clase, ahora podemos estar seguros que solamente quien tenga el la llave **privada** puede descifrar el contenido del mensaje cifrado con su respectiva llave pública.
Sin embargo, mi llave pública es... **pública**, cualquiera puede tener acceso a ella. ¿Cómo puedo estar seguro de quién me envió ese mensaje? 🤔
Para ello, vamos a firmar el mensaje.
## 2. Firmando un mensaje.
### Anexando la firma al documento
Ahora vamos a firmar un mensaje con nuestra llave privada para atestar que, en efecto, fuimos nosotros quien firmamos ese mensaje.
**Nota**: En términos más precisos, no somos técnicamente nosotros quien firmamos el mensaje, sino nuestra llave privada quien firmó ese mensaje. Si perdemos nuestra llave privada, cualquier persona podría firmar mensajes con ella. Por eso es importante guardar las llaves privadas de forma cifrada y segura y no compartirlas con nadie.
En Bitcoin, funciona de la misma forma. Si alguien tiene nuestras llaves privadas, en vez de poder "firmar documentos" podrá "firmar transacciones".
1. Firmemos un mensaje.
```
gpg --output hello.sig --sign hello.txt
```

Al firmar este mensaje, estamos creando un hash criptográfico del archivo `hello.txt` utilizando nuestra llave privada y guardamos la firma en el archivo `hello.sig`. De esta manera, si alguien modifica el contenido de `hello.txt`, al verificarlo utilizando nuestra llave pública, habría un hash diferente.
Una forma de pensar en esto es:
```
1. Abrimos hello.txt.
2. Agrego una cadena de texto con la llave privada
3. Guardamos el documento
4. Creamos un hash de ese nuevo documento.
```
```
hash(llave privada + hello.txt) = hello.sig
```
El documento generado (`hello.sig`) es un documento que incluye el mensaje original y la firma, combinados en un nuevo archivo.
Otra forma de pensar acerca de esto es como si quisiéramos firmar un archivo PDF, firmamos el documento con nuestra firma manuscrita y guardamos el archivo, generando una nueva versión del documento.
### Firmas acompañantes
Un problema del método anterior es que el archivo original se modifica y se crea un nuevo archivo. Cuando hablamos de un archivo como una imagen o PDF tiene mucho sentido. Sin embargo, esto puede ser un problema en otros contextos ☹️. Por ejemplo, ¿qué pasa si el archivo que queremos firmar es un MP3? El equivalente directo sería agregar algún sonido "firmando" el mensaje, pero eso podría arruinar la canción.
¿Qué tal si firmamos entonces con el mecanismo anterior? El problema con el que nos encontraríamos sería que podríamos estar modificando el formato del archivo original y quizá ya no pueda ser leído por el software que reproduce MP3.
¿Por qué no entonces mejor generamos una firma y mantenemos intacto el archivo original?
De esta forma, tendríamos lo siguiente:
```
hash(llave privada, hello.txt) = hello.sig + hello.txt
```
O, en términos simples: Hasheo el documento `A` con mi llave privada `B` y genero una firma `C`. En este caso, sabría que si tengo el documento original `A` y la firma del documento `C`, y la llave pública que está en función de `B`, puedo comprobar que ese documento, con esa llave pública generaron esa firma.
Pongámoslo en práctica:
```
gpg --output hello-detached.sig --detach-sig hello.txt
```
Ahora tenemos dos archivos:
* `hello.txt`, nuestro archivo original.
* `hello-detached.sig`, nuestra firma acompañante.

Ya podemos verificar que `hello-detached.sig` haya sido generado con `hello.txt` y la llave privada que derivó a la llave pública con la que vamos a verificar la firma más adelante.
```
gpg --verify hello.sig hello.txt
```

### Firmando un mensaje cifrado
La firma de un documento (en este caso, nuestro mensaje) nos permite atestar que nostros firmamos un documento en particular. O en términos más precisos "la persona que tenía acceso a la llave privada _P_ firmó el documento".
Cualquier persona que tenga acceso a nuestra llave pública _Q_ puede verificar eso.
Sin embargo, en nuestro caso anterior el archivo original sigue intacto. Si ese mensaje tuviera información importante, quien sea que tenga acceso lo podría leer.
📌 Recuerda que **cifrar** es diferente que **firmar**. Cuando _ciframos_ el mensaje, utilizamos la llave pública del **receptor**. Cuando _firmamos_ un mensaje, lo hacemos utilizando nuestra propia llave **privada**.
Vamos entonces a cifrar ese archivo para asegurarnos que solo lo pueda leer quien queramos que lo lea.
1. Ciframos el archivo con la llave privada de nuestro receptor.
```
gpg --encrypt -r miguel.salazar@tec.mx hello.txt
```
Ahora tenemos un archivo cifrado llamado `hello.txt.gpg`.
2. Firmamos el archivo cifrado.
```
gpg --output hello.sig --detach-sig --sign --local-user satoshi@vistomail.com hello.txt.gpg
```
De esta forma, podemos garantizar lo siguiente:
* Solamente el receptor puede descifrar el mensaje.
* El receptor puede verificar que la el mensaje está firmado con la llave privada del emisor.
🙌 ¡Ya puedes enviar mensaje de forma segura! 🤓
### Envío de actividad
Envíame un correo a miguel.salazar@tec.com con lo siguiente:
* El archivo cifrado con tu mensaje (`<matricula>.txt.gpg`).
* La firma acompañante de tu mensaje (`<matricula>.sig`).
* Incluye tu llave pública (`<matricula>.asc`).
Nota que no tienes que enviar el mensaje de texto original para garantizar que solo el receptor a quien queremos enviarle el mensaje lo pueda leer, y el receptor tendrá garantía de que fuimos nosotros quien lo enviamos.
Aquí tienes una guía sobre cómo hacerlo:
```
gpg --gen-key
gpg --keyserver pgp.mit.edu --send-key <matricula>@tec.mx
gpg --keyserver pgp.mit.edu --recv-keys D8A3F32FEBAFD5EFF2EC88187DEBAC883029802F
touch <matricula>.txt
echo "<Tu mensaje>" >> <matricula>.txt
gpg --encrypt -r D8A3F32FEBAFD5EFF2EC88187DEBAC883029802F <matricula>.txt
gpg --output <matricula>.sig --detach-sig --sign --local-user <matricula>@tec.mx <matricula>.txt.gpg
```
## III. Descifrado y verificación de documentos
A la brevedad posible, te responderé tu correo con otro mensaje firmado por mi (en este ejemplo será `hola.txt.gpg`) que descifraremos el y verificaremos su firma digital (`hola.sig`) .
1. Primero, verifica su autenticidad.
```
gpg --verify hola.sig hola.txt.gpg
```

2. Ahora, descifra el mensaje.
```
gpg --decrypt hola.txt.gpg
```

3. Sube a Canvas el contenido descifrado mensaje que te envié.
## IV. Retos adicionales (Opcional)
* Descarga de nuevo el binario cliente de Bitcoin Core y su firma digital. Verifica su firma.
* Agrega a tu Web of Trust las llaves públicas de tus compañeros y compañeras de clase.
* Agrega a tu firma de correo electrónico (personal o del Tec) un enlace a tu llave pública y su huella digital. Ejemplo:
```
PGP Key: https://pgp.mit.edu/pks/lookup?op=get&search=0x7DEBAC883029802F
Fingerint: D8A3 F32F EBAF D5EF F2EC 8818 7DEB AC88 3029 802F
```
* Publica la huella digital, enlace a tu llave pública y/o ASCII armor de tu llave pública en tus perfiles (ej. website personal, Twitter, GitHub, etc.).
## V. Investigación Adicional (Opcional)
* Documentos con firmas ASCII (clearsign documents)
* Renovación de llaves
* Revocación de llaves
* Exportar/Importar llaves privadas
* Web of Trust
## VI. Recursos adicionales
* [The GNU Privacy Handbook](https://www.gnupg.org/gph/en/manual.html)
* [SKS Key Servers](https://sks-keyservers.net/status/)