JEP 538:密码学对象的 PEM 编码(第三次预览)
引言
有些格式如此普及,以至于我们几乎忘了它需要"被支持"。PEM 就是这样一种格式——每一个用过证书、SSH 或 HTTPS 的开发者都见过它,可 Java 的标准库偏偏一直没有正式支持读写它。于是无数项目各自造轮子。JEP 538 终于把这块拼图补进了标准库。
PEM:大家其实都在用的格式
如果你用过 HTTPS、SSH,或者任何一种证书,那你早就接触过 PEM 了——就是那些以 -----BEGIN----- 和 -----END----- 包裹起来的文本块。
- 它的结构很简单:Base64 编码的内容,外面包一层 BEGIN/END 标记。
- 它是密钥、证书和 CRL(证书吊销列表)落到磁盘上的默认格式。
- 几乎所有安全工具都认它:OpenSSL、Kubernetes 的 Secret、各家云厂商的密钥服务……PEM 是这个生态里事实上的通用语。
- 可讽刺的是,**JDK 一直没有一个官方支持的、用来读写 PEM 的 API。**一个如此普遍的格式,Java 标准库却始终把它当作"别人的事"。
没有它的时候大家怎么做
没有标准 API,结果就是每个项目都在重新发明轮子——而且发明得还未必对。
典型的手工做法是:
- 手工去掉首尾的 BEGIN/END 行,再对中间的内容做 Base64 解码。
- 靠猜来判断解出来的 DER 数据到底是什么——是 PKCS#8 私钥?X.509 证书?还是别的什么?
- 或者干脆仅仅为了解析几行文本,就引入 Bouncy Castle 这样的重量级第三方库。
- 而这些临时拼凑的代码,特别容易出那种微妙、且不抛异常的错误——它跑起来不报错,但行为悄悄错了,等你发现时可能已经酿成了安全问题。
这些散落在无数代码库里的临时代码,既是重复劳动,也是隐患的温床。
这个 JEP 提供了什么
JEP 538 在标准库里提供了一对干净的 API:PEMEncoder(负责写)和 PEMDecoder(负责读)。
- 标准库中内置
PEMEncoder与PEMDecoder。 - **可以直接解码成公钥、私钥、证书、CRL,或者通用的 PEM 对象。**你告诉解码器你期望的类型,它就返回对应的 Java 对象。
- **也能把实现了
BinaryEncodable的对象,编码回 PEM 文本。**文本与对象之间可以干净地双向转换。 - 加密的私钥也能处理——可以通过
PEMEncoder.of().withEncryption(password)配置编码器。
整个过程是类型安全的、简洁的,而且完全不需要你了解 ASN.1 的内部细节。
代码示例
// Decode a PEM file to a PrivateKey
PEMDecoder decoder = PEMDecoder.of();
PrivateKey key = decoder.decode(
Files.readString(Path.of("key.pem")),
PrivateKey.class);
// Encode a certificate back to PEM
PEMEncoder encoder = PEMEncoder.of();
String pem = encoder.encodeToString(certificate);
读取一个 PEM 文件、拿到一个 PrivateKey 对象——**三行代码。**反过来,把一个证书编码回 PEM 字符串——**两行。**把它和前面那套"手工去行 + Base64 + 猜算法 + 引依赖"的老代码对比一下,差距一目了然。
前后对比
此前:
- 手工做字符串处理,再加 Base64 解码。
- 每种密钥算法都要选一次
KeyFactory,而且全靠猜——猜错了就出问题。 - 为了一个 JDK 本就该理解的格式,额外引入一个第三方依赖。
此后:
- 一次解码调用,就返回正确的对象。
- 文本与对象之间可以干净地来回转换。
- 信任边界(trust boundary)内不再需要第三方库。
这最后一点尤其值得强调:安全相关的第三方库,本身就是你信任边界里的一环。**少一个第三方依赖,就少一份供应链风险。**JEP 538 让你能用标准库完成这件事,把一个外部依赖从密码学的核心路径上移除了。
预览状态
这已经是第三次预览,说明 API 正在根据社区反馈持续打磨。
- 它在 JDK 27 中作为第三次预览发布,是根据早期反馈改进而来的。
- 使用时需要
--enable-preview标志。 - 它属于 JDK 安全 API 现代化的一部分——和其他一系列安全相关的改进一道,把 Java 的密码学开发体验,带到符合当代需求的水准。
结论
PEM 是几乎每一套部署都在用的格式,可 Java 开发者多年来一直在用各种临时方案来对付它——手工解析、算法靠猜、引入重型依赖。JEP 538 用一套标准库 API 补上了这个长期存在的缺口,取代了那些容易出错的手工代码和不必要的第三方依赖。一个大家早就在用的格式,终于在 Java 里得到了官方支持。