最简单靠谱的方式是用c#调用gpg命令行,而非纯托管库;需确保gnupg已安装且在path中,使用argumentlist安全传参,注意编码、批处理模式及exitcode检查。

用 C# 调用 GPG 命令行是最简单靠谱的方式
别折腾纯托管库(比如 SharpPrivacy 或老版 BCPG),它们要么已废弃、要么不支持现代 GPG 的默认算法(如 Ed25519 密钥、AEAD 加密模式),要么文档全无。直接调 gpg 可执行文件,稳定、兼容性好、行为可预测。
前提是目标机器已安装 GnuPG(推荐 gpg4win 或 brew install gnupg),且 gpg 在 PATH 中。不是“能不能”,而是“必须这么干”。
-
加密命令示例:
gpg --encrypt --recipient "alice@example.com" --output doc.gpg doc.txt -
解密命令示例:
gpg --decrypt --output doc.txt doc.gpg - 加
--batch --yes避免交互;用--passphrase-fd 0+ 重定向 stdin 支持密码输入(但私钥密码建议用 agent 或免密密钥) - 务必检查
Process.ExitCode:非 0 表示失败(比如密钥未找到、签名验证失败、权限问题)
GPG 进程启动时常见错误和绕过方法
最常卡在 System.Diagnostics.Process.Start 启动失败或输出为空——根本不是代码问题,而是环境或参数陷阱。
-
"gpg: command not found":确认gpg在PATH;C# 中可用ProcessStartInfo.UseShellExecute = false+ 显式指定完整路径(如"C:\Program Files\GnuPG\bin\gpg.exe") - 输出乱码或空:设置
ProcessStartInfo.StandardOutputEncoding = Encoding.UTF8(GPG 默认用 locale 编码,Windows 控制台常是 GBK) - 解密时卡住:没加
--batch,GPG 等待终端输入;或私钥有密码但没提供(用--pinentry-mode loopback需额外配置 gpg-agent,不推荐初试) - 加密失败报
"No public key":确认公钥已导入(gpg --import alice.pub),且邮箱/指纹完全匹配--recipient参数
如何安全传入敏感参数(尤其是 recipient 和 passphrase)
别拼接字符串构造命令行——这是命令注入高危区。GPG 命令本身支持多参数分隔,ProcessStartInfo.ArgumentList(.NET 5+)是唯一干净方案;旧版本只能用 Arguments 字符串但需严格转义。
- .NET 5+ 推荐写法:
startInfo.ArgumentList.Add("--recipient"); startInfo.ArgumentList.Add("alice@example.com"); - 避免把用户输入直接塞进
--recipient:先用gpg --list-keys校验存在性,再传入 - 绝不要用
--passphrase "xxx":明文密码会出现在进程列表(ps aux或 Windows 任务管理器可见);改用--passphrase-fd 0+process.StandardInput.WriteLine(pass),并确保UseShellExecute = false - 如果必须用密码短语,优先让 GPG agent 管理(
gpg-connect-agent /bye检查是否运行),代码里完全不碰密码
文件加密后体积变大、解密失败或中文乱码的根源
这不是 C# 的锅,是 GPG 默认行为和编码配合问题。关键点就两个:是否启用 ASCII armor、文本模式是否开启。
- 加密时加
--armor会生成 base64 文本(.asc 文件),体积增 33%,但适合邮件传输;不加则为二进制(.gpg),更小更快——按需选,别混用 - 解密出乱码?检查原始文件编码(如 UTF-8 with BOM)和解密后写入时是否用了相同
Encoding;GPG 本身不改内容字节,乱码只发生在读写环节 - 解密失败报
"invalid packet (ctb=XX)":大概率是文件被二次编码(比如用文本编辑器打开二进制 .gpg 文件再保存)、或传输时损坏(FTP 用了 ASCII 模式传 .gpg) - 性能提示:大文件(>100MB)加密/解密耗时主要在 I/O,GPG 本身流式处理,无需加载全文到内存——C# 侧用
FileStream直接对接StandardInput/BaseStream即可
真正麻烦的是密钥生命周期管理:公钥过期、吊销、子密钥切换……这些 GPG 自己管,C# 只负责调用。别试图在代码里解析密钥环或校验信任链——交给 gpg --check-signatures 和用户自己配置的信任模型。










