如何在Go中验证GPG公钥、私钥字符串的有效性
Go 实现GPG密钥有效性验证的方案
不需要自行拆解密钥结构,使用目前社区维护的OpenPGP兼容库即可实现。官方原x/crypto/openpgp库停止维护后,ProtonMail维护的fork分支是目前兼容性最好、支持最新GPG密钥格式的实现。
前置依赖
执行以下命令安装依赖:go get github.com/ProtonMail/go-crypto
核心验证逻辑
你需要完成三类常见验证,可根据实际需求选择:
1. 公钥有效性验证
验证逻辑:
- 解析ASCII装甲格式的公钥字符串
- 校验公钥的自签名合法性,确认密钥未被篡改
- 校验公钥是否在有效期内(若设置了过期时间)
2. 私钥有效性验证
验证逻辑:
- 解析ASCII装甲格式的私钥字符串
- 若私钥设置了 passphrase 加密,先完成私钥解密
- 校验私钥结构完整性,验证私钥对应的公钥自签名合法性
3. 公钥与私钥配对验证
验证逻辑:
- 生成随机测试串用私钥完成签名
- 用公钥验证签名合法性,校验通过即证明二者配对
示例代码
package main import ( "bytes" "crypto/rand" "fmt" "github.com/ProtonMail/go-crypto/openpgp" "github.com/ProtonMail/go-crypto/openpgp/armor" "io" ) // 验证公钥有效性 func ValidatePublicKey(pubKeyStr string) (*openpgp.Entity, error) { block, err := armor.Decode(bytes.NewBufferString(pubKeyStr)) if err != nil { return nil, fmt.Errorf("公钥装甲解码失败: %w", err) } entities, err := openpgp.ReadKeyRing(block.Body) if err != nil { return nil, fmt.Errorf("公钥解析失败: %w", err) } if len(entities) == 0 { return nil, fmt.Errorf("未找到有效公钥实体") } pubEntity := entities[0] // 校验自签名 if err = pubEntity.PrimaryKey.VerifySelfSignature(); err != nil { return nil, fmt.Errorf("公钥自签名校验失败: %w", err) } // 校验有效期 if pubEntity.PrimaryKey.KeyExpired(pubEntity.PrimaryIdentity().SelfSignature) { return nil, fmt.Errorf("公钥已过期") } return pubEntity, nil } // 验证私钥有效性 func ValidatePrivateKey(privKeyStr string, passphrase []byte) (*openpgp.Entity, error) { block, err := armor.Decode(bytes.NewBufferString(privKeyStr)) if err != nil { return nil, fmt.Errorf("私钥装甲解码失败: %w", err) } entities, err := openpgp.ReadKeyRing(block.Body) if err != nil { return nil, fmt.Errorf("私钥解析失败: %w", err) } if len(entities) == 0 { return nil, fmt.Errorf("未找到有效私钥实体") } privEntity := entities[0] // 解密私钥(未加密可跳过) if privEntity.PrivateKey.Encrypted { if err = privEntity.PrivateKey.Decrypt(passphrase); err != nil { return nil, fmt.Errorf("私钥解密失败: %w", err) } } // 校验私钥完整性 if err = privEntity.PrivateKey.Validate(); err != nil { return nil, fmt.Errorf("私钥结构校验失败: %w", err) } // 校验自签名 if err = privEntity.PrimaryKey.VerifySelfSignature(); err != nil { return nil, fmt.Errorf("私钥对应公钥自签名校验失败: %w", err) } return privEntity, nil } // 验证公私钥是否配对 func CheckKeyPairMatch(pubEntity *openpgp.Entity, privEntity *openpgp.Entity) error { // 生成随机测试数据 testData := make([]byte, 32) if _, err := rand.Read(testData); err != nil { return fmt.Errorf("生成测试数据失败: %w", err) } // 私钥签名 var sigBuf bytes.Buffer if err := openpgp.DetachSign(&sigBuf, privEntity, bytes.NewReader(testData), nil); err != nil { return fmt.Errorf("私钥签名失败: %w", err) } // 公钥验签 _, err := openpgp.CheckDetachedSignature(openpgp.EntityList{pubEntity}, bytes.NewReader(testData), &sigBuf, nil) if err != nil { return fmt.Errorf("签名校验失败,公私钥不配对: %w", err) } return nil }
注意事项
- 若你的密钥包含子密钥,需要额外遍历
Entity.Subkeys字段,逐一校验子密钥的签名有效性和有效期 - 若不需要验证配对关系,单独校验公钥或私钥可跳过配对校验步骤
内容的提问来源于stack exchange,提问作者Christian Bongiorno
相关产品推荐
相关产品推荐

