OpenPGP en Delphi: cifrar, firmar y verificar sin GnuPG

· Componentes
OpenPGP en Delphi: cifrar, firmar y verificar sin GnuPG

OpenPGP sigue siendo la forma en que muchas organizaciones intercambian archivos. Un banco quiere los lotes de pagos cifrados con su clave pública, un socio envía informes cifrados con GnuPG, una versión tiene que distribuirse con una firma separada. Desde Delphi esto solía significar instalar GnuPG en cada máquina y llamar a gpg.exe desde una línea de comandos, con toda la gestión de rutas, llaveros y códigos de salida que eso conlleva.

sgcCrypto ahora implementa OpenPGP por sí mismo, en Object Pascal puro. No hay DLL que distribuir, ni herramienta de línea de comandos que invocar, ni dependencia de Indy. Las claves, el cifrado, el descifrado, las firmas y la verificación son un puñado de llamadas a funciones sobre TBytes, y el resultado es interoperable con GnuPG en ambos sentidos.

Versión 4 y versión 6

Se admiten dos generaciones del formato:

Cuando cifras para varios destinatarios, la librería elige el formato que todos ellos pueden leer: el formato AEAD de RFC 9580 si todos lo admiten, y en caso contrario el formato clásico con protección de integridad. Un mensaje dirigido a una mezcla de claves antiguas y nuevas se abre en todas partes.

Generar una clave

Una sola llamada crea una clave completa: una clave primaria de firma, una subclave de cifrado, el ID de usuario y las autofirmas que las unen.

uses
  sgcCrypto_PGP, sgcCrypto_PGP_Cert;

var
  oAlice: TsgcPGPKey;
begin
  // v4 Ed25519 + Curve25519, the default key type of GnuPG
  oAlice := sgcPGP_GenerateKey('Alice <alice@example.com>', 'secret',
    pgpkEd25519, 4);
  try
    Memo1.Text := oAlice.SaveToArmored(False);  // public key, share it
    SaveSecretKey(oAlice.SaveToArmored(True));  // protected secret key
  finally
    oAlice.Free;
  end;
end;

Los tipos de clave son RSA 2048, 3072 y 4096, Ed25519, Ed448 (versión 6) y ECDSA con ECDH sobre NIST P-256, P-384 y P-521. Las claves Brainpool se pueden importar. Las claves secretas se protegen con tu frase de contraseña, usando SHA-256 iterado en la versión 4 y Argon2 con AES-OCB en la versión 6.

Cifrar y descifrar

Cifra para una o varias claves públicas, opcionalmente firmado por el remitente. La salida está en ASCII armor por defecto, lista para adjuntar a un correo o subir a un servidor.

// encrypt to Bob, signed by Alice, compressed with ZIP
vEncrypted := sgcPGP_Encrypt(vData, [oBob], oAlice);

// or protect a file with a passphrase only
vEncrypted := sgcPGP_EncryptSymmetric(vData, 'shared passphrase');

El descifrado recibe un llavero con tus claves secretas. Una clave bloqueada se desbloquea en una copia privada con la frase de contraseña, de modo que el llavero en sí nunca cambia. El resultado te indica para quién era el mensaje, cómo se cifró y si la firma que contiene es válida.

var
  oRing: TsgcPGPKeyRing;
  vResult: TsgcPGPDecryptResult;
begin
  oRing := TsgcPGPKeyRing.Create;
  try
    oRing.LoadFromFile('bob-secret.asc');
    vData := sgcPGP_Decrypt(vEncrypted, oRing, 'bob passphrase', oContacts,
      vResult);
    if vResult.Verify.Valid then
      Log('Signed by ' + vResult.Verify.SignerUserID);
  finally
    oRing.Free;
  end;
end;

Firmar y verificar

Se admiten firmas separadas para archivos y versiones, mensajes firmados en línea y firmas en texto claro para texto que debe seguir siendo legible, con modo texto para firmas independientes del final de línea.

// detached signature, the classic file.sig
vSignature := sgcPGP_Sign(vData, oAlice);
if sgcPGP_VerifyDetached(vData, vSignature, oContacts, vVerify) then
  Log('Valid signature from ' + vVerify.SignerUserID);

// cleartext signed text, readable without any tool
vSigned := sgcPGP_ClearSign('Payment batch 2026-10 approved', oAlice);

Funciona con GnuPG

Cada función se probó en ambos sentidos con GnuPG 2.4.5: los archivos y las firmas creados por GnuPG se descifran y verifican en Delphi, y todo lo que produce la librería es aceptado por gpg --decrypt y gpg --verify. Eso incluye los mensajes cifrados con OCB que GnuPG 2.4 escribe por defecto para las claves que genera, y que muchas librerías OpenPGP no pueden leer. La compatibilidad con la versión 6 se comprobó con las claves y mensajes de ejemplo de RFC 9580.

Las claves viajan entre ambos mundos como texto armado. Exporta una clave desde Delphi y ejecuta gpg --import, o carga la salida de gpg --export --armor en un TsgcPGPKeyRing.

Pensado para entradas no confiables

Una aplicación que descifra archivos externos debe asumir que algunos son hostiles. La librería rechaza el antiguo formato de cifrado sin protección, notifica todos los fallos de descifrado con el mismo error para que no pueda usarse como oráculo, rechaza las firmas SHA-1 sobre documentos y establece límites para los factores de trabajo de las frases de contraseña y para el tamaño descomprimido, de modo que un mensaje manipulado no pueda bloquear el servidor.

Qué más hay de nuevo

Las unidades de OpenPGP compilan con Delphi 7 a Delphi 13 y C++Builder, para Win32 y Win64. Las claves DSA y ElGamal se pueden cargar pero no usar, y los antiguos cifrados CAST5, 3DES e IDEA no se admiten.

Leer a continuación

¿Preguntas sobre una integración concreta, un formato bancario o una clave que necesitas importar? Ponte en contacto. Te responderán las personas que escribieron el código.