密码学 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 失守可泄露私钥)业务身份 + 请求...
密码学 04:HMAC 为什么需要秘密
服务端收到一个订单和一个 SHA-256 值,重新计算后两者一致。第 00、03 篇已经指出问题:攻击者能改订单,也能随手再算一个公开摘要。于是有人在订单与服务端之间增加了双方事先知道、攻击者不知道的密钥,再问:能否仅凭一串公开的认证值发现攻击者改过订单?可以,但要记住一个代价:接收者既然拥有同一把密钥,也有能力给任意新订单生成同样有效的值。 本篇把这种机制称为消息认证码(MAC),选择标准化的 HMAC-SHA-256 做最小实验。过去的文章有时把 HMAC 口语化地称为“对称密钥签名”;为了避免与私钥签、公钥验的数字签名混淆,这里固定叫 MAC。谁握有秘密、谁能验证与谁能伪造,是这一区别的核心。 换掉可公开重算的东西 设合成订单的字节已明确为 b"order=demo-001;quantity=1"。服务端和调用方事先通过可信渠道共享一把独立的密钥,消息通过可能受攻击者操控的链路送达。发送者计算 HMAC,把订单与 tag 一起交给接收者;接收者用同一把密钥重新计算并按安全接口比较。 123456789发送方(持有 K) ...
密码学 03:哈希能证明什么
下载一份账单,网页同时给出“账单的 SHA-256”。算完发现两串十六进制相等:能说这份文件是银行发的吗?如果下载页和摘要都被同一个攻击者替换,不能。同样地,把一笔交易的哈希值打印出来,既不证明是谁授权交易,也不证明它已经写入共同历史。摘要比较首先回答的是已知预期摘要时,当前输入是否与它对应;这里的“已知”必须说清来源。 在 00 里,攻击者同时换掉合成订单与公开预期摘要,相等检查仍返回真。02 又展示不同的字段组可以拼出完全相同的字节,不论使用哪种哈希函数都救不了未定义的字段边界。这篇再看哈希本身承诺哪类困难,以及三个经常被混称为“找到碰撞”的不同问题。 输入、输出和可信起点 对固定算法 SHA-256,输入是一串确切字节,输出是 32 字节。例如 b"abc" 的摘要为 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad。输出的十六进制有 64 个字符,只是每个字节写成两个字符后的显示形式。该算法不使用秘密密钥,任何拿到消息的人都能重复计算。 123456作者文件字节 -- SH...
密码学 02:密码学处理哪些字节
订单页面上看见 quantity=1,不等于签名程序看到相同的消息。浏览器可以发送 JSON、表单字段或二进制协议;同一个 JSON 对象还可以有不同的空格和键顺序。如果一个端点对它收到的原始字节做摘要,另一个端点先解析再重新输出 JSON,即使两边都在谈“同一笔订单”,校验也可能失败。更糟的是,两个不同字段组合能拼出完全相同的字节:此时再强的哈希或签名算法也无法代替消息边界的约定。 第 00、01 篇讲“要保护什么”。这篇回答一个更靠前的问题:确切保护了哪些字节?输入没有说清,关于身份、用途和完整性的结论都容易越界。 从可读字符走到被验证的输入 假设订单文本里有中文“订单”。程序要先约定字符编码,例如 UTF-8,再确定字段顺序、数字表示、空白处理与字段长度,最后才可把结果交给摘要或签名操作。 123456789业务对象 {"sku":"book","quantity":1} | 指定字段、顺序、字符编码、长度和表示法 v确定的一串字节 --------> SHA-25...
密码学 01:窃听、篡改、冒充与重放怎样分开
浏览器向订单接口发 quantity=1。攻击者如果能看见消息,已经造成泄露;如果能把数量改成 9,是另一种失败;如果直接构造一条“来自用户 A”的新消息,前两个问题都不解释它;如果原封不动重发旧的有效订单,还会产生第四种失败。“消息安全”不是一个布尔值:必须先说明攻击者站在哪里、能做什么,接着逐项说出想阻止什么。 上一篇用摘要说明“当前消息和当前摘要相等”不能凭空证明来源。这篇继续使用同一合成订单,把四种攻击放在一个不受保护的教学通道上。它没有加密或签名:实验的作用是构造要阻止的失败,不是伪装成已经部署了安全协议。 给攻击者划出能力边界 在这个实验里,浏览器生成 JSON 字节;攻击者控制浏览器与教学接收者之间的一段明文通道。接收者相信 JSON 里的 sender 字段,但没有核对任何身份凭据。攻击者可读取字节、改动内容、插入新消息或记录后再发送。每种能力带来不同的问题。 1234567 可读、可改、可注入、可记录/转发浏览器 -- 原始订单字节 --> [ 网络中的攻击者 ] -- 任意字节 --> 接收者 ...










