Golang openpgp 库中 GPG 密钥签名错误分析与解决方案


Golang openpgp 库中 GPG 密钥签名错误分析与解决方案

本文深入探讨了使用 go 语言 `go.crypto/openpgp` 库进行 gpg 公钥签名时可能遇到的“签名无效”问题。核心原因在于该库早期版本中 `signidentity` 方法的实现缺陷,它错误地使用了子密钥签名算法而非用户id签名算法。文章将分析此问题,并强调使用最新 `golang.org/x/crypto/openpgp` 库的重要性,以确保加密操作的正确性和安全性。

引言:GPG 密钥签名在 Go 中的挑战

GPG(GNU Privacy Guard)签名是确保数据完整性、来源真实性和不可否认性的重要手段。在 Go 语言中,openpgp 库提供了实现 GPG 加密和签名的能力。然而,开发者在使用该库进行公钥签名时,可能会遇到生成的签名在外部 GPG 工具(如 gpg --check-sigs)验证时显示为“bad Signature”的问题。这通常不是因为代码逻辑上的明显错误,而是由于库底层实现的特定缺陷。

问题复现:早期 openpgp 库的签名逻辑

为了理解这个问题,我们首先来看一个典型的 Go 语言中用于签名公钥用户 ID 的代码结构。以下示例展示了如何加载公钥和私钥实体,然后使用私钥对公钥的用户 ID 进行签名:

因赛AIGC 因赛AIGC

因赛AIGC解决营销全链路应用场景

因赛AIGC 280 查看详情 因赛AIGC
package main

import (
    "bytes"
    "fmt"
    "io"

    "golang.org/x/crypto/openpgp"
    "golang.org/x/crypto/openpgp/armor"
    "golang.org/x/crypto/openpgp/packet"
)

// LoadEntityFromArmoredKeyRing 从 ASCII Armored 字符串加载 openpgp.Entity
// password 参数仅在加载私钥且私钥加密时需要
func LoadEntityFromArmoredKeyRing(armoredKey string, password []byte) (*openpgp.Entity, error) {
    keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewReader([]byte(armoredKey)))
    if err != nil {
        return nil, fmt.Errorf("读取 Armored 密钥环失败: %w", err)
    }
    if len(keyring) == 0 {
        return nil, fmt.Errorf("未找到密钥实体")
    }

    entity := keyring[0] // 假设密钥环中只有一个实体

    // 如果有私钥且需要解密
    if entity.PrivateKey != nil && entity.PrivateKey.Encrypted {
        if password == nil {
            return nil, fmt.Errorf("私钥已加密,但未提供密码")
        }
        if err := entity.PrivateKey.Decrypt(password); err != nil {
            return nil, fmt.Errorf("解密私钥失败: %w", err)
        }
    }
    return entity, nil
}

// SignPublicKeyWithPrivateKey 使用私钥签名公钥的用户ID
// 此函数演示了签名的核心逻辑,并强调了可能出现问题的 SignIdentity 调用
func SignPublicKeyWithPrivateKey(
    armoredPublicKey string,
    armoredPrivateKey string,
    privateKeyPassword string,
) (string, error) {
    // 1. 加载公钥实体
    pubEntity, err := LoadEntityFromArmoredKeyRing(armoredPublicKey, nil)
    if err != nil {
        return "", fmt.Errorf("加载公钥实体失败: %w", err)
    }

    // 2. 加载私钥实体并解密
    priEntity, err := LoadEntityFromArmoredKeyRing(armoredPrivateKey, []byte(privateKeyPassword))
    if err != nil {
        return "", fmt.Errorf("加载私钥实体失败: %w", err)
    }

    // 3. 获取公钥的用户ID
    var userIdName string
    for _, identity := range pubEntity.Identities {
        userIdName = identity.Name
        break // 通常取第一个用户ID进行签名
    }
    if userIdName == "" {
        return "", fmt.Errorf("公钥实体中未找到用户ID")
    }

    fmt.Printf("正在使用私钥 '%s' 签名公钥 '%s' 的用户ID '%s'\n", priEntity.PrimaryKey.KeyIdString(), pubEntity.PrimaryKey.KeyIdString(), userIdName)

    // 4. 执行签名操作
    // 核心问题曾发生在此处:早期版本的 openpgp 库在此方法中存在实现缺陷
    err = pubEntity.SignIdentity(userIdName, priEntity, nil)
    if err != nil {
        return "", fmt.Errorf("签名用户ID失败: %w", err)
    }
    fmt.Println("用户ID签名成功。")

    // 5. 将签名的公钥实体序列化为 ASCII Armored 字符串
    buf := new(bytes.Buffer)
    writer, err := armor.Encode(buf, openpgp.PublicKeyType, nil)
    if err != nil {
        return "", fmt.Errorf("创建 Armored 编码器失败: %w", err)
    }
    if err := pubEntity.Serialize(writer); err != nil {
        return "", fmt.Errorf("序列化签名的公钥实体失败: %w", err)
    }
    if err := writer.Close(); err != nil

以上就是Golang openpgp 库中 GPG 密钥签名错误分析与解决方案的详细内容,更多请关注其它相关文章!


# 环中  # 怎样优化网站乱代码  # 景观综合网站建设  # 网站存储优化原理  # 山东seo培训系统  # 无锡网站建设 网站制作  # 网站视频推广多少钱  # 阿云网站建设  # seo数据指标计划  # 朝阳租房网站建设  # 哪个网站可以发推广帖  # 相关文章  # 这个问题  # 在此  # 第一个  # word  # 库中  # 转换为  # 加载  # 文档  # 公钥  # igs  # crypto  # red  # ai  # 工具  # 编码  # golang  # go 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 蜻蜓FM如何设置移动流量播放  QQ阅读小说搜索入口地址_QQ阅读小说搜索入口地址搜索在线阅读  鲁班大师乓乓皮肤获取方法  win11关机几秒又自己开机 Win11关机自动重启问题修复  狙击外星人小游戏在线链接_狙击外星人小游戏网页链接  精通VS Code多光标编辑以实现闪电般快速的修改  Magento 2 产品保存事件中安全更新属性的最佳实践  如何修改Windows截图的默认保存位置_告别C盘让桌面更整洁【教程】  聚水潭ERP后台管理系统登录 聚水潭ERP官方登录通道  OpenWeatherMap API:通过城市名称获取天气预报数据指南  vivo手机视频通话美颜怎么设置_vivo视频通话美颜开启方法  如何快速去除厨房重油污? 2025年最好用的厨房清洁剂推荐  使用jQuery精确检测除指定元素外任意位置的点击事件  《律学法考》查看学习数据方法  《糖豆》添加舞曲方法  Yandex无需登录畅游 俄罗斯搜索引擎最新官网指南  漫蛙漫画直连入口 _ manwa官方备用入口实时检测  《地下城堡4:骑士与破碎编年史》墓穴挑战125攻略  如何用Golang优化微服务间请求性能_Golang 微服务请求性能优化方法  J*aScript模块加载器_RequireJS原理分析  服装短视频如何起号推广?服装短视频起号推广有什么要求?  感染了幽门螺杆菌一定会导致胃癌吗?蚂蚁庄园今日答案最新11.30  不吃碳水化合物是健康减肥的好办法吗  Python高效统计字典嵌套列表值在目标列表中的出现次数  谷歌邮箱官方入口链接 谷歌邮箱网页版电脑端快速登录  跨语言测试实践:使用Python Selenium测试现有J*a Web项目  《偃武》甘宁技能详解  中大网校app做题记录清除方法  汽水音乐车机版官网5.0 汽水音乐车机版5.0版本下载入口  RxJS中如何高效地在一个函数内处理和合并多个数据集合  Linux如何优化系统启动流程_Linux启动项优化方案  Lar*el 中高效执行多列更新:单次查询实现  三星M34录音变声问题_Samsung M34麦克风调整  AffinityDesigner图层蒙版怎么用_AffinityDesigner图层蒙版设计应用  Python中处理嵌套字典与列表的数据提取与过滤教程  《伊瑟》凶影追缉库卢鲁boss攻略  Excel如何设置动态下拉菜单_Excel表格下拉选项快速方法  C++中std::thread和std::async的区别_C++并发编程与线程与异步任务比较  性能与资源监视器快捷打开  VB表达式书写规则解析  抖音号升级成企业资质怎么弄?有什么好处?  偃武诸葛亮阵容搭配推荐  我居然低估了 DeepSeek,这次更新它做到了这些!  金牛福袋获取攻略  如何测试您的网站全球打开速度-网站海外测速工  Go语言反射机制下访问嵌入结构体中的被遮蔽方法  晓晓优选app支付宝绑定方法  顺丰快递单号查询寄件人 顺丰寄件人查询入口  《火花chat》搜索好友方法  如何通过settings.json个性化您的VS Code体验 

 2025-11-21

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.