ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

isomorphic-git 的 onSign 签名回调:PGP 签名提交与注释标签及签名验证实战

isomorphic-git 的 onSign 签名回调:PGP 签名提交与注释标签及签名验证实战 开发工具【免费下载链接】isomorphic-gitA pure JavaScript implementation of git for node and browsers!项目地址https://gitcode.com/gh_mirrors/is/isomorphic-git点击查看免费下载本文以 isomorphic-git 的onSign回调为核心完整讲解如何用 PGP 为提交commit和注释标签annotated tag签名、onSign回调必须实现的函数契约、签名的源码级写入方式以及通过log、readCommit、readTag返回的payload与gpgsig进行签名验证的完整流程。读完后你可以在 Node 或浏览器环境中为 isomorphic-git 启用提交签名并自行实现签名与验签逻辑同时理解“签名有效”与“建立信任”之间的差距。一、使用 onSign 为提交签名isomorphic-git 的 PGP 签名功能通过一个onSign回调与底层实现解耦你只需提供一个符合约定接口的签名函数再把它和私钥一起传给commit等 API 即可。文档给出的最小用法如下import { pgp } from isomorphic-git/pgp-plugin git.commit({ ..., onSign: pgp.sign })结合仓库源码可以补充一个关键点单传onSign并不会触发签名。在 commit 的 API 入口中签名动作由signingKey参数驱动onSign只是签名实现// src/api/commit.js 中的参数校验逻辑简化 if (signingKey) { assertParameter(onSign, onSign) }也就是说完整调用需要同时提供两个参数——signingKeyASCII armor 编码的 PGP 私钥字符串和onSign签名实现import * as git from isomorphic-git import { NodeFS } from isomorphic-git/nodefs import { pgp } from isomorphic-git/pgp-plugin await git.commit({ fs: NodeFS, dir: /path/to/repo, message: Signed commit, author: { name: Mr. Test, email: mrtestexample.com, timestamp: 1504842425, timezoneOffset: 0 }, signingKey: privateKey, // ASCII armor 编码的 PGP 私钥 onSign: pgp.sign, // 符合 onSign 契约的签名函数 })仓库的测试用例create signed commit见tests/test-commit.js中it(create signed commit, ...)正是这样使用的传入测试私钥signingKey: privateKey与onSign: pgp.sign创建签名提交随后用公钥验证断言invalid为空数组、valid中包含 key idf2f0ced8a52613c4。测试用的公私钥对存放在tests/fixtures/pgp-keys.js。二、两个 PGP 实现插件的取舍isomorphic-git 官方提供两个可用作onSign的插件选择依据如下维度OpenPGP.js 实现isomorphic-git/openpgp-pluginisomorphic-pgp 实现isomorphic-git/pgp-plugin适用场景推荐用于 Node 应用推荐用于浏览器应用密钥支持对不同类型密钥的支持更广泛仅支持有限类型的密钥许可证LGPL文档指出这通常意味着不能将其打包进闭源应用MIT体积约 164 kbgzip 后约 21 kbgzip 后简言之Node 端对许可证体积不敏感且需要广泛的密钥兼容性时选 OpenPGP.js 插件浏览器端在意包体积和 MIT 许可证时选 isomorphic-pgp 插件。两者的pgp.sign都满足下文相同的回调契约切换实现不需要改动业务代码。三、onSign 回调的函数契约如果你不想用官方插件、而要自己实现onSign例如对接服务端签名服务、KMS 或浏览器 WebCrypto必须遵守以下 APIasync ({ payload, secretKey }) { signature }参数类型 [ 默认值]说明payloadstring待签名的明文消息secretKeystring以 ASCII armor 编码的 PGP 私钥理论上可以包含多个密钥返回值Promise{signature: string}以 ASCII armor 编码的 detached分离式签名该契约在仓库的类型定义中有对应声明见 src/typedefs.js/** * typedef {Object} SignParams * property {string} payload - a plaintext message * property {string} secretKey - an ASCII armor encoded PGP key */ /** * callback SignCallback * param {SignParams} args * return {{signature: string} | Promise{signature: string}} - an ASCII armor encoded detached signature */需要注意两点secretKey是运行时传入的不要写死在回调里。isomorphic-git 每次调用onSign时都会带上当前signingKey的值因此同一个回调可以服务多把私钥。返回值必须是分离式签名的 ASCII armor 文本-----BEGIN PGP SIGNATURE-----...-----END PGP SIGNATURE-----。从源码结构看签名写入流程对签名字符串还做了一次换行归一化避免 CRLF 破坏 git 对象格式。哪些 API 接受 onSign除了文档以commit为例的用法仓库中接受onSign/signingKey参数的 API 还包括git.commit —— 签名提交git.annotatedTag —— 签名注释标签git.merge —— 创建合并提交时签名非 fast-forward 场景git.addNote 与 git.removeNote —— notes 分支上新提交同样会走签名流程其内部复用_commit。所有这些 API 的参数校验逻辑一致一旦提供了signingKey就强制要求同时提供onSign否则抛出MissingParameterError。四、源码视角签名是如何被写入 git 对象的理解了这一层才能明白payload到底签了什么。提交对象。在 src/commands/commit.js中构建好无签名的GitCommit后执行if (signingKey) { comm await GitCommit.sign(comm, onSign, signingKey) }而 src/models/GitCommit.js的静态方法sign实现了真正的拼装static async sign(commit, sign, secretKey) { const payload commit.withoutSignature() // 去掉签名后的对象全文 待签名 payload const message GitCommit.justMessage(commit._commit) let { signature } await sign({ payload, secretKey }) // renormalize the line endings to the one true line-ending signature normalizeNewlines(signature) const headers GitCommit.justHeaders(commit._commit) const signedCommit headers \n gpgsig indent(signature) \n message return GitCommit.from(signedCommit) }可见payload就是“不含gpgsig头的提交对象原始文本”签名以gpgsig头部形式插入在头信息与提交正文之间并对签名块做了缩进indent这正是 git 的git log -s/gpg --verify所要求的格式。注释标签对象同理。src/models/GitAnnotatedTag.js中static async sign(tag, sign, secretKey) { const payload tag.payload() // tag 对象去掉签名部分 let { signature } await sign({ payload, secretKey }) signature normalizeNewlines(signature) const signedTag payload signature // 签名直接追加在 tag 对象末尾 return GitAnnotatedTag.from(signedTag) }注意两者的格式差异commit 把签名放在gpgsig头中而 tag 把分离式签名直接拼接在对象文本末尾——这与原生 git 的行为一致。五、验证签名验证时使用的两个要素是签名commit 的.gpgsig或 tag 的.signature和签名 payload。log、readCommit、readTag都会返回 payload源码可证src/commands/readCommit.js返回payload: commit.withoutSignature()src/commands/readTag.js返回payload: tag.payload()。文档给出的三段验证示例完整保留如下publicKey为 ASCII armor 公钥由你自行保管。批量验证一批提交// Verify a whole bunch of commits import { pgp } from isomorphic-git/pgp-plugin let commits await git.log({ fs, dir, ref: main }) for (const { commit, payload } of commits) { let { valid, invalid } await pgp.verify({ payload, publicKey, signature: commit.gpgsig }) // valid is a string[] of the valid key ids // invalid is a string[] of the invalid key ids. Ideally this is empty. }验证单个 commit 对象// Verify a commit object import { pgp } from isomorphic-git/pgp-plugin let oid await git.resolveRef({ fs, dir, ref: main }) let { commit, payload } await git.readCommit({ fs, dir, oid }) let { valid, invalid } await pgp.verify({ payload, publicKey, signature: commit.gpgsig }) // valid is a string[] of the valid key ids // invalid is a string[] of the invalid key ids验证注释标签对象// Verify an annotated tag object import { pgp } from isomorphic-git/pgp-plugin import { resolveRef, readTag } from isomorphic-git let oid await resolveRef({ fs, dir, ref: v1.0.0 }) let { tag, payload } await readTag({ fs, dir, oid }) let { valid, invalid } await pgp.verify({ payload, publicKey, signature: tag.signature }) // valid is a string[] of the valid key ids // invalid is a string[] of the invalid key idsverify的返回值是{ valid, invalid }两个 key id 数组valid是验证通过的 key id 列表invalid是失败的 key id 列表——理想情况下invalid为空。仓库测试正是据此断言的expect(invalid).toEqual([])、expect(valid).toEqual([f2f0ced8a52613c4])。六、“签名有效”并不等于“可信”文档特别强调仅验证签名有效不足以建立信任trust。你还需要确认两件事这个publicKey确实属于写这个 commit 的人你一开始是怎么找到这个publicKey的。文档指出目前没有标准答案并给出两条常见路径及其缺陷路径一通过 GitHub 邮箱匹配公钥。用commit.author.email和commit.committer.email去匹配 GitHub 用户名由于邮箱可能是隐私的这并不简单再到 GitHub 上查询该用户的 PGP 公钥。优点是 GitHub 充当了权威方基本可以相信公钥确实属于该用户GitHub API 甚至允许你检查该邮箱是否经过验证。缺点是只对“在 GitHub 上公开邮箱且上传过 PGP 公钥”的用户有效。路径二从签名中提取公钥 ID再去 PGP 密钥服务器查询。文档给出了提取与查询的参考代码const extractKey (gpgsig) { const m Message.parse(gpgsig) for (const p of m.packets) { if (p.tag 2 /* Signature Packet */) { for (const s of p.packet.unhashed.subpackets) { if (s.type 16 /* Issuer */) { return s.subpacket.issuer_s } } } } } const lookupKey async (keyid) { let text await (await fetch(http://pgp.mit.edu/pks/lookup?opgetsearch0x${keyid})).text() let matches text.match(/-----BEGIN PGP PUBLIC KEY BLOCK-----(.|\n)*-----END PGP PUBLIC KEY BLOCK-----/) if (matches) return matches[0] }这条路的缺陷更严重只有当事人主动上传密钥到服务器才能生效且公共密钥服务器毫无安全性可言——任何人都可以上传一把自称johnsmithaol.com的公钥。因此还必须确保查到的公钥邮箱与commit.author.email或commit.committer.email一致并借助 Web-of-Trust密钥被其他密钥签名层层递推到“可信密钥”或其他机制判断信任。可行的缓解策略信任首次使用TOFU第一次看到johnsmithaol.com的签名提交时查询并保存其公钥之后若同一邮箱出现不同公钥向用户告警“密钥已变更”。这与 SSH 首次连接时的指纹确认体验类似。企业化方案向用户发送验证邮件把验证过的 PGP 公钥集中存放到数据库中。去中心化系统的建议如果你构建的是为用户自动生成 PGP 密钥的去中心化系统文档建议把公钥直接保存在 git 仓库本身——“那看起来是最明显的位置”。归根结底文档抛出的两个开放问题是我在哪里找到公钥我凭什么相信这把公钥确实属于这个邮箱地址isomorphic-git 提供的是签名与验签的可靠原语而信任模型的落地需要应用层自行设计。七、小结与延伸阅读onSign是一个async ({ payload, secretKey }) { signature }的分离式签名回调payload为明文对象文本返回 ASCII armor 签名触发签名需要signingKeyonSign同时提供覆盖commit、annotatedTag、merge、addNote、removeNote等 API验签三要素log/readCommit/readTag返回的payload、对象中的gpgsig或 tag 的signature、以及你信任来源的publicKey签名有效 ≠ 可信公钥获取与信任建立需结合 TOFU、Web-of-Trust、GitHub 权威邮箱或集中式公钥库自行实现。延伸阅读commit API 文档、commit 实现入口、GitCommit 模型、GitAnnotatedTag 模型、SignCallback 类型定义、签名提交测试。赞分享开发工具【免费下载链接】isomorphic-gitA pure JavaScript implementation of git for node and browsers!项目地址https://gitcode.com/gh_mirrors/is/isomorphic-git点击查看免费下载相关推荐Git verify-commit提交签名的验证机制Git verify commit提交签名的验证机制 1. 为什么需要提交签名验证 在多人协作的Git仓库中你是否曾遇到以下问题 无法确认某次提交是否真版本控制开发工具CLIAsterinas数字签名代码签名与验证Asterinas数字签名代码签名与验证 概述 在当今数字化时代软件安全已成为系统开发的核心关注点。Asterinas作为一个用Rust编写的安全、快速、通操作系统内核驱动系统编程ProGit2项目详解使用GPG签名Git提交与标签ProGit2项目详解使用GPG签名Git提交与标签 为什么需要Git签名 在分布式版本控制系统中验证代码来源的真实性至关重要。虽然Git本身提供了加密安全文档教程版本控制上一篇现代Spring Boot开发工作流重构Claude Code Template如何提升团队协作效率下一篇MoveFlow 规范推断Specification Inference完全指南面向 Aptos Move 的 WP 混合推断工作流与 Move 规范语言参考创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表