您报告的错误表明您的应用程序无法与远程对等方建立受信任的 SSL 连接,因为它无法找到有效的证书路径。
我觉得很奇怪的是为什么它在几天前有效,而现在却无效:可能是服务器更改了证书,或者您的设置以某种方式发生了变化。
SSL 配置将高度依赖于您用于连接远程服务器的软件:如果您使用标准 Java 类(如 URLConnection 或 HttpURLConnection)或 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 使用以下过程尝试按指定顺序查找信任材料:
- 首先,您可以在运行应用程序时使用
javax.net.ssl.trustStore 系统属性指向包含您受信任的服务器证书的keystone。如果还定义了javax.net.ssl.trustStorePassword 系统属性,则其值用于在打开信任库之前检查信任库中数据的完整性。
- 如果未指定
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 提供了有关该工具的更多信息。