news 2026/9/26 10:11:32

Substrate subkey 密钥管理工具实战指南:密钥生成、恢复、签名验证与容器化部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Substrate subkey 密钥管理工具实战指南:密钥生成、恢复、签名验证与容器化部署
  • 区块链
  • 开发框架
  • 后端

【免费下载链接】substrate

Substrate: The platform for blockchain innovators

项目地址:https://gitcode.com/gh_mirrors/su/substrate
点击查看免费下载

subkey是 Substrate 官方提供的一站式密钥管理命令行工具,面向 Substrate 生态链(如 Polkadot、Kusama 以及各类平行链和 Substrate 项目)的开发者与用户。本文以仓库中 scripts/ci/docker/subkey.Dockerfile.README.md 对 subkey 的能力定义为主线,结合 bin/utils/subkey/README.md、bin/utils/subkey/SECURITY.md 与 scripts/ci/docker/subkey.Dockerfile 等仓库资料,系统讲解如何生成与检查密钥对、从助记词和原始种子恢复密钥、对消息进行签名与验证、派生分层确定性子密钥,以及如何通过 Docker 容器安全地使用该工具。读完本文,你将能够独立完成 Substrate 链账户密钥的全生命周期管理,并掌握容器化部署与安全实践要点。

认识 subkey:Substrate 生态的密钥管理工具

Substrate 官方文档对subkey的定位是:"Thesubkeyprogram is a key management utility for Substrate-based blockchains"——即面向 Substrate 系区块链的密钥管理工具。根据仓库中 scripts/ci/docker/subkey.Dockerfile.README.md 的定义,你可以使用 subkey 完成以下任务:

  • 生成并检查密码学安全的公钥/私钥对;
  • 从助记词(secret phrase)和原始种子(raw seed)恢复密钥;
  • 对消息进行签名与验签;
  • 对编码后的交易进行签名与验签;
  • 派生分层确定性(Hierarchical Deterministic, HD)子密钥对。

从源码结构看,subkey 的完整实现位于仓库的 bin/utils/subkey/ 目录。其中 Cargo.toml 声明了subkey3.0.0 这个独立的二进制 crate,入口为 src/main.rs,其核心逻辑极简——main()直接调用subkey::run(),全部功能通过 src/lib.rs 实现,命令行解析依赖clap 4.2.5(启用derive特性),错误类型则复用sc-cli(client/cli)的定义。也就是说,subkey 实际上是 Substrate 客户端 CLI 工具链在密钥管理场景下的专门化封装。

完全离线的密钥工具

subkey 在运行时不需要联网。这一点既是设计特性,也是安全要求:密钥操作不涉及任何网络请求,因此你可以(也应该)在完全断网的机器上生成和管理密钥。正如 bin/utils/subkey/README.md 强调的:"for the best security, you should be usingsubkeyon a machine that isnot connectedto the internet."

安装与获取 subkey

subkey 的获取方式有两种:通过 Cargo 从源码安装,或直接使用官方发布的 Docker 容器镜像。

通过 Cargo 安装

使用cargo install可以只安装subkey这一个工具,而无需编译整个节点。仓库给出的推荐命令如下:

# 安装指定版本的 subkey crate cargo install --force subkey --git https://github.com/paritytech/substrate --version <SET VERSION> --locked
  • --force:覆盖已安装的旧版本;
  • --version <SET VERSION>:指定 subkey crate 的版本号(例如本仓库中的 3.0.0);
  • --locked:使用仓库Cargo.lock中锁定的依赖版本,保证构建可复现。

需要说明的是,从源码安装 subkey 需要先具备 Substrate 的构建依赖环境;如果构建过程中报错,通常是因为缺少系统级依赖。

在 Docker 容器中运行

对于不想本地编译、又希望隔离运行环境的场景,官方在 Docker Hub 发布了parity/subkey镜像,直接运行即可:

docker run -it --pull=always docker.io/parity/subkey:latest <command to subkey>
  • 使用latest标签时建议配合--pull=always,确保拉取到最新镜像;
  • 也可以改用指定版本的标签来固定版本;
  • <command to subkey>即你要执行的 subkey 子命令,例如subkey generate。

官方镜像的构建细节:解析 subkey.Dockerfile

仓库中 scripts/ci/docker/subkey.Dockerfile 正是构建该官方镜像的 Dockerfile,它从侧面揭示了 subkey 容器的运行模型:

FROM docker.io/library/ubuntu:20.04 # metadata ARG VCS_REF ARG BUILD_DATE ARG IMAGE_NAME LABEL io.parity.image.authors="devops-team@parity.io" \ io.parity.image.vendor="Parity Technologies" \ io.parity.image.title="${IMAGE_NAME}" \ io.parity.image.description="Subkey: key generating utility for Substrate." \ io.parity.image.source="https://github.com/paritytech/substrate/blob/${VCS_REF}/scripts/ci/docker/subkey.Dockerfile" \ io.parity.image.revision="${VCS_REF}" \ io.parity.image.created="${BUILD_DATE}" \ io.parity.image.documentation="https://github.com/paritytech/substrate/tree/${VCS_REF}/subkey" # show backtraces ENV RUST_BACKTRACE 1 # add user RUN useradd -m -u 1000 -U -s /bin/sh -d /subkey subkey # add subkey binary to docker image COPY ./subkey /usr/local/bin USER subkey # check if executable works in this container RUN /usr/local/bin/subkey --version ENTRYPOINT ["/usr/local/bin/subkey"]

这份 Dockerfile 的几个关键点值得留意:

  1. 基础镜像与元数据:基于ubuntu:20.04,通过构建参数VCS_REF、BUILD_DATE、IMAGE_NAME注入版本、构建时间与镜像名,并写入 OCI Label,用于镜像溯源与审计。
  2. 非 root 运行:创建了 UID 1000、无密码、家目录为/subkey的专用用户subkey,并以USER subkey切换,容器内进程默认不具备 root 权限——这符合密钥工具"最小权限"的安全惯例。
  3. ENTRYPOINT 直接指向二进制:容器的入口就是/usr/local/bin/subkey,因此docker run ... subkey generate等价于直接执行subkey generate。
  4. 构建期自检:RUN /usr/local/bin/subkey --version在构建阶段就验证二进制在当前容器内可执行,避免发布坏镜像。

生成加密安全的密钥对

生成随机账户

生成一个新密钥对只需一条命令:

subkey generate

输出类似(以下均为示例数据,切勿复用本文出现的任何种子与助记词):

Secret phrase `hotel forest jar hover kite book view eight stuff angle legend defense` is account: Secret seed: 0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d Public key (hex): 0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515 Account ID: 0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515 SS58 Address: 5Hpm9fq3W3dQgwWpAwDS2ZHKAdnk86QRCu7iX4GnmDxycrte

输出字段解读

generate输出的五类信息需要分清"秘密"与"公开"的边界:

字段性质说明
Secret phrase(助记词)秘密即 mnemonic phrase,默认 12 个单词,必须妥善保密
Secret seed(秘密种子)秘密即 Private Key,一个 64 位十六进制值,必须妥善保密
Public key (hex)公开公钥的十六进制表示,可由秘密信息推导
Account ID公开账户标识,通常与公钥十六进制一致
SS58 Address公开面向特定网络的地址表示(见下文)

其中助记词与秘密种子是仅有的两份"秘密"信息,其余所有字段都可以从它们推导出来。公钥与Account ID独立于具体网络;而SS58 Address(即公开地址)则是公钥针对某一特定网络的编码表示——同一个种子在不同网络中对应不同的 SS58 地址。例如对上述种子:

  • Polkadot:16m4J167Mptt8UXL8aGSAi7U2FnPpPxZHPrCgMG9KJzVoFqM
  • Kusama:JLNozAv8QeLSbLFwe2UvWeKKE4yvmDbfGxTuiYkF2BUMx4M

SS58 地址格式是 Substrate 生态统一的账户地址编码规范(含前缀、校验和等),不同网络通过不同的地址前缀区分。

JSON 输出:面向自动化

默认的文本输出适合人读,而脚本自动化场景则推荐 JSON 输出:

subkey generate --output-type json

输出:

{ "accountId": "0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515", "publicKey": "0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515", "secretPhrase": "hotel forest jar hover kite book view eight stuff angle legend defense", "secretSeed": "0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d", "ss58Address": "5Hpm9fq3W3dQgwWpAwDS2ZHKAdnk86QRCu7iX4GnmDxycrte" }

配合jq可以只提取某个字段,例如只取秘密种子:

subkey generate --output-type json | jq -r .secretSeed

输出:

0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d

附加用户自定义密码

generate支持在种子之外再追加一段用户自定义的密码(password),它会被混入密钥派生过程:

subkey generate --password extra_secret

输出:

Secret phrase `soup lyrics media market way crouch elevator put moon useful question wide` is account: Secret seed: 0xe7cfd179d6537a676cb94bac3b5c5c9cb1550e846ac4541040d077dfbac2e7fd Public key (hex): 0xf6a233c3e1de1a2ae0486100b460b3ce3d7231ddfe9dadabbd35ab968c70905d Account ID: 0xf6a233c3e1de1a2ae0486100b460b3ce3d7231ddfe9dadabbd35ab968c70905d SS58 Address: 5He5pZpc7AJ8evPuab37vJF6KkFDqq9uDq2WXh877Qw6iaVC

设置密码后,仅凭助记词无法恢复原账户。例如直接用助记词检查:

subkey inspect "soup lyrics media market way crouch elevator put moon useful question wide"

恢复出的是5Fe4sqj2K4fRuzEGvToi4KATqZfiDU7TqynjXG6PZE2dxwyh,而不是预期的5He5pZpc7AJ8evPuab37vJF6KkFDqq9uDq2WXh877Qw6iaVC。只有同时提供密码才能完整恢复:

subkey inspect --password extra_secret "soup lyrics media market way crouch elevator put moon useful question wide"

这次输出才会正确地回到5He5pZpc7AJ8evPuab37vJF6KkFDqq9uDq2WXh877Qw6iaVC。这意味着密码成为恢复账户的"第二个秘密因子"——务必把它与助记词分开妥善保存。

密钥恢复与检查:subkey inspect

如果你手头拥有关于某个密钥的部分信息,inspect子命令可以帮助你补全并展示它的完整信息。

从助记词或种子恢复(拥有秘密信息时)

subkey inspect < mnemonic | seed >

例如传入秘密种子:

subkey inspect 0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d

输出:

Secret Key URI `0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d` is account: Secret seed: 0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d Public key (hex): 0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515 Account ID: 0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515 SS58 Address: 5Hpm9fq3W3dQgwWpAwDS2ZHKAdnk86QRCu7iX4GnmDxycrte

仅凭公钥信息检查(无秘密信息时)

如果只有公开数据(公钥或地址),同样可以检查,但只会得到公开字段的子集:

subkey inspect --public < pubkey | address >

两条使用注意:

  • 助记词 → 种子是单向的:从助记词可以恢复出秘密种子,反之从种子无法还原出助记词;
  • 公开信息不可反推秘密:出于显然的原因,仅传入公钥或地址时,inspect永远无法恢复出任何秘密信息。

消息签名与验证

使用私钥签名

subkey 允许用私钥对消息签名,任何人都可以用你的公钥验证签名:

echo -n <msg> | subkey sign --suri <seed|mnemonic>

其中--suri指定签名所用的密钥来源(秘密种子或助记词)。示例:

MESSAGE=hello SURI=0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d echo -n $MESSAGE | subkey sign --suri $SURI

输出(十六进制签名):

9201af3788ad4f986b800853c79da47155f2e08fde2070d866be4c27ab060466fea0623dc2b51f4392f4c61f25381a62848dd66c5d8217fae3858e469ebd668c

注意:每次执行sign的输出都会不同,因为签名过程中引入了随机性(如随机 nonce)。每份签名虽然各不相同,但都是有效的。

使用公钥/地址验证签名

给定消息、签名和地址,verify可以判断该消息是否确实由该地址对应私钥的持有者签名:

echo -n <msg> | subkey verify <sig> <address>

示例:

MESSAGE=hello URI=0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515 SIGNATURE=9201af3788ad4f986b800853c79da47155f2e08fde2070d866be4c27ab060466fea0623dc2b51f4392f4c61f25381a62848dd66c5d8217fae3858e469ebd668c echo -n $MESSAGE | subkey verify $SIGNATURE $URI

验证成功时输出:

Signature verifies correctly.

验证失败(例如签名不匹配、消息被篡改或地址不对应)时输出:

Error: SignatureInvalid

这套签名/验签机制基于 Substrate 的加密原语构建,底层同样支撑着对编码交易的签名与验签场景——在 Substrate 系链上,交易签名正是利用同一套私钥派生与签名算法完成的。换句话说,generate产出的私钥既可以用来签名消息,也可以用于签名链上交易。

派生分层确定性(HD)子密钥

subkey 支持通过 SURI(Secret URI)路径派生子密钥。在 SURI 中使用/分隔派生路径,即可从一个根密钥派生出结构化、可复现的子密钥树。仓库中的真实用法出现在节点开发链的注释示例里,bin/node/cli/src/chain_spec.rs 给出了两种典型写法:

// for i in 1 2 3 4 ; do for j in stash controller; do subkey inspect "$secret"/fir/$j/$i; done; done // for i in 1 2 3 4 ; do for j in session; do subkey --ed25519 inspect "$secret"//fir//$j//$i; done; done // generated with secret: subkey inspect "$secret"/fir

从这段注释可以看出两个关键用法:

  1. 路径派生:subkey inspect "$secret"/fir/$j/$i会在根秘密$secret之后依次追加fir、$j(如 stash/controller/session)、$i等派生路径段,每一段都会派生出一个确定性的子密钥对。这与 BIP32 风格的分层确定性思路一致,非常适合为测试网批量生成"stash / controller / session"三套角色密钥。
  2. 指定签名算法:subkey --ed25519 inspect ...用于在默认的 SR25519 算法之外显式选择 ED25519 算法。Substrate 生态主要使用 SR25519(基于 Schnorr 签名,速度快、适合链上验证),但在需要兼容 ED25519 的场景(例如一些已有的签名工具链)时可以切换。

这种派生方式让开发者可以用一个根助记词 + 路径规则管理一整组相关密钥,而不是为每个账户单独保存一份秘密。

虚荣地址生成:subkey vanity

subkey 内置了虚荣地址(vanity address)生成器,可以搜索一个地址中包含指定模式的种子:

subkey vanity --network polkadot --pattern bob

其中--network指定目标网络的 SS58 地址格式,--pattern指定希望地址包含的字符串。示例输出:

Generating key containing pattern 'bob' best: 190 == top: 189 Secret Key URI `0x8c9a73097f235b84021a446bc2826a00c690ea0be3e0d81a84931cb4146d6691` is account: Secret seed: 0x8c9a73097f235b84021a446bc2826a00c690ea0be3e0d81a84931cb4146d6691 Public key (hex): 0x1a8b32e95c1f571118ea0b84801264c3c70f823e320d099e5de31b9b1f18f843 Account ID: 0x1a8b32e95c1f571118ea0b84801264c3c70f823e320d099e5de31b9b1f18f843 SS58 Address: 1bobYxBPjZWRPbVo35aSwci1u5Zmq8P6J2jpa4kkudBZMqE

于是 Bob 得到了一串以自己名字开头的地址:1**bob**YxBPjZWRPbVo35aSwci1u5Zmq8P6J2jpa4kkudBZMqE。

需要提醒的是,虚荣地址生成本质上是暴力搜索:模式越短越容易命中,模式越长越耗时。例如 3 个字符的bob可以较快搜到,而 5 个字符的alice需要尝试的随机地址数量呈指数级增长,耗时显著加长。在批量搜索或长时间运行时,应根据硬件情况量力而行。

密钥安全最佳实践

仓库 bin/utils/subkey/SECURITY.md 对密钥安全给出了明确的守则,总结如下:

subkey 会生成并展示两份"秘密"信息:

  • secret phrase(助记词):一串单词,默认 12 个(也可为 12、15、18、21 或 24 个);
  • secret seed(秘密种子):一个大十六进制数值。

私钥面临两类核心风险:

  • 丢失:没有做好备份,密钥一旦丢失即永久无法找回;
  • 泄露:包括恶意软件、钓鱼、键盘记录器、备份存放于联网且未妥善保护的机器等多种途径。

你必须确保:既不丢失这两份秘密,也不让除你之外的任何人访问到它们。

具体行动准则:

  • ☠️绝不在任何情况下向任何人透露助记词或秘密种子——一旦泄露,对方即可动用你的资金、代你发送交易;
  • ☠️ 如果有人索要你的助记词或秘密种子,可以确信对方是在试图盗取你的资金;
  • ✅SS58 地址本来就是公开信息,可以放心分享,收款或交互只需要你的公开地址;
  • ⚠️ 同一把密钥在多条链上复用虽然技术上可行,但通常不建议,除非你有充分的理由并理解其中的风险与弊端;
  • 建议先在测试网(如 Westend)上练习,测试网上的代币损失不会带来财务后果,是安全的实验场所。

此外,bin/utils/subkey/README.md 还提醒:若将 subkey 输出保存到文件,务必设置合适的文件权限,并在用完后尽快删除。

获取完整命令列表

本文覆盖了 subkey 的核心高频操作,但并非全部能力。任何时刻都可以通过内建帮助查看完整的命令清单:

subkey --help

大多数子命令还提供更细粒度的帮助,例如:

subkey generate --help

小结

subkey 是 Substrate 生态密钥管理的标准工具:generate负责产出密码学安全的密钥对(支持 JSON 输出与自定义密码),inspect负责从助记词、种子乃至公钥地址恢复和检查密钥信息,sign/verify提供消息及交易签名验证能力,SURI 路径派生支持按角色批量派生子密钥,vanity可生成含指定模式的地址。官方同时提供cargo install与 Docker 容器两种分发方式,容器镜像由仓库中的 subkey.Dockerfile 构建,以非 root 用户、单二进制入口的方式交付。

无论你是 Substrate 节点运营者、平行链开发者,还是链上账户的使用者,掌握 subkey 就等于掌握了账户密钥从生成、备份、恢复到使用的完整闭环。请始终牢记:助记词与秘密种子是你的全部资产,离线生成、妥善备份、绝不外泄。

  • 区块链
  • 开发框架
  • 后端

【免费下载链接】substrate

Substrate: The platform for blockchain innovators

项目地址:https://gitcode.com/gh_mirrors/su/substrate
点击查看免费下载

相关推荐

上一篇:Flet LayoutControl 完全指南:尺寸、定位、2D 变换与隐式动画实战
下一篇:Vuetify0 发布候选(RC)深度解读:API 冻结、默认无障碍与 v1.0 冲刺全景

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 10:09:25

Java 开发里的埋点是什么

目录 埋点采集什么信息 Java 里常见的埋点实现方式 1. 代码硬编码埋点&#xff08;最基础&#xff09; 2. AOP 切面埋点&#xff08;Java 项目最常用&#xff01;&#xff09; 3. 中间件 / 异步埋点 4. 字节码埋点&#xff08;探针&#xff0c;如 SkyWalking&#xff09;…

作者头像 李华
网站建设 2026/9/26 10:08:27

Windows下用QEMU模拟ARM64安装银河麒麟V10全流程

不扯虚的&#xff0c;先说一下我为什么折腾这个。当时接了一个信创适配的活儿&#xff0c;软件要跑在银河麒麟V10上&#xff0c;CPU是鲲鹏的ARM架构。可我手边没有鲲鹏服务器&#xff0c;连一台ARM开发板都临时借不到&#xff0c;只有一台Windows笔记本。最开始想过上云&#x…

作者头像 李华
网站建设 2026/9/26 10:06:48

Java List查找对象性能优化:从contains到HashMap的O(1)方案

先聊个实际场景吧。有一次线上接口报警&#xff0c;CPU 被打满&#xff0c;十几个 QPS 就把服务拖到超时。查了半天&#xff0c;锅竟然出在一个 1 万大小的 List 上——有同事在循环里反复调用list.contains()去判断某个对象是否存在。1 万条数据不算大&#xff0c;但循环 500 …

作者头像 李华