两端怎么在公开信道上协商出共享密钥?拿到的共享秘密能直接当密钥吗?对方给的公钥要检查什么?没有认证会怎样?这篇用 JDK 的 KeyAgreement 逐项真实运行,并实现 HKDF 验证标准向量。
X25519 公钥(X.509 编码) 44 字节,私钥(PKCS#8) 48 字节;共享秘密 32 字节;双方算出的相同 = true;与第三方 C 算出的相同 = false EC P-256 公钥(X.509 编码) 91 字节,私钥(PKCS#8) 67 字节;共享秘密 32 字节;双方算出的相同 = true;与第三方 C 算出的相同 = false EC P-384 公钥(X.509 编码)120 字节,私钥(PKCS#8) 80 字节;共享秘密 48 字节;双方算出的相同 = true;与第三方 C 算出的相同 = false 共享秘密(前 8 字节)0891a1c2d495f94c…,看起来是随机的,但它是 DH 的原始输出(椭圆曲线上的点坐标),不是可以直接拿来当 AES 密钥的均匀随机字节串;应该先经过 KDF 同一个 KeyAgreement 对象再次 generateSecret() → IllegalStateException: Not initialized correctly X25519 计算共享秘密 120 μs/次;生成密钥对 130 μs/次 EC P-256 计算共享秘密 590 μs/次;生成密钥对 117 μs/次 EC P-384 计算共享秘密 1299 μs/次;生成密钥对 681 μs/次 DH 2048(有限域) 计算共享秘密 354 μs/次;生成密钥对 347 μs/次
点坐标改了 1 个 bit(不在曲线上):KeyFactory.generatePublic 成功,但 ECDH → InvalidKeyException: Point is not on curve 无穷远点作为公钥 → IllegalArgumentException: w is ECPoint.POINT_INFINITY 坐标 (1, 2) 不在曲线上 → InvalidKeyException: Point is not on curve P-256 私钥配 P-384 公钥(曲线不匹配)→ InvalidKeyException: Invalid coordinate X25519 的 u = 0(小阶点)作为对方公钥 → ECDH 抛 InvalidKeyException: Point has small order X25519 的 u = 1 → InvalidKeyException: Point has small order
RFC 5869 附录 A.1 的测试向量:自己用 HmacSHA256 实现的 HKDF 输出 3cb25f25faacd57a90434f64… 与标准值一致 = true 同一个共享秘密、不同的 info:encrypt 密钥 53265870decc…,mac 密钥 ff8797afab84…,两个密钥相同 = false;双方各自派生的 encrypt 密钥相同 = true 换一个 salt 派生出的 encrypt 密钥相同 = false 一次派生 64 字节:前 32 字节 95e638cf…,后 32 字节 8d1418c5…(HKDF-Expand 的输出前缀与短输出一致:true) 用 Alice 派生的密钥加密、Bob 派生的密钥解密:hello
无认证的 DH,中间人 Mallory 把双方的公钥都换成自己的:Alice 与 Mallory 的秘密相同 = true;Bob 与 Mallory 的秘密相同 = true;Alice 与 Bob 的秘密相同 = false(两边都以为在和对方通信,其实各自与 Mallory 建立了密钥) Bob 用长期签名密钥(Ed25519)签名自己的 DH 公钥,Alice 事先知道 Bob 的验证密钥:验证真实公钥 → true Mallory 把 Bob 的 DH 公钥换成自己的,沿用 Bob 的签名 → 验证 false(中间人被发现;前提是 Alice 事先持有可信的 Bob 验证密钥) 握手记录(transcript)绑定进密钥派生:KDF 的 info 里放入双方公钥,即使对方被替换,两边派生的密钥也不一致:true