密码学 00:从 HTTPS 与交易两张图开始
把一笔订单从浏览器发往网站,再把一笔测试交易从钱包交给节点,这两件事都会出现“哈希、公钥、签名”。如果只记住算法名称,很容易做出一个危险的推论:网站已经有 HTTPS,订单自然是真的;交易验签成功,货物自然已经交付。两个推论都不成立。 本系列不从算法表开始,而从攻击者能做什么开始。本篇先看两条路径的责任边界,再拿固定订单字节做一个可以失败的摘要实验。图中的 TLS 与链节点是后续章节的研究对象:本篇没有运行 TLS 握手,也没有发送真实交易。 第一张图:订单发到 HTTPS 服务 设浏览器准备提交公开的合成订单 order_id=demo-001, sku=book, quantity=1。攻击者可能控制一段网络,能够观察、修改、转发旧数据,也可能提供一个伪装成服务端的地址。目标不是让订单“看起来复杂”,而是让客户端与服务端各自知道自己正在依赖什么。 12345678浏览器(预期服务名、可信 CA 配置) HTTPS 端点 | 连接 + 验证身份 / 协商临时密钥 | |<===== 双向 TLS ...
密码学 19:QUIC 如何复用 TLS 而自行保护数据包
浏览器走 HTTPS 提交 order=demo-001;quantity=1,如果服务改用 HTTP/3,连接依旧需要验证服务身份,也依旧需要协商密钥。常见的一句概括是“QUIC 跑在 UDP 上,内置 TLS 1.3”。这句话若被理解成“把 TLS 的一条记录装进一个 UDP 包”,就会把后续的包号、密钥更新和加密边界都画错。问题应具体到字节:TLS 交给 QUIC 的是什么?QUIC 自己对哪些字节做了什么? 从订单到数据包:谁执行哪一步 RFC 9001 将两件事分给不同组件。TLS 负责握手消息的语义:双方交换临时 key share,服务用证书及握手签名绑定它的身份,Finished 检查到目前为止的握手转录,随后得到不同方向、不同阶段的流量秘密。QUIC 承载握手字节,并用这些秘密派生自己的包保护密钥。TLS 的记录层不会为 QUIC 的订单创建 TLSCiphertext 记录。QUIC 用自己的帧、包号、头字段和 AEAD,把订单所在的流数据放进受保护的数据包中。 12345678浏览器 ...
密码学 20:SSH 为什么既有主机密钥,又有登录密钥
连接一台测试服务器,SSH 客户端既要判断“这真是打算连接的那台主机”,又要让服务器判断“这位用户能不能登录”。两种判断都可能用公钥签名,却不意味着两把密钥可以合并:第一道问题由客户端验证服务器,第二道问题由服务器验证用户。如果把 SSH 登录的公钥当成主机身份,网络上的冒充者就能在用户出示登录证据以前让客户端连错服务器。 第一把密钥:连接的是谁 SSH transport 先协商密钥交换算法、主机公钥算法、加密及完整性算法。以 RFC 4253 §8 的 DH 例子说明输入:客户端和服务器各自产生一份不公开的临时指数,交换公有 DH 份额,分别算出相同的 K。这些公有份额不提供身份。服务器还发送公开的主机密钥 K_S,用相应主机私钥对交换摘要 H 签名。 H 不只是“连接上了”这四个字的哈希;例子的输入包含双方版本串、双方算法协商报文、服务器主机公钥、两份交换公有值及共同秘密 K,并按 SSH 字节类型编码。客户端验证签名,也必须从可信途径知道哪把主机公钥属于期望的服务器。本地 pinned known_hosts 是一种来源;主机证书另需明确签发者与适用主机。若首次连接只点“...
密码学 21:VPN 的密钥怎样建立——IKEv2、IPsec 与安全关联
一台开发机经 VPN 访问测试网络中的 HTTPS RPC,使用钱包提交一笔无价值交易。出现三个“连接安全”的说法:VPN 已连接,HTTPS 握手成功,交易签名验过。它们保护的对象并不相同。VPN 先解决“这两个网络端点如何商定保护哪些 IP 包、用哪些方向的密钥”,不能因此替钱包签交易,也不保证远端 RPC 给出的区块状态可信。 先把通道建立者与数据保护者分开 IKEv2(RFC 7296)协商 IPsec 安全关联;ESP(RFC 4303)保护落到相应关联上的数据包。一个安全关联,简称 SA,不是一张写着“本 VPN 全部通信都安全”的通行证:它包含标识、算法、密钥、方向、序号等状态;可保护的流量还受流量选择器与路由影响。IKE 自己的 IKE SA 保护后续控制消息;给数据包用的是 Child SA。ESP 双向流量通常使用两条方向不同的 SA。 1234567开发机(发起方) 测试网关(响应方)IKE_SA_INIT: 提案、临时 DH 值、Ni ----> 选择提案、临时 DH 值、Nr ...
密码学 22:DNSSEC 与 DoH、DoT 各保护哪一段
浏览器打算向 orders.test 发一笔合成 HTTPS 订单。它先要把域名解析为地址。有人建议“启用加密 DNS”;有人建议“启用 DNSSEC”。如果客户端到解析服务的连接虽有 TLS,但解析服务给出了攻击者操纵的答案,该相信谁?反过来,如果能验证 DNSSEC 签名,路边的监听者就看不见域名查询了吗?两种建议回答的根本不是同一道题。 被保护的不是同一段 普通语言说,DoH/DoT 是对客户端与它选择的解析服务之间的传输建立加密、认证通道。DNSSEC 是让有能力验证的解析方核对某个域所有者签出的 DNS 数据来源和完整性。边界如图: 12345678客户端 stub -- DoH: HTTPS / DoT: TLS --> 选定的解析服务 ----> 权威 DNS │ 验证配置的 TLS 服务名 │ 解析器在 TLS 终点能看到问题 │ │ 可能自己做 DNSSEC 验证 └── 若自己验证 DNSSEC:从预置信任锚 ...
密码学 23:API 请求认证为什么还需要规范化、时间与消费状态
一笔合成订单通过 HTTPS 发到服务端,订单正文 order=demo-001;quantity=1,客户端和服务端另约定用 HMAC 验证这笔 API 请求。第一次成功后,攻击者原封不动重发先前捕获的请求:请求体没被改,HMAC 依然正确。攻击者不必知道秘密,也可能让业务再次处理这笔订单。这不是 HMAC 被破解,而是校验输入里缺了一道“此请求是否已经生效”的判断。 签名哪些字节,谁持有密钥 HMAC 的计算方和验证方都必须有同一共享秘密;服务端也能生成同样的 MAC。因此它和钱包私钥签名交易不一样:交易可由其他参与者用公钥验证,而 HMAC 的服务端一方不能凭自己也能制造的 MAC 向第三方证明“只有客户端能生成”。TLS 又是第三件事:浏览器确认连的是 orders.test 并加密连接,不自动把 HTTP 订单绑定到应用层的权限或一次性消费状态。 为了让 HMAC 验证表达明确含义,发送者与验证者必须对同一串字节达成一致。例如只签 body、不签 HTTP 路径,原本针对 /orders 的正文在另一个端点也可能变成有意义的输入。简单用 method + path + ...
密码学 24:JWT、JWS 与 JWE——可读令牌、签名令牌和加密令牌
订单 API 收到一个形如 xxxxx.yyyyy.zzzzz 的访问令牌。开发者看到第三段是签名,就把第二段用户信息当成“被加密过的秘密”;又有人看到验签通过,就跳过了这个令牌到底签给哪个服务、什么时候过期的检查。攻击者完全可以复制一个仍在有效期内的 bearer token,带着它走一条自己的 TLS 连接。问题并不是 JWT 这种格式“安全还是不安全”,而是把不同操作解决的问题说成了一回事。 看一个令牌时先问:它做了哪种操作 JWT 是在各方之间传递声明(claims)的格式及处理约定。常见 iss 标明发行者,aud 标明预期使用者,exp 给出过期时刻。JWT 可以承载为 JWS(签名或 MAC 保护),也可以承载为 JWE(加密),还可能有嵌套组合;不是看到三个点分隔段就能断言所有 JWT 都加密了。 JWS compact 有三段:受保护头、payload、签名。前两段是 base64url 编码;编码不提供保密性,拿到令牌的一方即使没有公钥,也能读出 payload 的 JSON 声明。持有者可能仍因未通过签名验证而不能可信地采用这些声明;可读与可信是两件不同的事...
密码学 26:私钥、公钥、地址与钱包为什么是不同对象
钱包界面只显示一条收款地址,却能管理多条链、多个账户、找零和不同的接收方式。把“私钥→公钥→地址”画成三格直线,确实能解释某一类外部账户,却解释不了为什么 Bitcoin 同一公钥能对应不同地址、为什么合约也有地址,以及只有地址的钱包为什么不能签交易。地址是告诉付款者如何指向一种接收条件的编码,钱包则是保存、发现、构造并可能签署相关交易的程序或设备。 从一个临时私钥到两种 Bitcoin 接收方式 在本章的临时实验中,coincurve 随机生成一把 secp256k1 私钥;绝不保存到 Git 或文章中。对应公钥有 33 字节压缩表示和 65 字节以 04 开头的非压缩表示。密码学上的椭圆曲线点相同,但进入后续哈希的字节序列不同。Bitcoin 常见 P2PKH、P2WPKH v0 可以取压缩公钥的 SHA256→RIPEMD160,生成 20 字节 HASH160,然后选择不同的锁定条件与网络编码: 123456一次性 Bitcoin 签名私钥 ─曲线乘法─> secp256k1 公钥点 └─33 字节...
密码学 27:Bitcoin 交易到底签哪些字节——UTXO、锁定条件与 sighash
钱包要从一个合成 Bitcoin 输出拿出 7000 sat,准备付给别人 5000 sat;假设剩余 2000 sat 作为手续费。有人说“把交易哈希签一下就行”。但交易序列化有见证数据、有上一笔交易的输出金额,还有多种 sighash 标志:究竟是哪些字节进签名,签名以后为什么节点还要查这笔输出有没有被别人先花? 本章只取一种确定的条件:Bitcoin 隔离见证 v0 的原生 P2WPKH、SIGHASH_ALL。它足以把输入、旧输出、收款方以及消费状态分开;legacy 旧式 sighash 与 Taproot 的 BIP 341 采用其他规则,不可直接复制这段算法。 拥有私钥与有权花费某个输出是两道判断 Bitcoin 使用 UTXO:上一次交易的某个输出含金额及锁定脚本;本次交易输入引用它的 outpoint(前交易 ID 和输出索引),并提交能满足锁定条件的数据。对本例 P2WPKH 而言,旧输出的见证程序为 OP_0 <20字节HASH160(压缩公钥)>,这笔花费的 witness 提交签名及压缩公钥;节点把公钥哈希与旧输出预期值核对,再按指定签名算法...
密码学 28:Ethereum 交易与 EIP-712 授权——两个 nonce 与两个验证点
钱包签了一条合成订单授权,并经 HTTPS RPC 送出一笔测试交易。签名都来自同一钱包私钥,为什么交易会因为账户 nonce 用过而失败,而订单授权还可能被再次兑现?这两个对象在不同的地方使用不同的消费状态:交易由执行层节点处理,链下类型化授权由部署它的应用或合约处理。EIP-712 帮助双方就类型与域达成一致,但原规范明确说它不包含重放保护。 一把私钥签两种不同的字节 选定 EIP-1559 的 0x02 类型交易,签名输入不是界面上的“转 100 wei”几个汉字。它包括 chain ID、此账户在这条链的交易 nonce、费率上限、gas limit、目的地址、数量、data 和 access list 的指定 RLP 编码,前面还有类型字节。节点先按此交易类型核对签名,再看账户余额、nonce、费用、gas 和执行规则;EIP-1559 的链 ID 必须绑定到目标链,不应仅因为椭圆曲线验签成立就忽略它。 12345678钱包私钥 ├─ 交易 type 0x02:keccak256(0x02 || RLP(chainId, accountNonce, │ ma...










