9.1.3.3. Correo electrónico

Los mecanimos de cifrado y firma electrónica se implementan en los mensajes de correo electrónico a través de dos estándares, cuya principal diferencia es la infraestructura sobre la que se sustenta la confianza en las claves de cifrado:

  • OpenPGP (RFC 3156), en el que cada usuario genera sus propias claves y se establece una red de confianza totalmente descentralizada. Los certificados PGP incluyen como datos identificativos el nombre del propietario y su dirección de correo electrónico.

  • S/MIME (RFC 8551), en el que se usa la estructura jerárquica PKI de certificados X.509, que se explicó al hablar del certificado digital.

Por lo demás, los estándares son similares en la estrategia que usan para firmar o cifrar el cuerpo del mensaje (no los campos de cabecera[1]):

Firma

Al firmar un mensaje se añade un campo MIME multipart/signed a la cabecera:

Content-Type: multipart/signed; protocol="application/x-pkcs7-signature";
    micalg=sha-256; boundary="XXXXX"

donde:

  • protocol especifica qué estándar se usa (en este caso, S/MIME).

  • micalg, el algoritmo de resumen utilizado.

  • boundary, como en cualquier otro MIME, permite identificar las distintas partes.

Y el cuerpo del mensaje se estructura en dos partes:

  1. La primera contiene el mensaje original, el cual puede a su vez estar constituido por múltiples partes MIME (p.e. porque el mensaje contuviera adjuntos).

  2. La segunda constituye la firma sobre la parte anterior:

From: yo@micasa.com
To: tu@tucasa.com
Content-Type: multipart/signed; protocol="application/x-pkcs7-signature";
    micalg=sha-256; boundary="XXXXX"
Subject: Un mensaje firmado

--XXXXX
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

Este es el texto del mensaje que se firma...
...
... Mucho más texto ...
...

--XXXXX
Content-Type: application/x-pkcs7-signature
Content-Disposition: attachment; filename="smime.p7s"
Content-Transfer-Encoding: base64

LA.FIRMA.EN.SI.TAL.VEZ.LA.CLAVE

--XXXXX

La firma se construye resumiendo la primera parte con una función de hash (SHA-256 en nuestro ejemplo) y cifrándola con la clave privada del emisor. El contenido de esta segunda parte, sin embargo, puede contener, además de la propia firma, el certificado público del emisor.

Nota

La primera parte es inline mientras que la segunda parte adopta la forma de un adjunto de nombre smime.p7s. Esto implica que un cliente que no entienda la firma, muestre el texto del mensaje normalmente y presente la firma como un adjunto a descargar.

Cifrado

El cifrado de un mensaje consiste en cifrar el cuerpo del mensaje del siguiente modo:

  • Muy comúnmente se comprime el cuerpo lo que reduce su tamaño y, además, disminuye la existencia de patrones que hagan menos seguro el cifrado.

  • Se genera una clave simétrica (p.e. AES) y se cifra el cuerpo con ella.

  • Se cifra la clave anterior con la clave pública del destinatario a fin de que sólo el destinario tenga acceso a ella y por tanto a descifrar el mensaje. Si hay varios destinatarios, se adjutan copias de la clave simétrica cifradas con sendas claves públicas.

Los mensajes de correo pueden estar cifrados y firmados lo cual implica hacer primero una acción y luego la otra. Lo habitual es que los MUA firmen primero y cifren después el mensaje firmado.

9.1.3.3.1. S/MIME

OpenSSL tiene una suborden (smime) que implementa este estándar y nos sirve para probar cómo funciona. Incluye además otra (cms) que proporciona más opciones que la anterior, pero sirve para el mismo fin.

Firma

Ya hemos visto que firmar un mensaje de correo consiste en firmar el contenido de la primera parte del multipart/signed (lo que antes de haberse firmado el correo constituía el cuerpo del mensaje) y añadirlo como contenido de la segunda parte en forma de adjunto (cuyo nombre en el ejemplo es smime.p7s). Podemos emular estas acciones con openssl tomando un archivo (que hará el papel de primera parte):

$ openssl smime -sign -in fichero.txt -signer micert.pem -out fichero.eml

done micert.pem es un archivo PEM con el certificado y la clave privada del firmante[2]. El archivo resultante fichero.eml adopta el aspecto de un correo electrónico multipart/signed como el mostrado más arriba. Si quisiéramos obtener exclusivamente la firma, y no todo el mensaje, podríamos hacer con la salida anterior:

$ openssl smime -pk7out -in fichero.eml -out smime.p7s

o bien haber cambiando el formato de salida de la primera orden (que por defecto es SMIME):

$ openssl smime -sign -in fichero.txt -signer micert.pem -outform pem -out smime.p7s

La firma, generada así, contiene, además del resumen y la clave simétrica cifrados (recuérdense los conceptos sobre firma digital), el certificado del firmante, por si el destinatario careciera de ella. No contendrá, no osbtante, certificados intermedios aunque micert.pem los incluyera[3].

Obtenida la firma podemos verificarla:

$ openssl smime -verify -in fichero.eml

aunque la verificación fallará si falta algún certificado intermedio. Para subsanarlo puede añadirse a la orden anterior la opción -noverify:

$ openssl smime -verify -in fichero.eml -noverify

o incluir los certificados intermedios en un archivo y referirlo con -CAfile:

$ openssl smime -verify -in fichero.eml -CAfile ca-certs.pem

Advertencia

Si se intenta verificar la firma usando smime.p7s:

$ openssl smime -verify -in smime.p7s -inform pem -noverify -content fichero.txt

la verificación fallará como consecuencia de que el estándar S/MIME antes de calcular el resumen modifica los cambios de línea (en UNIX habitualmente “:kbd:n:”) a la forma canónica “rn”. Podemos solucionarlo o incluyendo la opción -binary al generar la firma para evitar la modificación:

$ openssl smime -sign -binary -in fichero.txt -signer micert.pem -outform pem -out smime.p7s

o modificando al vuelo fichero.txt para que presente tales cambios de línea al hacer la verificación:

$ openssl smime -verify -in smime.p7s -inform pem -noverify -content <(sed 's:$:^M:' fichero.txt)

Otra acción útil que puede hacerse sobre smime.p7s es rescatar los certificados de usuario que contenga:

$ openssl pkcs7 -in smime.p7s -print_certs -out certs.pem

Cifrado

Para cifrar un archivo (en un mensaje de correo se cifraría el cuerpo del mensaje) la orden es esta:

$ openssl smime -encrypt -in fichero.txt -aes256 -out fichero.eml pubkey.pem

donde cert.pem contiene la clave pública del destinatario del archivo cifrado. Si se quiere cifrar para más destinatarios habrä que añadir archivos PEM adicionales a la orden, ya que es inútil añadirlos dentro de un mismo archivo PEM. Obsérvese, además, que se incluye la opción -aes256 para indicar que se use cifrado AES. Este cifrado es simétrico, porque, como ya se ha explicado al explicar el cifrado de mensajes de correo, el archivo se cifra con una clave simétrica generada ad hoc y es esta clave simétrica la que se cifra con las claves públicas de los destinatarios.

El archivo resultante de esta orden tiene forma de mensaje de correo:

MIME-Version: 1.0
Content-Disposition: attachment; filename="smime.p7m"
Content-Type: application/x-pkcs7-mime; smime-type=enveloped-data;
   name="smime.p7m"
Content-Transfer-Encoding: base64

MENSAJE.CIFRADO.CODIFICADO.EN.BASE64

y, de hecho, si añadiera sus campos de cabecera típicos (Subject, From, etc.) sería un mensaje válido para ser enviado mediante SMTP. Por esta razón, si el mensaje se consulta a través de un MUA sin soporte para mensajes cifrados, se observará un mensaje sin contenido visible, pero con un adjunto llamado smime.p7m.

Para obtener este adjunto directamente, prescindiendo del formato SMIME, podríamos haber hecho:

$ openssl smime -encrypt -in fichero.txt -aes256 -out smime.p7m -outform pem pubkey.pem

De este archivo de salida (o el .eml) se puede extraer el contenido original descifrando con el certificado completo (incluyendo la clave privada) del destinatario:

$ openssl smime -decrypt -in smime.p7m -inform pem -recip cert.pem
El contenido original perfectamente descifrado

aunque, si se tienen por separado certificado y clave privada, puede indicarse ésta mediante la opción -inkey.

Nota

Los archivos PKCS #7 generados con las órdenes de este epígrafe pueden bichearse con:

$ openssl cms -cmsout -print -in smime.p7m -inform pem

9.1.3.3.2. OpenPGP

Para ilustrar este estándar usaremos GnuPG, aunque lo habitual es que los agentes de correo (MUA) como mutt o Thunderbird, hagan uso del estándar de manera sencilla, o incluso podemos llegar a usarlo con determinados servicios de webmail y la extensión adecuada del navegador. Por ejemplo, en Chromium existe la extensión Mailenvelope que permite el cifrado de mensajes para los principales sitios de webmail (Gmail, Yahoo, Outlook, etc.).

Nota

En Windows GPG4Win permite hacer gráficamente las operaciones que aquí mostraremos para Linux, añade plugins para algunas aplicaciones como Outlook y Explorer e incluye además un gestor de certificados X.509 (aunque es este el tipo de certificados que soporta el gestor nativo de Windows).

9.1.3.3.2.1. Instalación

La instalación es sumamente sencilla:

# apt install gnupg

Como configuración podemos usar la siguiente:

$ mkdir -m 700 ~/.gnupg
$ cat > ~/.gnupg/gpg.conf
#keyserver hkps://sks-keyservers.net:443
keyserver hkp://pool.sks-keyservers.net
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.3.3.2.2. Cifrado simétrico

Antes de entrar realmente en harina, podemos usar GnuPG para hacer cifrado simétrico:

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

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

-----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 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.3.3.2.3. Gestión de claves

La aplicación utiliza un repositorio donde va almacenando las claves públicas y privadas que se necesiten. Por ejemplo, nuestro propio par de claves (pública y privada) y las claves públicas de todos aquellos con los que intercambiemos información de forma segura. El objetivo de este epígrafe es cómo saber generar nuestra clave (recordemos que en PGP cada usuario genera sus claves), cómo exportar e importar claves, y cómo borrar claves que no deseemos almacenar más.

Generación

Para generar un par de claves podemos 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, que 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 algoritmo[5]. 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 incluso sin llegar a estar completo, es una de ellas). Si no especificamos ninguna clave en concreto, se exportarán 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 fichero[6].

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 usando 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

Eliminación de claves

Para borrar una clave del repositorio basta con utilizar las opciones --delete-keys, --delete-secret-keys o --delete-secret-and-public-keys, dependiendo de si queremos borrar una clave pública o una clave privada. Por ejemplo:

$ gpg --delete-keys yanoloquiero@example.net

Confianza

Ya se ha explicado que los certificados PGP no presentan una estructura jerarquizada de confianza, sino que son los propios usuarios los que otorgan confianza a una clave ajena firmándola. Al listar claves, nos encontramos entre corchetes la confianza que nos inspira la clave. [ absoluta ] implica confianza ciega y se fija automaticamente si nosotros mismos somos los que hemos generado la clave.

Supongamos que hemos importado una clave de un conocido:

$ gpg --import pubkey-conocido.asc
$ gpg --list-keys --keyid-format short
[...]

pub   rsa3072/079F9ECF 2021-03-11 [SC] [caduca: 2023-03-11]
      CF7DAB0C27CFF10B842B0DED1A54391B079F9ECF
uid      [desconocida] Mi mejor amigo <amigo@gmail.com>

La clave aparece con una confianza desconocida. Si hubieramos obtenido la clave por un canal seguro (p.e. el amigo nos la ha facilitado personalmente), podríamos entonces firmarla:

$ gpg --sign-key amigo
$ gpg --list-signatures
[...]

pub   rsa3072 2021-03-11 [SC] [caduca: 2023-03-11]
      CF7DAB0C27CFF10B842B0DED1A54391B079F9ECF
uid        [   total   ] Mi mejor amigo <amigo@gmail.com>
sig 3        1A54391B079F9ECF 2021-03-11  Mi mejor amigo <amigo@gmail.com>
sig          A2123969EB13CB39 2021-03-11  Licenciado Cebadilla (cuenta de pruebas) <xxxx@gmail.com>

De esta forma la confianza cambiaría (ahora es [ total ]) y la clave de nuestro conocido pasaría a incluir nuestra firma, que acredita que le hemos dado nuestra confianza. Esta confianza se incorpora a la clave, de modo que si la exportamos:

$ gpg --export amigo > pubkey-conocido-firmada.asc

La clave incorporará nuestra firma y si la subimos a un servidor de claves también lo incorporará, lo cual puede ayudar a otros a confiar en ella. En cambio, sino hemos conocido personalmente al dueño lo más apropiado es:

  1. Obtener la clave del conocido.

  2. Firmarla con nuestra clave.

  3. Exportarla y cifrarla para este desconocido:

    $ gpg --export amigo | gpg -se -r amigo > ~/tmp/pubkey-amigo.asc.asc
    

    Nota

    Cómo se cifra lo trataremos a continuación.

  4. Enviarla por correo electrónico al conocido con un texto que explique que hemos firmado su clave y que se le adjuntamos cifrada.

Con ello, nos aseguraremos de que el conocido es el dueño de la cuenta de correo electrónico que refiere en el clave (porque recibe el mensaje) y que es el dueño de tal clave (porque es capaz de descifrarla con la clave privada).

Nota

De lo que no podríamos estar seguros es de su identidad física real.

Este conocido, por su parte, deberá descrifrar la clave y exportarla a un servidor de claves:

$ gpg -decrypt pubkey-amigo.asc.asc
$ gpg --import pubkey-amigo.asc
$ gpg --send-keys 079F9ECF

9.1.3.3.2.4. Cifrado asimétrico

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 correspondiente a la pública con la que se firmó, basta con:

$ gpg -qd secreto.asc
Esto es un secreto

En realidad, esto acciíon que hemos presentado como cifrado asimétrico no es tal, sino que GnuPG practica un cifrado híbrido: se genera una clave simétrica ad hoc para cifrar el archivo y es la clave simétrica la que se cifra con la clave pública proporcionada con la opción -r. El archivo resultante contiene el archivo cifrado y la clave cifrada. De hecho, es posible repetir la opción -r para que el archivo pueda ser descifrado por vartios:

$ gpg -er su_correo@dominio.com -r otro@example.net -o - fichero.txt > fichero.txt.asc

En este caso, la clave simétrica se cifra dos veces con sendas claves públicas y las dos versiones cifradas se adjuntan al archivo cifrado.

9.1.3.3.2.5. Firma digital

Para firmar un archivo basta con:

$ echo "Este es el contenido del fichero que firmo" > fichero.txt
$ gpg --detach-sign --default-key mi_cuenta@example.com -o fichero.sig fichero.txt

De esta manera tenemos un fichero original (fichero.txt) y su resumen cifrado digitalmente con nuestra clave privada en fichero.sign. Si analizamos el fichero de firma:

$ gpg --list-packets fichero.sign
:signature packet: algo 1, keyid 53175AA29C972B7B
        version 4, created 1543050622, md5len 0, sigclass 0x00
        digest algo 10, begin of digest 04 9e
        hashed subpkt 33 len 21 (issuer fpr v4 040968BBC05C39A4DD2A43BD53175AA29C972B7B)
        hashed subpkt 2 len 4 (sig created 2018-11-24)
        hashed subpkt 28 len 23 (signer's user ID)
        subpkt 16 len 8 (issuer key ID 53175AA29C972B7B)
        data: [3071 bits]

veremos algunas características de la firma, como:

  • qué algoritmo de clave asimétrica se usó, el 1, que se corresponde con una clave asimétrica RSA, válida tanto para firma como para cifrado. El significado de los códigos puede encontrarse en el RFC 4880, y en concreto en la sección 9.1.

  • qué clave se usó: la 53175AA29C972B7B, que efectivamente es la nuestra:

    $ gpg --keyid-format long -list-keys
    /home/usuario/.gnupg/pubring.kbx
    --------------------------------
    pub   rsa3072/53175AA29C972B7B 2018-11-21 [SC] [expires: 2020-11-20]
          040968BBC05C39A4DD2A43BD53175AA29C972B7B
    uid                 [ unknown] Soy el que soy <mi_cuenta@example.com>
          sub   rsa3072/4B1F09C9B84F038E 2018-11-21 [E] [expires: 2020-11-20]
    
  • con qué algoritmo se resumió el fichero, el 10, que es SHA512 según la sección 9.4 del RFC 4880.

Si hacemos llegar ambos archivos a un tercero, y éste posee nuestra clave pública, podrá verificar nuestra identidad gracias a descifrar la firma y la integridad del fichero gracias al resumen que contiene esta:

$ gpg --verify fichero.sign fichero.txt
[...]
Primary key fingerprint: 0409 68BB C05C 39A4 DD2A  43BD 5317 5AA2 9C97 2B7B
$ echo $?
0

Ver también

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

Notas al pie