OpenPGP in Delphi: Encrypt, Sign and Verify Without GnuPG

· Components
OpenPGP in Delphi: Encrypt, Sign and Verify Without GnuPG

OpenPGP is still how many organisations exchange files. A bank wants payment batches encrypted to its public key, a partner sends reports encrypted with GnuPG, a release has to ship with a detached signature. From Delphi this usually meant installing GnuPG on every machine and calling gpg.exe from a command line, with all the path, keyring and exit code handling that goes with it.

sgcCrypto now implements OpenPGP itself, in pure Object Pascal. There is no DLL to deploy, no command line tool to call and no dependency on Indy. Keys, encryption, decryption, signatures and verification are a handful of function calls on TBytes, and the result interoperates with GnuPG in both directions.

Version 4 and Version 6

Two generations of the format are supported:

When you encrypt to several recipients, the library picks the format every one of them can read: the AEAD format of RFC 9580 when all of them support it, the classic integrity protected format otherwise. A message to a mix of old and new keys still opens everywhere.

Generate a Key

One call creates a complete key: a primary signing key, an encryption subkey, the user ID and the self signatures that bind them together.

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;

Key types are RSA 2048, 3072 and 4096, Ed25519, Ed448 (version 6), and ECDSA with ECDH on NIST P-256, P-384 and P-521. Brainpool keys can be imported. Secret keys are protected with your passphrase, using iterated SHA-256 for version 4 and Argon2 with AES-OCB for version 6.

Encrypt and Decrypt

Encrypt to one or more public keys, optionally signed by the sender. The output is ASCII armored by default, ready to attach to a mail or upload to a server.

// 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');

Decryption takes a keyring with your secret keys. A locked key is unlocked on a private copy with the passphrase, so the keyring itself never changes. The result tells you who the message was for, how it was encrypted and whether the signature inside is valid.

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;

Sign and Verify

Detached signatures for files and releases, inline signed messages and cleartext signatures for text that must stay readable are all supported, with text mode for line ending independent signatures.

// 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);

Works With GnuPG

Every feature was tested both ways against GnuPG 2.4.5: files and signatures made by GnuPG decrypt and verify in Delphi, and everything the library produces is accepted by gpg --decrypt and gpg --verify. That includes the OCB encrypted messages GnuPG 2.4 writes by default for keys it generated, which many OpenPGP libraries cannot read. The version 6 support was checked against the sample keys and messages of RFC 9580.

Keys move between both worlds as armored text. Export a key from Delphi and run gpg --import, or load the output of gpg --export --armor into a TsgcPGPKeyRing.

Built for Untrusted Input

An application that decrypts files from outside has to assume some of them are hostile. The library refuses the old unprotected encryption format, reports every decryption failure with the same error so it cannot be used as an oracle, rejects SHA-1 signatures on documents, and has limits for passphrase work factors and decompressed size so a crafted message cannot tie up the server.

What Else Is New

The OpenPGP units compile with Delphi 7 to Delphi 13 and C++Builder, for Win32 and Win64. DSA and ElGamal keys can be loaded but not used, and the old CAST5, 3DES and IDEA ciphers are not supported.

Questions about a specific integration, a bank format or a key you need to import? Get in touch. You will get a reply from the people who wrote the code.