【问题标题】:sun.security.validator.ValidatorException: SunCertPathBuilderException -while importing certificatesun.security.validator.ValidatorException: SunCertPathBuilderException - 导入证书时
【发布时间】:2021-06-26 07:28:44
【问题描述】:

我遇到了异常

sun.security.validator.ValidatorException:PKIX 路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException:无法找到到请求目标的有效证书路径

我已在该位置设置 SSL 证书

C:\Program Files\AdoptOpenJDK\jdk-11.0.9.11-hotspot\lib\security

keytool -import -keystore cacerts -file C:\Users\test\Desktop\Certificate\oCertificate.cer

但是我在访问服务器时遇到了上述异常。

我看到的结果 我已将证书添加到 Jdk cacerts 文件中,但它工作了两天,然后我又遇到了同样的错误。我无法让它工作我能够成功地 ping 服务器而不是再次显示异常。

【问题讨论】:

    标签: java https ssl-certificate


    【解决方案1】:

    您描述的问题是运行 keytool 来导入证书会给您这个错误吗?请提供选项-trustcacerts 并查看相关文档:

    导入新的可信证书

    在将证书添加到密钥库之前,keytool 命令 通过尝试从中构建信任链来验证它 证书到自签名证书(属于根 CA), 使用密钥库中已有的可信证书。

    如果指定了 -trustcacerts 选项,则附加 证书被考虑用于信任链,即 cacerts 文件中的证书。

    如果 keytool 命令无法从 要导入的证书最多为自签名证书( 从密钥库或 cacerts 文件),然后是证书 打印信息,并提示用户通过以下方式进行验证 将显示的证书指纹与指纹进行比较 从其他(可信的)信息来源获得,这些信息可能 成为证书所有者。非常小心,以确保证书是 在将其作为可信证书导入之前有效。然后用户有 停止导入操作的选项。如果 -noprompt 选项 指定,则不与用户交互。

    来源:https://docs.oracle.com/en/java/javase/11/tools/keytool.html

    另外,您可能会发现 keytool 不是很人性化,您可能会喜欢其他软件,例如:https://keystore-explorer.org/downloads.html 更多。

    或者,如果问题是您的(TLS 客户端,甚至 TLS 服务器)软件存在一些证书问题,则可能是因为 jccampanero 已经建议服务器可能已切换到不同的证书,或者据我所知服务器实际上可能是负载均衡器后面的几个不同的服务器,它们可能并不都具有相同的证书。 (或者您可能安装了一些 Java 更新来替换默认的 cacerts 文件?)

    如果出现问题,我强烈建议阅读 JSSE 文档并使用 java 选项 -Djavax.net.debug=all 启用调试日志记录,或者可能比 all 少一点,例如 handshake,请参阅 Java 11 文档:

    https://docs.oracle.com/en/java/javase/11/security/java-secure-socket-extension-jsse-reference-guide.html#GUID-31B7E142-B874-46E9-8DD0-4E18EC0EB2CF

    这显示了您的应用程序使用的确切 TrustStore、服务器在握手期间提供的证书以及许多其他作为 TLS 握手一部分的协商内容。

    如果您希望完全控制您信任的颁发证书的人,您可以配置自己的信任库,而不是可以在 Java 安装之外使用的默认信任库,选项如下:

    java -Djavax.net.ssl.trustStore=samplecacerts \
         -Djavax.net.ssl.trustStorePassword=changeit \
         Application
    

    我相信研究此调试日志记录应该可以直接解决问题,如果不能解决问题,请向我们提供一些相关的日志记录。

    【讨论】:

      【解决方案2】:

      您报告的错误表明您的应用程序无法与远程对等方建立受信任的 SSL 连接,因为它无法找到有效的证书路径。

      我觉得很奇怪的是为什么它在几天前有效,而现在却无效:可能是服务器更改了证书,或者您的设置以某种方式发生了变化。

      SSL 配置将高度依赖于您用于连接远程服务器的软件:如果您使用标准 Java 类(如 URLConnectionHttpURLConnection)或 Apache HttpClient 或 OkHttp 等库,则可能会有所不同,等等。区别主要在于该软件是否在后台使用Java Secure Socket Extension (JSSE)

      假设您使用的是JSSE,为了成功配置您的信任关系,您需要正确配置一个TrustManager,更具体地说,一个X509TrustManager。来自docs

      TrustManager 的主要职责是确定是否应信任提供的身份验证凭据。

      基本上你可以通过两种方式配置这个X509TrustManager

      在手边,您可以create your own implementation。例如:

      // This KeyStore contains the different certificates for your server
      KeyStore keyStore = KeyStore.getInstance("JKS");
      keyStore.load(
        new FileInputStream("/path/to/serverpublic.keystore"),
        "yourserverpublickeystorepassword".toCharArray()
      );
      
      TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); // SunX509
      tmf.init(keyStore);
      TrustManager[] trustManagers = tmf.getTrustManagers();
      
      SSLContext sslContext = SSLContext.getInstance("TLS");
      // You can configure your client for mutual authentication 
      // and/or provide a SecureRandom also if you wish
      sslContext.init(null, trustManagers, null); 
      

      请考虑阅读此SO question 以获取完整示例。

      或者,另一方面,正如您所做的那样,您可以正确配置标准TrustManagerFactory

      如上述文档中所述,标准TrustManagerFactory 使用以下过程尝试按指定顺序查找信任材料:

      1. 首先,您可以在运行应用程序时使用javax.net.ssl.trustStore 系统属性指向包含您受信任的服务器证书的keystone。如果还定义了javax.net.ssl.trustStorePassword 系统属性,则其值用于在打开信任库之前检查信任库中数据的完整性。
      2. 如果未指定javax.net.ssl.trustStore 系统属性,则:
      • 如果文件java-home/lib/security/jssecacerts存在,则使用该文件;
      • 如果文件java-home/lib/security/cacerts存在,则使用该文件;
      • 如果这些文件都不存在,则 SSL 密码套件是匿名的,不执行任何身份验证,因此不需要信任库。

      无论使用何种机制,您都必须确保密钥库包含信任远程服务器所需的所有证书,不仅是 SSL 证书,还包括证书链中的所有证书。

      openssl 提供了一个有用的命令,允许您获取 SSL 连接中使用的所有证书:

      openssl s_client -showcerts -connect google.com:443
      

      当然,根据需要修改域。

      会输出不同的证书;请务必保存它们中的每一个,包括 —–BEGIN CERTIFICATE—–—–END CERTIFICATE—–,并将它们包含在您的密钥库中。

      This handy utility 也可能对此有所帮助:正如项目README, 中所指出的,它基本上会尝试连接到远程对等点并保存必要的证书信息。这个related article 提供了有关该工具的更多信息。

      【讨论】:

      • 我建议不要重新实现:“世界上最危险的代码:在非浏览器软件中验证 SSL 证书”,请参阅:cs.utexas.edu/~shmat/shmat_ccs12.pdf
      • 嗨@JohannesB。非常感谢您的反馈。请问,你能进一步解释你的意思吗?在我的回答中,我只是试图提供一些提示来帮助 OP 解决他/她的问题。其中一些与 JVM 及其提供的底层安全机制有关,正如您的回答中所建议的那样。我也提出了一个基于代码的解决方案,因为像 Apache HttpClient 这样的一些库必须处理它。当然,我同意你的观点,代码可以在很多方面进行改进,包括主机名验证。您也可以验证证书在当前日期是否有效
      • 通过检查他们的前后日期。您可能可以分析证书中提供的信息并查找 CRL 分发点或 OCSP 验证端点,您当然可以做很多事情。可能 JSSE 本身会执行一些检查,但当然可以进行一些改进。 OP 没有指明他/她正在使用的软件,因此不容易提供更准确的答案。无论如何,我同意你的看法,应始终特别注意避免可能的安全问题。
      • 我的意思是,在正常情况下,完全有可能在不编写任何 Java 代码的情况下解决问题,只需导入正确的证书(通常是自签名根,如果服务器不提供它们,还有中间证书)到 JSSE 信任库中。此处使用 JSSE,否则用户永远不会收到 JSSE 错误。默认的 Java 实现经过了很好的测试、记录和理解,但是如果您编写自己的自定义实现,则会为各种安全问题打开大门。
      • 我完全同意@JohannesB 的观点,在处理安全问题时,始终建议不要重新发明轮子,并尽可能使用标准配置。话虽如此,请注意,如答案中所示,JSSE 规范提供了有关如何在必要时实现自定义 TrustManagers 的指导,因此当然可以使用 API。无论如何,重要的是任何答案和建议的解决方案都有助于 OP 解决问题。
      【解决方案3】:

      正如其他答案所指出的那样,需要正确设置的事项列表很长并且根据您使用的 HTTP 客户端而有所不同,但假设您已遵循其他答案中提供的信息,这就是我要检查的内容:

      1. 确保将证书正确导入到 cacerts 文件中。这可能会被软件更新、组策略(如果您在 Windows 域中)、您意外更改 JVM 等覆盖。仔细检查正在使用的 JVM 的路径
      2. 导入证书并不总是足以确保正确设置信任。用于访问服务的主机名必须通过通用名称 (CN) 或主题备用名称 (SAN DNS) 条目与导入的证书相匹配。第一张图片显示了当前谷歌证书的通用名称。 ,第二张图片显示了 SAN: 这样做的结果是,您用于访问服务的任何主机名(例如 test.amazingapp.com)必须与 CN 或 SAN 字段中的条目之一匹配。如果您通过 IP 地址访问服务,则 IP 需要位于这些字段中的任何一个字段中。
      3. 最后,确保证书没有过期。

      如果您已经检查了所有这些但仍然无法正常工作,那么您可能需要使用系统属性 javax.net.debug 打开 SSL 日志记录,如 here on the Oracle JSSE Site 所述,使用 $ java -Djavax.net.debug=all 将提供所有信息关于握手的存在。这需要一些时间来解决,但答案会在那里。

      【讨论】:

      • 不同的主机名和过期的证书会产生不同的错误
      猜你喜欢
      • 2013-10-07
      • 1970-01-01
      • 2016-01-12
      • 1970-01-01
      • 2019-11-26
      • 2018-10-15
      • 1970-01-01
      • 2021-05-15
      • 1970-01-01
      相关资源
      最近更新 更多