密码学 14:证书为什么能绑定服务名与公钥
有人给浏览器发来一把公钥,说“我是 orders.test”。浏览器用它验证同一人送来的签名,结果当然可能成功。13 已经说明:**签名能证明私钥持有人参与了那次操作,却不能只靠自报的公钥确定持有人叫什么。**网站身份校验必须从浏览器自身预期访问的名称和事先配置的可信根出发,而不能让连接对面临时决定信任起点。 本篇用临时合成 CA 给 orders.test 签发一张测试证书,只在本机回环地址启动 TLS 1.3 服务端。客户端明确加载这一张 CA,匹配 DNS 名称后提交一笔合成订单;另试“名称不符”和“客户端没有这张 CA”两个失败路径。这里跑到了真实 OpenSSL/Python TLS 端点,但不是浏览器实验,更不把 CA 装进系统信任库。 验证者有一个先于网络对话的预期 TLS 连接发往哪个 IP 与客户端要认证哪个服务是两个不同输入。测试里套接字连的是 127.0.0.1 的随机端口,但客户端指定的服务名称是 orders.test。证书的 subjectAltName(SAN)包含 DNS 名称 orders.test;用于验证这张证书的测试 CA 只交给本次客户端...
密码学 13:正确公钥从哪里来
浏览器连上一个声称是 orders.test 的服务,服务把自己的公钥和对某段数据的签名一起发来。浏览器用收到的公钥验证,返回真。现在可以提交订单吗?还不可以:攻击者完全可以自己生成公私钥,给自己编的一段消息签名,再附上自己的公钥。算法做对了,身份却是假的。10 篇说明 DH 可以和中间人分别建立共同秘密;12 篇说明签名可以被正确公钥验证。要把两种能力组合为“与预期服务安全通信”,先得知道应当信任哪个公钥,并确认它签的是这一次、这个角色、这段通信。 本篇用事先固定的合成公钥模拟“浏览器已有可信起点”,再演示恶意自签、错误服务名、换 key share 与重放。固定公钥只是教学前提,不是现实世界的信任来源;14 的证书链才真正回答 HTTPS 的服务名与公钥如何绑定。 相同签名算法,两种截然不同的输入 客户端看到“消息 + 签名 + 公钥”时,至少要决定三个独立输入:希望连接的服务名称,从可信配置、证书链或别的认证制度拿到的预期公钥或公钥约束,以及要验证的确切消息字节。如果把公钥信任和消息验证都留给发件者随意指定,验证会变成自我背书。 12345678本地预期服务 orders....
密码学 12:数字签名验证了什么
钱包要给节点一个授权证据:节点必须能独立检查“这组明确字节经某个有权的私钥持有人签署”,却不应该因此获得生成新授权的能力。04 篇的 HMAC 做不到后一条,因为接收者也持有同一把密钥。数字签名提供另一种分工:私钥参与生成签名,公钥参与验证签名。但是验签函数返回 true 仍有至少两个问题悬而未决:这个公钥属于谁?这段被签的字节是否就是需要授权的那件事? 这篇用成熟库分别运行 RSA-PSS、P-256 上的 ECDSA 与 Ed25519 的正负验签,严格区分“原语实验通过”和“业务或链上的授权已经生效”。 三个输入,不是一句“签过了” 发送者在既定算法与编码约定下对确定字节生成签名;验证者必须同时拥有消息字节、签名和可信来源的正确公钥,还要知道怎样解码签名与选择算法参数。对恶意者自造密钥并签署的消息,verify(恶意者的公钥, 消息, 签名) 也能返回真,不能因此推断发件人是合法钱包主人或预期网站。 1234567甲:私钥(不得外发) + 确定的消息字节 -> 签名 | | ...
密码学 11:一个秘密为什么派生多把密钥
10 篇里,两个临时密钥交换参与者算到同一份秘密。接下来的订单请求和服务端响应,能不能把这串值直接拿来当一把万能密钥,既负责客户端发送又负责服务端发送,未来连请求认证也复用?不该这样做。不同方向、阶段与用途需要隔离:一边的密钥、nonce 进度或使用权限变化,不应无端变成另一边的隐性输入。派生密钥是从一份已经足够不可猜测的输入得到明确用途的多份输出,不是把弱密码凭空升级成强秘密。 这篇用 HKDF 的标准向量验证算法输出,再对一份进程内临时秘密分别派生客户端订单、服务端订单和 RPC 用途密钥。不打印实际临时秘密与派生密钥;公开的十六进制输出仅限 RFC 官方测试向量。 提取与扩展各自输入什么 HKDF 使用 HMAC 构成两步:先对输入密钥材料 IKM 与 salt 进行提取,得到伪随机密钥 PRK;再以 PRK、表示上下文和用途的 info、指定输出长度 L 做扩展,得到所需密钥材料。接口可以简写如下,但真实调用一定要给出字节级参数: 123456789101112 已有秘密 IKM + salt | v HKDF-Ex...
密码学 10:两端如何得到共同秘密,为什么仍怕中间人
浏览器与 HTTPS 端点第一次见面,却能协商供双方使用的流量密钥。是不是把这把密钥直接从网络上发过去了?不是。09 用小整数说明:双方交换各自算出的公开值,再用自己没有外发的私有输入,最终可以得到同一个结果。问题在于:没有人核对交换来的公开值到底属于谁,攻击者也能和两边分别完成运算。本篇将“共同秘密已算出”和“对端身份已确认”拆成两个各自可失败的断言。 这里会用成熟库执行真实 X25519 椭圆曲线密钥交换,但不运行 TLS 端点。真实库结果仅证明在本次输入下原语计算正确;服务名、证书、握手摘要与通信保护要在 13–16 章继续组合。 正常路线:线上传的不是最终秘密 仍用 09 的手算:公开模数 23、生成元 5,甲有私有指数 6,乙有私有指数 15。甲发公开 share 8,乙发公开 share 19;两人将对方公开 share 与自己的私有指数结合,分别算到结果 2。 123456甲 可被旁观的网络 乙私有 6 -> 发送 8 ---------> ...
密码学 09:模运算、群与困难问题需要多少数学
浏览器和 HTTPS 服务端没在线上发一把共同密钥,却能算出一把;钱包用私钥生成签名,节点只拿公钥就能验证。这些关系看着像“颠倒运算方向”,但若先读完一本数论教材才能解释,读者很可能忘了它们解决的原始问题:不安全链路上怎样形成共同秘密,或怎样让不知道私钥的人验证授权。这篇只补能看懂 DH、RSA、椭圆曲线责任分工的数学,不把小整数游戏说成生产密码学。 在本章所有例子中,数字刻意小得能手算;所谓“私有指数”也写在公开文章里。没有任何保密性承诺。真实参数、消息编码、身份验证和侧信道还要另行处理。 模运算是按一个固定范围计算 29 % 23 == 6:每次计算只保留除以 23 后的余数。Python 可直接执行 pow(5, 6, 23),意思是 5 的 6 次方再对 23 取余,不必先真的造一个巨大整数。许多密码学结构需要在这样的有限集合中计算。这里最重要的不是记定义,而是画出什么信息公开,什么输入暂不公开。 12345678公开数字(模数、运算规则) + 私有数字(某人的指数) | v 运算后输...
密码学 08:密码为什么不能直接做 SHA-256
数据库只存 SHA-256(密码),不存密码原文。泄漏后,攻击者是不是就无法还原用户密码了?如果密码是 book123 这样的小字典成员,攻击者拿到摘要,离线把常见猜测逐个 SHA-256 后比较即可;不需要攻击 SHA-256 的原像困难假设。输入空间太小,找目标输入并不贵。换一把更长的密码当然有帮助,但服务端存储方案仍必须考虑泄漏后的离线猜测成本、多用户复用猜测和参数演进。 前面 03 篇用 SHA-256 处理公开订单字节,这是合适的教学摘要任务。密码不是随机的高熵消息,不能把安全哈希函数对一般原像的困难性直接套在用户选择的短密码上。本篇用真实 Argon2id 库对照 SHA-256 的有限字典:同一个合成弱密码,两种方案最终都能猜到;区别在于 Argon2id 允许服务端设置验证所需的内存、遍数等成本,而不是承诺“弱密码永远猜不出来”。 攻击者读到什么,谁持有秘密 注册时服务端得到密码,生成一份各条记录独立的 salt,记录盐、算法版本、成本参数与密码哈希输出。登录时用这条记录的参数再算一次,按安全接口比较。数据库一旦泄漏,攻击者可离线读取盐、算法及参数,还能对候选密码...
密码学 07:AEAD 怎样保护内容与上下文
订单内容经加密后谁也看不懂,攻击者却把原本属于 POST /orders 的密文交给另一个接口,或者把标记身份的请求上下文换成了另一个用户。如果接收端只解密、不核对这些可见的上下文与被保护的消息是否同属一笔操作,就可能处理错误对象。第 06 篇展示 ECB 会泄漏重复结构,教学 XOR 也说明不带校验的密文可能被改。这篇直接用成熟库试一个提供保密性与完整性检查的接口:AEAD。 本章不实现 TLS。它执行真实 AES-GCM 原语的固定向量以及正确解密、改密文、改标签、改上下文和故意重复 nonce 的失败对照。它证明的是所选库、所选输入下的行为,不能凭一个原语实验宣布整个 HTTPS 订单服务安全。 五类输入,三类输出与一条失败路径 设服务和客户端已安全约定一个对称密钥 K,并在这一把密钥的生命周期里不重复使用加密 nonce。调用者将确切的订单字节作为明文,把服务方法、路径和已认定用户等不需加密、却不能被错配的字节作为 AAD(附加认证数据)。这里使用 AES-GCM;得到密文和认证标签。接收者传入相同 K、nonce、AAD 与标签解密,任何不一致都应失败,不能把 upda...
密码学 06:对称加密为什么不能单独认证消息
给订单 JSON 加密后,旁观者读不出数量。是否可以省去校验身份与完整性的步骤,让接收端解密后直接创建订单?不能。“看不懂密文”只涉及某种保密目标,并不自动保证来源可信、内容没被有意改变;同一个 AES 算法配上不同的模式,观察到的泄露和验证行为也不同。本章先看一个不能用于一般订单的加密模式,再用独立的玩具模型观察改动密文的可能性,最后明确为何 07 要转向认证加密。 谁持有密钥,接收端检查什么 对称加密的典型输入是一个共享密钥、明文字节,以及所选模式要求的初始化输入;输出是密文字节。服务端拿到解密密钥后可以恢复明文。如果双方共享同一密钥,不看额外凭据,服务端通常也有能力制造看似由发送端生成的密文;这不是“只有浏览器才能签名”的公开可验证授权。 12345发送端:共享 K + 明文 + 模式参数 --> 密文 --不可信通道--> 接收端 | 共享 K 解密 v ...
密码学 05:随机数、salt、IV 与 nonce 各要求什么
订单服务给每个请求发一个 ID,钱包签交易时也会看到 nonce;密码数据库给每个用户密码加 salt,加密接口还让调用方传 IV 或 nonce。于是一个诱人的错误设计出现了:所有带这类名字的字段,都用一次 random() 生成,反正“随机就安全”。这会忽略两个相反的要求:有的值必须令攻击者难以预测,有的值最关键的是不能在同一作用域内重复;有些值甚至可以是单调计数器。 本篇选择四种具体场景,不教自己造随机数发生器。先看谁产生、谁保存、是否公开,再看如果重新使用一个值会发生什么。 12345678系统随机源 ----> 生成密钥 / 临时秘密 ----------> 只给有权持有者 \--> 密码 salt --------------------> 与密码哈希一起公开保存密钥 K + 每密钥唯一的 AEAD nonce + 明文 -> 密文/标签 ↑ 重启、并发、多副本都必须避免加密时重复ECDSA 私钥 + 消息 + 签名内部 k ----------> 签名(k 失守可泄露私钥)业务身份 + 请求...












