在 macOS / JDK 21 上实测 java.util.prefs:userRoot 是 MacOSXPreferences,存在 com.apple.java.util.prefs.plist;put 不 flush 也立刻持久(kill -9 后仍读到);key 80 / value 8192 / 节点名 80 的上限;getInt 解析失败返回默认值;外部进程的修改能读到但监听器不通知;systemRoot 无权限写入。
在 JDK 21 上实测 EdDSA:Ed25519 公钥 44 / 私钥 48 / 签名 64 字节;同一消息签 20 次只有 1 个不同的签名(ECDSA 是 20 个);Java 签出的签名与 RFC 8032 测试向量逐字节相同,并与 OpenSSL 互通;篡改、截断、S ≥ L 都被拒绝;JDK 21 的 SunEC 上 Ed25519 签名 422 µs,比 ECDSA P-256(120 µs)慢约 3.5 倍。
在 JDK 21 上实测 X25519:RFC 7748 的测试向量公钥与共享密钥逐字节相同;getU() 是大端 BigInteger 而编码是小端;私钥有 5 位被 clamp 而不起作用;小阶点公钥一律抛 InvalidKeyException;与 OpenSSL 互通;协商 97 µs,比 ECDH P-256(552 µs)快约 5.7 倍。
在 JDK 21 上实测 RSASSA-PSS:必须 setParameter;盐 0 时签名确定、盐 20/32 时每次不同;RSA 2048 的最大盐是 222(SHA-256),超出时抛“Key is too short”;验证者必须知道盐长度;RSASSA-PSS 类型的密钥限定摘要与最小盐;与 OpenSSL 互通;PSS 与 PKCS#1 v1.5 的签名耗时相同(872 µs)。
在 JDK 21 上实测 CipherInputStream:GCM 被篡改或截断时一个明文字节都不交出并抛 IOException ← AEADBadTagException;CBC 无认证,中间篡改完全不被发现,NoPadding 在块边界截断悄悄少 16 字节;close() 不做验证;CipherOutputStream 忘记 close 得到 992 / 1016 字节的残缺文件;-Xmx128m 下 GCM 解密 40 MiB 起 OutOfMemoryError。
在 JDK 17 / 21 / 27 上实测 KeyPairGenerator:不 initialize 时 RSA 默认从 2048 位升到 3072 位、EC 从 256 升到 384;RSA 512–16384 位都可生成(1023 与 4097 也可以)、公钥指数必须是奇数;EC 只支持 P-256 / 384 / 521;RSA 2048 生成耗时 28–294 ms 波动 4.5 倍而 EC 几乎固定;SHA1PRNG + setSeed 可重现密钥;输出可被 OpenSSL 直接读取。
用 Java 对真实站点(github.com、wikipedia.org、example.com)做证书透明度验证:解析内嵌 SCT,重建预证书 TBS 并用日志公钥验证 5 个 SCT 的签名,向 Google Argon2026h2 日志取 STH 与审计路径验证 Merkle 包含证明(根与 STH 相同,条目逐字节相同);JDK 自己的 TLS 握手不检查 SCT。
在 JDK 21 上用本地 CA 与 openssl s_server 实测证书主机名验证:默认的 SSLSocket 对 15 种证书 / 主机名组合全部握手成功(不检查主机名),加上 HTTPS 端点标识或使用 HttpsURLConnection 只成功 9 种、HttpClient 8 种;SAN 优先于 CN、通配符只匹配一级、IP 必须用 IP 类型的 SAN;一行 setHostnameVerifier((h, s) -> true) 就让检查失效。
在 JDK 21 上实测安全审计日志:-Djavax.net.debug=ssl:record:plaintext 会把请求头与口令的明文写进日志;URL / URI / HttpRequest / Properties / char[] 的打印与 JDK 异常消息会带出口令;哈希链能发现篡改、删除与交换,但发现不了尾部截断与整条重算,HMAC 链加外部锚点才可以;JFR 的 TLS / X509 事件只含动作不含秘密。
在 Hibernate 6.6.4 + 真实 PostgreSQL 上实测持久化上下文:改托管实体的字段不用调用任何 save 就会在 commit 时发出(包含所有列的)UPDATE;同一 EntityManager 里同一主键是同一个对象;detach 后的修改不保存、merge 返回新的托管对象;AUTO 模式下查询前先 flush;托管实体 5 万个时无修改的 flush 也要 3.7 ms。
在 Hibernate 6.6.4 + 真实 PostgreSQL 上实测 JPA 实体生命周期:persist(游离对象)抛 EntityExistsException、对 removed 对象再 persist 回到 managed;@PostPersist 在 IDENTITY 下是 persist 时、在 SEQUENCE 下是 flush 时;@PostUpdate 里的修改要靠第二条 UPDATE 保存;@PreUpdate 里执行查询会递归 flush 478 次、发出 951 条 SQL;@PrePersist 抛异常后 commit 静默回滚。
在 Hibernate 6.6.4 + 真实 PostgreSQL 上实测 JPA 一级缓存:只有 find / getReference 命中缓存,所有 JPQL 都发 SQL,但返回的是缓存里的旧实体(另一个连接已改成 99 仍看到 30);只改 name 的提交会把别人写的 age 覆盖回旧值;读 10 万行不清理缓存堆内存 +82 MiB,每 1000 个 clear 或 StatelessSession 为 +0。