新闻中心
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 进行签名:
CA.LA
第一款时尚产品在线设计平台,服装设计系统
94
查看详情
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网络营销
# php加seo
# SEO基础舞蹈风暴
# 嘉兴网站关键词优化排名
# 江门如何用seo
# 厦门网络营销推广策划
# 网站代码优化的标准
# 品牌营销推广速来火5星
# 品质网站建设推广费用
# 相关文章
# 这个问题
# 在此
# 第一个
# word
# 库中
# 转换为
# 加载
# 文档
# 公钥
# igs
# crypto
# red
# ai
# 工具
# 编码
# golang
# go
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
荣耀Play7TPro怎样在信息App置顶客服对话_iPhone荣耀Play7TPro信息App置顶客服对话【优先查看】
京东京造J1和网易云音乐氧气真无线有什么不同_国产电商蓝牙耳机音质对比
windows10怎么关闭系统提示音_windows10彻底静音设置方法
KFC早餐时段怎么领特惠代码_KFC早餐订餐优惠代码获取与使用说明
2026春节假期时间安排 2026春节假日查询
Sublime怎么配置Nim语言环境_Sublime Nim代码高亮与补全
c++如何使用TBB库进行任务并行_c++ Intel线程构建模块
解决 MongoDB 聚合查询中对象数组 _id 匹配问题
LINUX的I/O重定向是什么_深入理解LINUX中 >、>> 与 < 的区别
Lar*el的路由模型绑定怎么用_Lar*el Route Model Binding简化控制器逻辑
C++ string find函数返回值npos详解_C++字符串查找失败的判断条件
AO3访问入口汇总 AO3网页版同人作品一键直达
神经网络二分类模型训练异常:高损失与完美验证准确率的排查与修正
Excel组合图表怎么做 Excel创建柱状图与折线组合图教程【图表】
Win11截图该按哪些键 Win11截屏完整流程解析【教程】
sublime怎么覆盖插件的默认快捷键_sublime快捷键优先级与设置
汽水音乐在线版入口_汽水音乐网页播放手册
德邦快递查询平台 德邦快递物流信息查询入口
C++如何比较两个字符串_C++ string compare函数与操作符对比
192.168.1.1管理中心入口 192.168.1.1路由器网页设置平台
不同用户不同价格! 索尼开启账户个性化定价测试
Python:递归比较文件夹内容并找出特定类型文件的差异
J*aScript中如何高效提取对象指定属性
如何创建没有密码的Windows本地账户_跳过微软账户登录的技巧【教程】
Win10文件资源管理器“此电脑”分组怎么关 Win10恢复经典视图【技巧】
聚水潭ERP登录页面入口 聚水潭ERP官网登录界面
响应式CSS Grid布局:优化网格项在小屏幕下的堆叠与宽度适配
火锅吃太多会怎样 火锅吃太多会上火吗
微博网页版主页入口 微博官方网站免登录访问
J*aScript对象创建方式_J*aScript设计模式应用
深入理解J*aScript Promise异步执行与微任务队列
2026年发布! 美少女养成动作RPG《神剑少女战记》发布实机演示
蛙漫漫画官网在线入口 蛙漫全本漫画免费阅读平台
PrimeNG Sidebar背景色自定义指南:CSS覆盖与主题化实践
yandex入口引擎手机版 yandex安卓版下载入口
Mac怎么查看崩溃日志_Mac控制台错误报告分析
Python中如何避免重复条件判断:利用数据结构实现动态逻辑
支付宝解绑银行卡步骤_支付宝如何解除绑定银行卡
TypeScript/J*aScript:高效查找数组中首个唯一ID对象
AI泡沫首次被“刺破”:GPU十年都无法存活!
三星GalaxyZFold5怎样在相册制作折叠屏分镜_iPhone三星GalaxyZFold5相册制作折叠屏分镜【创意编辑】
可靠CSGO开箱平台解析 CSGO开箱网合集
谷歌邮箱网页版官方页面入口 谷歌邮箱网页端快速访问
在WordPress中通过REST API获取BasicAuth保护的远程文章
c++如何使用Catch2编写单元测试_c++简洁易用的BDD风格测试框架
Mudbox图层蒙版怎么用_Mudbox图层蒙版数字雕刻应用技巧
J*aScript map 迭代中检测空数组元素的有效方法
Android Studio计算器C键逻辑错误排查与修复:条件判断优化指南
C++如何进行游戏物理模拟_使用Box2D库为C++游戏添加2D物理效果
新三国志曹操传110级星符试炼夏侯渊极难攻略


2025-11-21
浏览次数:次
返回列表
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