golang openPGP 加密解密

项目背景

项目需要使用openPGP对发送接口的数据进行加密,并且对返回的PGP消息进行解密。首先,对openPGP不熟悉,所有先了解一个openGPG。在了解openGPG之后,使用PHP官方扩展 gnupg。然后发现gnupg 在加密时,不能使用带密码的私钥。从此,开始了踩坑之旅。最后,使用golang PGP相关库来实现openPGP的加密与解密,PHP使用FFI特性来调用 golang。

一、openPGP

RFC文档请参考 RFC4880, linux上主要是使用gpg工具来加密与解密。相关文档可参考 https://www.gnupg.org/gph/en/manual.html。 openPGP对开发者可参考 这里

1.1 常用的gpg命令如下:
1. 查看本地导入的公钥
gpg --list-keys

2.导出
gpg --output example.gpg --export example@abc.org

3.导出为ascii格式
gpg --armor --export example@abc.org
1.2 使用gpg加密与解密消息

相关命令如下:

 1. 加密、签名并且压缩,注意压缩算法
gpg --encrypt --sign --armor --cipher-algo AES256 --digest-algo SHA256 --compress-algo 1 -r {Recipient} -u {uid} {need_to_be_encypt}

2.解密加密的PGP消息
gpg --output {out_file} --decrypt {need_be_decrypt}


二、golang openPGP

golang官方是支持openPGP,github上也有相关示例,但是不满足我需求(使用一个公钥加密,另外一个私钥签名最后的消息进行签名)。

golang 相关库的官方库文档 ,一个示例可以参考:例子一

2.1 首先是准备好相关的gpg key

因为使用 ReadKeyRing方法,所以gpg KEY要按照这个格式来。这里有两个地方提到golang key格式的,KKEY转换参考一,KEY转换参考二。具体命令可以参考以下:

第一步:
transfer PGP key to keyring
gpg --keyring pubring.gpg --export KEY > /tmp/exported.key
gpg --no-default-keyring --/=path/to/new-keyring.gpg --import /tmp/exported.key

第二步:
gnupg>v2 for golang
gpg --no-default-keyring --keyring ./ring.gpg --export-secret-keys > secret-key.gpg
2.2 读取keyring

关键代码如下:

读取解析KeyRing内容
priEntityList, err := openpgp.ReadKeyRing(PriKeyringFileBuffer)

如果是私钥还要解密:
priEntity := priEntityList[0]
if priEntity.PrivateKey.Encrypted {
passphraseByte := []byte(passPhrase)
priEntity.PrivateKey.Decrypt(passphraseByte)
for _, subkey := range priEntity.Subkeys {
subkey.PrivateKey.Decrypt(passphraseByte)
}
}
2.3 加密签名文件

使用 openpgp.Encrypt 来加密签名,关键代码如下:

encryptorWriter, err := openpgp.Encrypt(armorWriter, entityList, priEntity, nil, &packConfig)

如果需要将消息转换为 ascii格式,还需要使用 armor.Encode 来转码
2.4  解密消息

使用函数 openpgp.ReadMessage 来解密消息,如果是PGP消息,首先还需要解析PGP消息。关键代码如下:

解析PGP消息:
armorDecoder, err := armor.Decode(armorBytes)

解密消息:
messageDetail, err := openpgp.ReadMessage(armorDecoder.Body, entityList, nil, &config)

完整的示例可参考 go-openpgp-example ,官方库中有对加密压缩有疑问,请参考 https://github.com/golang/go/issues/24847 。后续的一个更新是更新了的

三、PHP使用 FFI 特性

如果PHP版本大于7.4,官方代码库已自带,直接下载源代码找到对应扩展编译安装。如果PHP版本小于PHP7.4 可以参考 PHP-FFI 。PHP-FFI中使用go,请参考 php-ffi-go