OpenPGP Armor 编码介绍

OpenPGP Armor

OpenPGP是使用最广泛的电子邮件加密标准。它由Internet工程任务组(IETF)的OpenPGP工作组定义为RFC 4880中的建议标准.OpenPGP最初源自由Phil Zimmermann创建的PGP软件。

虽然OpenPGP的主要目的是端到端加密电子邮件通信,但它也用于加密消息传递和其他用例,如密码管理器。

OpenPGP的加密消息,签名证书和密钥的基本描述是八位的字节流。为了通过不能保障安全的网络通道传输OpenPGP的二进制八位字节,需要编码为可打印的二进制字符。OpenPGP提供将原始8位二进制八位字节流转换为可打印ASCII字符流,称为Radix-64编码或ASCII Armor。

ASCII Armor是OpenPGP的可选功能。当OpenPGP将数据编码为ASCII Armor时,它会在Radix-64编码数据中放置特定的Header。OpenPGP可以使用ASCII Armor来保护原始二进制数据。OpenPGP通过使用Header告知用户在ASCII Armor中编码了什么类型的数据。

ASCII Armor的数据结构如下:

  • Armor标题行,匹配数据类型
  • Armor Headers
  • A Blank(零长度或仅包含空格) Line
  • The ASCII-Armored data
  • An Armor Checksum
  • The Armor Tail,取决于护甲标题线

具体示例:

1
2
3
4
5
6
7
8
9
10
-----BEGIN PGP MESSAGE-----

Version: OpenPrivacy 0.99


yDgBO22WxBHv7O8X7O/jygAEzol56iUKiXmV+XmpCtmpqQUKiQrFqclFqUDBovzSvBSFjNSiVHsuAA==

=njUN

-----END PGP MESSAGE-----

golang.org/x/crypto/openpgp/armor 代码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Encode 返回一个 WriteCloser,它将对 写入的数据进行编码
// Encode returns a WriteCloser which will encode the data written to it in
// OpenPGP Armor.
func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) {
bType := []byte(blockType)
err = writeSlices(out, armorStart, bType, armorEndOfLineOut)
if err != nil {
return
}

for k, v := range headers {
err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline)
if err != nil {
return
}
}

_, err = out.Write(newline)
if err != nil {
return
}

e := &encoding{
out: out,
breaker: newLineBreaker(out, 64),
crc: crc24Init,
blockType: bType,
}
e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker)
return e, nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 将返回值进一步封装为字符串
func EncodeArmor(blockType string, headers map[string]string, data []byte) string {
buf := new(bytes.Buffer)
w, err := armor.Encode(buf, blockType, headers)
if err != nil {
panic(fmt.Errorf("could not encode ascii armor: %s", err))
}
_, err = w.Write(data)
if err != nil {
panic(fmt.Errorf("could not encode ascii armor: %s", err))
}
err = w.Close()
if err != nil {
panic(fmt.Errorf("could not encode ascii armor: %s", err))
}
return buf.String()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 实际使用中将公钥导出为 ASCII Armor 格式
// ExportPubKey returns public keys in ASCII armored format.
func armorPubKeyBytes(bz []byte) string {
return armorBytes(bz, blockTypePubKey)
}

func armorBytes(bz []byte, blockType string) string {
header := map[string]string{
"type": "Info",
"version": "0.0.0",
}
return armor.EncodeArmor(blockType, header, bz)
}

func ExportPubKey(name string) (armor string, err error) {
bz := Get(infoKey(name))
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
info, err := readInfo(bz)
if err != nil {
return
}
return armorPubKeyBytes(info.GetPubKey().Bytes()), nil
}

在 CovalentChain 公钥、私钥的导入导出和传输等情况,使用 ASCII Armor 编码格式非常合适。另外,在私钥情况下,通常都需要在加密后,再进行编码处理。

参考链接:

  1. https://tools.ietf.org/html/rfc4880
  2. https://www.openpgp.org/
  3. golang.org/x/crypto/openpgp/armor