共享密钥的 HMAC 与私钥签名都能防篡改,边界在哪里?谁能伪造?签名多大、确定不确定?验证失败时返回 false 还是抛异常?这篇用 JDK 逐项真实运行,并量出各自的耗时。
HMAC-SHA256:标签 32 字节(2f72b69f9fb1…);同一个密钥同一条消息两次标签相同 = true(确定性)
改 1 个 bit 的消息,标签相同 = false
改 1 个 bit 的密钥,标签相同 = false
持有同一个密钥的「验证方」给自己编造的消息 {amount: 99999} 算出的标签,用同一个密钥验证 → true(验证方与签发方无法区分:HMAC 不提供不可否认性)
HmacSHA256 标签长度 32 字节
HmacSHA512 标签长度 64 字节
HmacSHA1 标签长度 20 字节
空密钥初始化 HmacSHA256 → IllegalArgumentException: Empty key
密钥长于分组长度(64 字节)时,HMAC 先把密钥哈希:用 200 字节密钥的标签 = 用其 SHA-256 摘要当密钥的标签 → trueEd25519 公钥 44 字节;签名 64–64 字节;同一条消息签两次相同 = true ;验证 true,消息被改 1 bit 验证 false ECDSA P-256 公钥 91 字节;签名 69–72 字节;同一条消息签两次相同 = false;验证 true,消息被改 1 bit 验证 false ECDSA P-256(P1363 格式) 公钥 91 字节;签名 64–64 字节;同一条消息签两次相同 = false;验证 true,消息被改 1 bit 验证 false RSA-2048 PKCS#1 v1.5 公钥 294 字节;签名 256–256 字节;同一条消息签两次相同 = true ;验证 true,消息被改 1 bit 验证 false RSA-2048 PSS 公钥 294 字节;签名 256–256 字节;同一条消息签两次相同 = false;验证 true,消息被改 1 bit 验证 false
签名被截短(63 字节)→ SignatureException: signature length invalid
签名改 1 bit → verify = false(返回 false,不抛异常)
换一个公钥 → verify = false
ECDSA 签名被截短 8 字节 → SignatureException: Invalid encoding for signature
ECDSA 签名(DER)被当作 P1363 格式去验证:SHA256withECDSAinP1363Format
DER(70 字节)交给 P1363 验证器 → verify = false
ECDSA 验证全零的 DER 签名 → verify = false
没有 initSign/initVerify 直接 update → SignatureException: object not initialized for signature or verification
验证模式下调用 sign() → SignatureException: object not initialized for signingHMAC-SHA256 对 29 字节消息:1.8 μs/次(签发与验证相同,一次计算) Ed25519 签名 428.3 μs/次;验证 420.7 μs/次(验证是签名的 0.98 倍时间) ECDSA P-256 签名 124.6 μs/次;验证 408.1 μs/次(验证是签名的 3.27 倍时间) RSA-2048 签名 934.0 μs/次;验证 34.1 μs/次(验证是签名的 0.04 倍时间)