He estado usando una YubiKey como llave de autenticación de dos factores y para firmar mis commits de Git. Hasta hace unos días usaba una YubiKey 4. Como mi computadora actual no tiene ningún puerto USB-A, durante el último año he tenido que andar con adaptadores de USB-A a USB-C en la mochila, y usar la llave ha sido una molestia. Por eso decidí que ya era hora de pasarme a mi YubiKey 5C “nueva” y así no tener que lidiar más con esos adaptadores.

En realidad ya tenía la llave desde hace tiempo, porque la pedí junto con una YubiKey 4 de reemplazo que conseguí por el problema del TPM de Infineon que ellos identificaron, pero apenas ahora decidí que era el momento de cambiar a la llave nueva.

La otra razón para hacerlo ahora es que mis viejas llaves GPG caducaron y tuve que volver a emitirlas, así que puedo aprovechar para usar la llave y firmar mis commits de Git.

La locura de GPG

Si has lidiado con llaves GPG en el pasado (o lo haces actualmente), sabes que puede ser bastante difícil ponerlas en marcha de forma correcta y segura si nunca lo has hecho. Mantener las llaves maestras sin conexión, ir moviendo llaves públicas, reemitir las llaves cuando caducan, quizás firmarlas con llaves anteriores para verificarlas, revocar llaves… No es fácil seguir las mejores prácticas ni siquiera en teoría. Cuando toca llevarlas a la práctica, todo se complica muchísimo.

La mayoría de los tutoriales en línea te mandan a generar las llaves desde la línea de comandos, navegando menús con un patrón de diseño poco común, y son un auténtico dolor de cabeza para ponerlas a funcionar. Casi todos dicen que uses una máquina sin conexión, como una Raspberry PI, y/o que generes las llaves maestras y las subllaves sin conexión y mantengas la llave maestra fuera de tu computadora.

🤯

Llevo casi 4 años lidiando con esto cada inicio de enero. Temo el momento en que aparece el siguiente error, avisándome de que mis llaves de firma han caducado.

error: gpg failed to sign the data
fatal: failed to write commit object

Este año decidí que era hora de intentar que el proceso fuera mucho más sencillo, usar la generación de certificados integrada y evitarme todo el lío de generar llaves GPG y demás.

Generación de llaves en la YubiKey

Con esto todavía tenemos que usar la interfaz de línea de comandos, pero es un poco más evidente porque tiene menos pasos.

Primero tenemos que configurar nuestra tarjeta/dispositivo.

Configuración de la tarjeta/dispositivo

Ejecuta gpg --card-status en la terminal y deberías ver algo como esto:

Reader ………..: Yubico Yubikey 4 OTP U2F CCID
Application ID …: <Serial number>
Version ……….: 2.1
Manufacturer …..: Yubico
Serial number ….: <Device serial number>
Name of cardholder: unspecified
Language prefs …: en
Sex …………..: unspecified
URL of public key : \[not set\]
Login data …….: <e-mail or nothing>
Signature PIN ….: not forced
Key attributes …: rsa4096 rsa4096 rsa4096
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 9

Si ves algo parecido y no aparece ningún error, puedes continuar.

Ejecuta gpg --card-edit

Llegarás a una interfaz de línea de comandos que muestra la misma información del inicio y, al final, un prompt gpg/card>.

Escribe admin y presiona Enter.

Ahora puedes escribir help para saber cómo cambiar la información del titular y los PIN de la tarjeta.

Algo que DEBES hacer es cambiar los PIN predeterminados de la tarjeta. Hay dos. El primero es el PIN que usas para acceder a las llaves de la tarjeta. El segundo es el PIN de “Admin” o PUK (Personal Unblocking Code, código personal de desbloqueo).

Para hacerlo, escribe passwd y verás un menú:

gpg/card> passwd  
gpg: OpenPGP card no. <Serial number> detected

1 - change PIN  
2 - unblock PIN  
3 - change Admin PIN  
4 - set the Reset Code  
Q - quit

Escribe 1 y sigue las indicaciones para actualizar tu PIN. El PIN predeterminado es 123456.

Cuando termines, elige 3 para cambiar el PIN de Admin. El predeterminado es 12345678.

Una vez cambiados ambos PIN, vuelve al menú principal para terminar de actualizar la información de la llave.

Los comandos que puedes usar son:

  • name – para cambiar el nombre del titular
  • login – para cambiar el correo electrónico o el login del titular

Después de eso podemos pasar a la generación de las llaves propiamente dicha.

Generación de llaves

Ahora sí toca generar las llaves.

Para ello, escribe generate.

Se te preguntará Make off-card backup of encryption key? (Y/n), pero, como se menciona en la documentación de YubiKey, esto es solo un respaldo parcial y no creará una copia completa de las llaves secretas, así que da igual si respondes Yes o No. Yo respondo No(n).

Si ya hay llaves guardadas en la tarjeta, se te preguntará si quieres reemplazarlas:

gpg: Note: keys are already stored on the card!

Replace existing keys? (y/N)

Asegúrate de no reemplazar algo importante de lo que no tengas respaldo y continúa con Y. Si hay algo importante, es mejor abandonar todo el proceso con Ctrl+C.

Se te pedirá el PIN de la tarjeta que configuraste antes.

Luego tendrás que elegir durante cuánto tiempo serán válidas las llaves:

Please specify how long the key should be valid.
        0 = key does not expire
      <n> = key expires in n days
     <n>w = key expires in n weeks
     <n>m = key expires in n months
     <n>y = key expires in n years
 Key is valid for? (0)

Si quieres que las llaves sean válidas solo por 1 año, escribe 1y.

Se te pedirá que confirmes la fecha de caducidad. Revisa que todo esté bien y confirma.

Después se te pedirá que ingreses la información del propietario de la llave:

Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: <Your name>
Email address: <Your e-mail>
Comment: <Comment is optional>
 You selected this USER-ID:
     "<Your name> <comment> <your e-mail>
 Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?

Type `O` to confirm if everything seems correct.

Luego se te pedirá el PIN de Admin o PUK. Es necesario porque se realizará una operación de escritura en la tarjeta.

Si todo está bien, la YubiKey empezará a parpadear y seguirá así un buen rato (en mi caso, unos cinco minutos).

Cuando termine verás el siguiente mensaje:

gpg: key <Short ID> marked as ultimately trusted
gpg: revocation certificate stored as '/Users/bisko/.gnupg/openpgp-revocs.d/<LONG ID>.rev' public and secret key created and signed.

Anota el <Short ID>, porque lo usaremos más adelante.

En este punto puedes salir del prompt de GPG con quit o Ctrl+C.

Ahora toca guardar la llave pública, para poder reutilizar la llave en otra computadora.

Para ello, ejecuta el siguiente comando:

gpg --armor --export <Short ID> > <Short ID>.asc

Esto exportará la llave pública a un archivo llamado <Short ID>.asc. Lo usarás en el futuro si quieres poder usar la YubiKey en otra computadora o si pierdes tu configuración de GPG.

Para restaurar la llave pública en tu llavero, tienes que hacer lo siguiente:

gpg --import < <Short ID>.asc
gpg: key <Short ID>: public key "Your Name <your@email>" imported
gpg: Total number processed: 1
gpg: imported: 1

Una nota muy importante. Si se trata de una instalación nueva de GPG o de otra computadora, GPG no reconocerá por sí solo las llaves que están en la tarjeta. Necesitas ejecutar gpg --card-status para que lea la tarjeta y detecte las llaves que contiene.

Configurar Git para firmar

Para configurar Git de modo que firme tus commits, debes agregar lo siguiente a tu ~/.gitconfig.

En la sección [user], tienes que agregar (o actualizar) la siguiente línea:

signingkey = 0x<Short ID>

Luego, si quieres firmar todos tus commits, agrega lo siguiente al final del archivo:

[gpg]
  program = /usr/local/bin/gpg
[commit]
  gpgsign = true

Con esto Git firmará todos tus commits.

Para configurar GitHub, tienes que agregar tu llave pública a tu perfil. Para ello, ve a GitHub -> Settings -> SSH and GPG keys -> New GPG key.

Verás un cuadro de texto donde debes pegar todo el contenido del archivo <Short ID>.asc que generamos antes.

Si quieres copiar el contenido del archivo (en macOS) puedes hacer lo siguiente:

cat <Short ID>.asc | pbcopy

Esto copiará el contenido completo del archivo al portapapeles.

Pega eso en el cuadro de texto de GitHub y haz clic en el botón Add GPG key.

Después de eso puedes intentar hacer un commit y un push a GitHub, y deberías ver la bonita etiqueta Verified.

Depuración

A veces las cosas no funcionan a la primera. Para reunir más información puedes probar lo siguiente:

Firmar con GPG

Primero intenta firmar algo con la llave:

gpg -s <file>

Si esto falla, intenta averiguar por qué; yo no soy ningún experto como para ayudarte con eso.

Revisa la salida de Git con GIT_TRACE=1 git commit -am “test”

Esto te mostrará exactamente lo que hace Git mientras intenta hacer el commit y firmarlo. Te dará una pista de por qué podría estar fallando la firma.

De ahí también puedes ver que Git ejecuta el siguiente comando:

/usr/local/bin/gpg --status-fd=2 -bsau <Key ID>

Puedes intentar ejecutarlo manualmente para ver si te da un error más útil.