知其然,知其所以然
Java 内部原理 2026-10-05

ProcessHandle:观察和管理子进程

在 JDK 21 / macOS 上实测 ProcessHandle:info() 能读到命令与 CPU 时间但只有 454 / 742 个进程有 command;descendants() 比 children() 多孙进程,只杀父进程后 4 个后代全部存活并改挂到 pid 1;onExit() 对非子进程也有效,延迟约 3–13 ms;destroy() 返回 true 但忽略 SIGTERM 的进程仍然活着,destroyForcibly 得到退出码 137。

Java 进程 ProcessHandle destroy onExit descendants 真实验证 动画演示
Java 内部原理 2026-10-05

ProcessBuilder:安全地启动外部命令

在 JDK 21 / macOS 上实测 ProcessBuilder:拼接 sh -c 会执行注入的命令而数组参数不会;Runtime.exec(String) 不识别引号;不读输出时子进程写到 70000 字节就阻塞(管道约 64 KiB),stderr 同样;pb.start() 用 Java 进程的 PATH 查找可执行文件而 sh -c 用子进程的 PATH;启动失败的错误码 2 / 13 / 7。

Java 进程 ProcessBuilder 命令注入 管道 环境变量 真实验证 动画演示
Java 内部原理 2026-10-05

System.Logger:模块化环境中的轻量日志接口

在 JDK 21 上实测 System.Logger:默认后端是 java.util.logging(TRACE / DEBUG 不输出,ERROR 显示为 SEVERE);Supplier 在级别禁用时不求值而 expensive() 直接调用求值 1000 次;占位符是 MessageFormat 的 {0} 而不是 %s;禁用时字符串拼接 10.8 ns 而 Supplier 约 2 ns;自定义 LoggerFinder 能收到 JDK 的 Runtime.exit 日志。

Java 日志 System.Logger java.util.logging LoggerFinder 模块 真实验证 动画演示
Java 内部原理 2026-10-05

Console:交互式终端中的安全输入

用伪终端在 JDK 17 / 21 / 27 上实测 Console:管道里 System.console() 为 null,伪终端里非 null(JDK 27 多了 isTerminal());readPassword 不回显并返回可清零的 char[],而 Scanner 与 readLine 会回显;Ctrl-D 让 readPassword 返回 null;LC_ALL=C 时 Console.charset() 是 US-ASCII 而 defaultCharset() 仍是 UTF-8。

Java Console readPassword 终端 密码输入 真实验证 动画演示
Java 内部原理 2026-10-05

java.util.prefs:轻量偏好设置的生命周期

在 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 无权限写入。

Java java.util.prefs Preferences 偏好设置 持久化 真实验证 动画演示
Java 内部原理 2026-10-05

EdDSA:现代椭圆曲线签名

在 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 倍。

Java 密码学 EdDSA Ed25519 签名 OpenSSL 真实验证 动画演示
Java 内部原理 2026-10-05

XDH:基于椭圆曲线的密钥交换

在 JDK 21 上实测 X25519:RFC 7748 的测试向量公钥与共享密钥逐字节相同;getU() 是大端 BigInteger 而编码是小端;私钥有 5 位被 clamp 而不起作用;小阶点公钥一律抛 InvalidKeyException;与 OpenSSL 互通;协商 97 µs,比 ECDH P-256(552 µs)快约 5.7 倍。

Java 密码学 XDH X25519 密钥交换 ECDH 真实验证 动画演示
Java 内部原理 2026-10-05

RSASSA-PSS:带随机盐的 RSA 签名

在 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)。

Java 密码学 RSASSA-PSS RSA 签名 盐 OpenSSL 真实验证 动画演示
Java 内部原理 2026-10-05

CipherInputStream:加密流的结束和认证边界

在 JDK 21 上实测 CipherInputStream:GCM 被篡改或截断时一个明文字节都不交出并抛 IOException ← AEADBadTagException;CBC 无认证,中间篡改完全不被发现,NoPadding 在块边界截断悄悄少 16 字节;close() 不做验证;CipherOutputStream 忘记 close 得到 992 / 1016 字节的残缺文件;-Xmx128m 下 GCM 解密 40 MiB 起 OutOfMemoryError。

Java 密码学 CipherInputStream AES-GCM 认证加密 真实验证 动画演示
Java 内部原理 2026-10-05

KeyPairGenerator:生成公钥和私钥

在 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 密码学 KeyPairGenerator RSA EC SecureRandom 密钥生成 真实验证 动画演示
Java 内部原理 2026-10-05

证书透明度:如何验证证书发布记录

用 Java 对真实站点(github.com、wikipedia.org、example.com)做证书透明度验证:解析内嵌 SCT,重建预证书 TBS 并用日志公钥验证 5 个 SCT 的签名,向 Google Argon2026h2 日志取 STH 与审计路径验证 Merkle 包含证明(根与 STH 相同,条目逐字节相同);JDK 自己的 TLS 握手不检查 SCT。

Java 证书透明度 CT SCT X.509 Merkle树 真实验证 动画演示
Java 内部原理 2026-10-05

证书主机名验证:公钥可信不等于目标可信

在 JDK 21 上用本地 CA 与 openssl s_server 实测证书主机名验证:默认的 SSLSocket 对 15 种证书 / 主机名组合全部握手成功(不检查主机名),加上 HTTPS 端点标识或使用 HttpsURLConnection 只成功 9 种、HttpClient 8 种;SAN 优先于 CN、通配符只匹配一级、IP 必须用 IP 类型的 SAN;一行 setHostnameVerifier((h, s) -> true) 就让检查失效。

Java TLS 主机名验证 证书 SAN HttpsURLConnection HttpClient 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号