KeychainStore 不向 Mac OS X 上的 Java 提供证书

Posted

技术标签:

【中文标题】KeychainStore 不向 Mac OS X 上的 Java 提供证书【英文标题】:KeychainStore doesn't provide certificates to Java on Mac OS X 【发布时间】:2020-09-20 16:21:57 【问题描述】:

在研究问题 related to Jib 时,我发现 java -Djavax.net.ssl.trustStoreType=KeychainStore 不会为我在 Mac OS X 上的 Java 进程提供证书,并且它无法验证任何公共网站(以及我的私人网站)的 TLS/SSL自签名证书)。

这是我如何测试它的示例:

$ cat <<EOF > A.java
public class A 
  public static void main(String[] args) throws Exception 
    new java.net.URL("https://google.com/").openStream().close();
  

EOF

结果如下:

$ java -Djavax.net.ssl.trustStoreType=KeychainStore A.java
Exception in thread "main" javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:326)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:269)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:645)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:464)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:360)
    at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:422)
    at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:183)
    at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
    at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1144)
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1055)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:395)
    at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
    at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1587)
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1515)
    at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:250)
    at java.base/java.net.URL.openStream(URL.java:1140)
    at A.main(A.java:3)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:439)
    at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:306)
    at java.base/sun.security.validator.Validator.validate(Validator.java:264)
    at java.base/sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:313)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:222)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:129)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:629)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:464)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:360)
    at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:422)
    at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:183)
    at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
    at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1144)
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1055)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:395)
    at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
    at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1587)
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1515)
    at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:250)
    at java.base/java.net.URL.openStream(URL.java:1140)
    at A.main(A.java:3)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at jdk.compiler/com.sun.tools.javac.launcher.Main.execute(Main.java:404)
    at jdk.compiler/com.sun.tools.javac.launcher.Main.run(Main.java:179)
    at jdk.compiler/com.sun.tools.javac.launcher.Main.main(Main.java:119)
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
    at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
    at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297)
    at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:434)
    ... 30 more

任何线索为什么这不起作用?我错过了什么吗?

PS。关于我的环境的一些信息(我也尝试过 Java 11):

$ echo $JAVA_HOME
/Library/Java/JavaVirtualMachines/adoptopenjdk-14.0.1.jdk/Contents/Home

$ java -version
openjdk version "14.0.1" 2020-04-14
OpenJDK Runtime Environment AdoptOpenJDK (build 14.0.1+7)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14.0.1+7, mixed mode, sharing)

【问题讨论】:

【参考方案1】:

我对其进行了测试,在您的情况下,以下命令应该可以工作: java -Djavax.net.ssl.trustStore=/Library/Java/JavaVirtualMachines/adoptopenjdk-14.0.1.jdk/Contents/Home/lib/security/cacerts A.java

您可以根据需要编辑 cacerts 文件。

更多信息,您可以查看:

How to set up Java VM to use the root certificates (truststore) handled by Mac OS X

How to use ssl certificates in Java on a Mac

【讨论】:

以上是关于KeychainStore 不向 Mac OS X 上的 Java 提供证书的主要内容,如果未能解决你的问题,请参考以下文章

如何在Mac OS X上安装 Ruby运行环境

Mac OS X 如何升级系统自带的 Ruby ?

NFS: Mac OS X (server) and Mac OS X (clients)实现思路

Mac OS X安装 Scrapy

如何在 Mac OS X 上装设 LaTeX 以及 CJK 套件

制作 Mac OS X El Capitan 的U盘引导安装