Delphi 中的 OpenPGP:无需 GnuPG 即可加密、签名和验证

· 组件
Delphi 中的 OpenPGP:无需 GnuPG 即可加密、签名和验证

OpenPGP 仍然是许多机构交换文件的方式。银行要求付款批次用它的公钥加密,合作伙伴发来用 GnuPG 加密的报表,发布包需要附带一个分离签名。在 Delphi 中,这通常意味着要在每台机器上安装 GnuPG,并从命令行调用 gpg.exe,同时还要处理随之而来的路径、密钥环和退出码问题。

sgcCrypto 现在自行实现了 OpenPGP,使用纯 Object Pascal。无需部署 DLL,无需调用命令行工具,也不依赖 Indy。密钥、加密、解密、签名和验证都只是对 TBytes 的几个函数调用,而且结果可与 GnuPG 双向互操作。

版本 4 和版本 6

支持两代格式:

向多个收件人加密时,库会选择每个人都能读取的格式:如果所有人都支持,就使用 RFC 9580 的 AEAD 格式,否则使用经典的完整性保护格式。发给新旧密钥混合收件人的消息,在任何地方都能打开。

生成密钥

一次调用就能创建完整的密钥:一个主签名密钥、一个加密子密钥、用户 ID,以及把它们绑定在一起的自签名。

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;

支持的密钥类型有 RSA 2048、3072 和 4096,Ed25519,Ed448(版本 6),以及基于 NIST P-256、P-384 和 P-521 的 ECDSA 与 ECDH。可以导入 Brainpool 密钥。私钥用你的口令保护,版本 4 使用迭代 SHA-256,版本 6 使用 Argon2 加 AES-OCB。

加密和解密

可以向一个或多个公钥加密,并可选择由发送方签名。输出默认是 ASCII armor 格式,可直接附加到邮件或上传到服务器。

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

解密需要一个包含你的私钥的密钥环。被锁定的私钥会在私有副本上用口令解锁,因此密钥环本身不会改变。结果会告诉你消息是发给谁的、使用了什么加密方式,以及其中的签名是否有效。

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;

签名和验证

支持用于文件和发布包的分离签名、内联签名消息,以及必须保持可读的文本所用的明文签名,还提供文本模式,使签名与换行符风格无关。

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

与 GnuPG 协同工作

每项功能都与 GnuPG 2.4.5 进行了双向测试:GnuPG 生成的文件和签名可以在 Delphi 中解密和验证,库生成的所有内容都能被 gpg --decrypt 和 gpg --verify 接受。这包括 GnuPG 2.4 对其自己生成的密钥默认写出的 OCB 加密消息,许多 OpenPGP 库无法读取这种消息。版本 6 的支持则对照 RFC 9580 的示例密钥和消息进行了检查。

密钥以 armor 文本的形式在两个世界之间流动。从 Delphi 导出密钥后运行 gpg --import,或者把 gpg --export --armor 的输出加载到 TsgcPGPKeyRing 中。

为不可信输入而设计

解密外部文件的应用必须假定其中一些是恶意的。库会拒绝旧的无保护加密格式,对所有解密失败报告相同的错误,因此无法被用作 oracle,拒绝文档上的 SHA-1 签名,并对口令工作因子和解压后的大小设置了限制,使精心构造的消息无法拖垮服务器。

其他新功能

OpenPGP 单元可在 Delphi 7 到 Delphi 13 和 C++Builder 下编译,支持 Win32 和 Win64。DSA 和 ElGamal 密钥可以加载但不能使用,也不支持旧的 CAST5、3DES 和 IDEA 密码。

延伸阅读

对某个具体集成、银行格式或需要导入的密钥有疑问?欢迎联系我们。你会收到编写这些代码的人的回复。