如何用Elixir生成GPG兼容的PEM编码密钥并实现加解密签名功能
问题原因
你当前的实现存在两个核心问题,导致导出的公钥无法被GnuPG识别:
- 格式不兼容:你导出的是原始RSA公钥的X.509 PEM格式,和GnuPG使用的OpenPGP协议密钥格式完全不属于一套标准。OpenPGP密钥除了基础RSA参数外,还要求包含密钥创建时间、用户身份标识、密钥自签名、用途标记等元数据,缺少这些元数据的裸RSA密钥GPG无法解析。
- 公钥提取逻辑错误:你写的
public_key_from_private_key函数存在bug,将RSA公钥的指数字段错误赋值为和模数相同的值,你输出的公钥内容前后两段完全重复就是这个问题导致的,生成的公钥本身就是无效的。
遗漏的实现步骤
要生成GnuPG兼容的密钥,你需要补充以下实现逻辑:
1. 先修正公钥提取的基础错误
Erlang的RSAPrivateKey结构体中,第2位是RSA模数n,第3位才是公钥指数e,修正后的代码如下:
defp public_key_from_private_key(private_key) do # 正确提取模数和公钥指数 {:RSAPublicKey, elem(private_key, 2), elem(private_key, 3)} end
2. 封装为OpenPGP格式
裸RSA密钥必须按OpenPGP标准封装后才能被GPG识别,推荐两种实现方案:
快速实现方案:调用系统GPG命令
直接通过System.cmd调用本地安装的gpg工具完成密钥生成、导出、加解密操作,不需要自己实现OpenPGP协议逻辑,示例逻辑:# 生成GPG密钥的示例 def generate_gpg_key(user_id, passphrase \\ "") do batch_config = """ %echo Generating GPG key Key-Type: RSA Key-Length: 2048 Subkey-Type: RSA Subkey-Length: 2048 Name-Real: #{user_id} Expire-Date: 0 Passphrase: #{passphrase} %commit %echo done """ System.cmd("gpg", ["--batch", "--gen-key"], input: batch_config) end # 导出GPG兼容的ASCII格式公钥 def export_gpg_public_key(user_id) do {pub_key, 0} = System.cmd("gpg", ["--export", "-a", user_id]) pub_key end导出的公钥头部为
-----BEGIN PGP PUBLIC KEY BLOCK-----,直接保存即可被gpg --show-keys识别。纯Elixir实现方案:自行封装OpenPGP结构
如果不想依赖系统GPG工具,需要按OpenPGP v4标准组装以下数据包后再导出为ASCII装甲格式:- 公钥数据包:包含密钥版本、创建时间、RSA参数(n、e)
- 用户ID数据包:包含密钥持有者的姓名、邮箱信息
- 自签名数据包:用私钥对上述公钥和用户ID信息签名,证明密钥归属
组装完成后按OpenPGP ASCII装甲格式编码,头部替换为PGP专用的标记即可。
3. 格式区分
如果需要同时支持导出通用RSA PEM和GPG兼容格式,要做明确的功能拆分:
- 通用RSA PEM用于非GPG场景的加解密,不要给GPG工具使用
- GPG专用格式仅用于和GnuPG生态交互,不要作为通用RSA密钥使用
内容的提问来源于stack exchange,提问作者fschuindt
相关产品推荐
相关产品推荐

