在 Java 中运行时添加 CA





0/5 (0投票)
CompositeJKS 允许您在不替换系统 CA 列表的情况下,将自定义 Java KeyStore 加载到 SSL 上下文中。
许多公司托管自己的内部证书颁发机构 (CA)。这些服务颁发 X.509 证书,例如用于 HTTPS 连接。为了使 Web 浏览器和程序化客户端信任使用此类内部证书的服务器的连接,需要将适当的根证书导入到“受信任的根证书”列表中。
此列表的精确位置和格式取决于所使用的操作系统、编程语言和工具。例如,Internet Explorer 和 Google Chrome 在 Windows 上运行时使用 Windows 证书存储,而 Mozilla Firefox 无论运行在哪个操作系统上,都使用自己的私有证书存储。
在这里,我想说明在 Java 客户端中使用内部签名的 Web 服务时出现的特定挑战。与 Mozilla Firefox 一样,Java 使用自己的证书存储,而不是依赖于操作系统实现。在基于 Debian 的 Linux 发行版上,该文件通常位于 /etc/ssl/certs/java/cacerts。在 Windows 上,该文件可以在类似 C:\Program Files\Java\jre1.8.0_77\lib\security\cacerts 的路径中找到。
这些文件使用 Java KeyStore 文件格式,可以使用 keytool
命令行工具进行修改。要将您自己的 CA 添加到受信任根的列表中,您可以运行
keytool -import -trustcacerts -file yourca.pem -alias yourca
-keystore [Location of the certificate store as described above]
当提示输入密码时,请使用 Java 设置的默认密码:changeit
更改此密码既不需要也不建议,因为此特定的 Java KeyStore 文件仅包含 CA 的公钥,因此不存储需要保护的任何私有数据。
不幸的是,修改此全局 KeyStore 存在一些问题。在 Windows 上,安装新的 Java 版本将用默认值替换修改后的文件。在 Windows 和 Linux 上,此修改需要管理员权限。这些问题促使人们需要一种方法来应用特定于应用程序的修改,以修改受信任的 CA 列表。
Java 系统属性 javax.net.ssl.trustStore
可用于指定加载 cacerts 文件的替代路径。您可以创建默认文件的本地副本,并使用 keytool
对其进行修改。然后,您可以像这样启动应用程序
java -Djavax.net.ssl.trustStore=/path/to/my/cacerts -jar myapp.jar
如果相关应用程序是在 Tomcat 中运行的 Web 服务,您可以改为将此行添加到 /etc/default/tomcat8
JAVA_OPTS="${JAVA_OPTS} -Djavax.net.ssl.trustStore=/path/to/my/cacerts"
另一种可能性是在应用程序本身的启动代码中使用以下方式设置 Java 系统属性
System.setProperty("javax.net.ssl.trustStore", "/path/to/my/cacerts");
请注意,最后一种选项要求 KeyStore 是磁盘上的真实文件,而不是嵌入在 JAR 中的文件。
上述所有选项都有一个主要缺点:通过有效地分叉上游默认 cacerts 文件,您的应用程序将无法获得对“公共”CA 的任何未来更新。同样,特定于应用程序的 KeyStore 不会包含管理员对全局 KeyStore 所做的任何自定义设置。
我们开发了一个小型的 Java 库来帮助解决这个问题:CompositeJKS 允许您在不替换系统 CA 列表的情况下,将自定义 Java KeyStore 加载到 SSL 上下文中。系统和自定义 KeyStore 将合并到一个组合视图中。
要使用此库,请将以下内容添加到您的 Maven pom.xml
<dependency>
<groupId>com.oneandone</groupId>
<artifactId>composite-jks</artifactId>
<version>1.0</version>
</dependency>
然后,您可以使用如下调用
SslContextUtils.mergeWithSystem("/path/to/my/cacerts");
CompositeJKS 还支持加载嵌入在 JAR 中的 JKS 文件。将您的文件放在 src/main/resources/ 中,让 Maven 嵌入它,然后使用如下调用
SslContextUtils.mergeWithSystem(getClass().getClassLoader().getResourceAsStream("cacerts")));