【发布时间】:2021-01-11 12:17:43
【问题描述】:
我正在为 java 中的 REST API 创建一个 HTTPS 客户端,但有些问题我无法很好地解决
需要注意的几点:
-
服务器正在使用自签名证书(在某些环境中,客户可能会使用受信任的机构对其进行签名)
-
我已从服务器下载根证书并使用
keytool命令添加到truststore -
我将系统属性设置为
System.setProperty("javax.net.ssl.trustStore", path); -
我可以列出服务器证书以验证它是否正确导入并可供我的客户端使用,例如:
String filename = path_to_truststore; FileInputStream is = new FileInputStream(filename); KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); String password = "changeit"; keystore.load(is, password.toCharArray()); // This class retrieves the most-trusted CAs from the keystore PKIXParameters params = new PKIXParameters(keystore); // Get the set of trust anchors, which contain the most-trusted CA certificates Iterator it = params.getTrustAnchors().iterator(); while (it.hasNext()) { TrustAnchor ta = (TrustAnchor) it.next(); // Get certificate X509Certificate cert = ta.getTrustedCert(); if (cert.getIssuerDN().getName().contains(server_cert_CN)) { System.out.println(cert.toString()); } } -
服务器证书在替代主题名称中没有 IP 地址,因此我使用自定义
HostnameVerifier来缓解该问题,该问题运行良好
之后,我正在创建一个 URL 对象并获得如下连接:
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
但我总是遇到错误:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
我尝试使用我自己的信任库。我尝试从信任库创建自己的 SSLSocketFactory,但运气不佳。
我现在可以克服上述错误的唯一方法是使用信任所有证书TrustManager,我真的很想避免。
我还尝试使用属性从 SSL 等获取一些调试级别的日志记录:
System.setProperty("javax.net.debug", "ssl,handshake,trustmanager");
但除了上述异常之外,我没有得到任何输出。
欢迎任何帮助或想法。我正在使用 java 1.8
更新
我根据 Felix 的请求用于创建 SSLContext 的代码
private SSLContext getSSLContext(File certificatePath) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, IOException {
if (sslContext == null) {
InputStream inputStream = new FileInputStream(certificatePath);
X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(inputStream);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry(Integer.toString(1), certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManagerFactory.getTrustManagers(), null);
// Setting the context above with create TrustManager does not work.
// An accept all trust manager as below works.
// context.init(null, getSelfSignedTrustManager(), null);
sslContext = context;
}
return sslContext;
}
后来,我在创建任何连接之前设置了上下文,例如:
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
【问题讨论】:
-
你试过 -Djavax.net.debug=all 以防万一吗?
-
我记得当我们最近不得不在 Java 中使用 SSL 套接字和自签名证书时,我还必须处理一些类似的异常。也许,https://github.com/felix-seifert/network-programming/tree/main/task-4-encrypted-sockets-ssl-socket 上的源代码为您提供了一些解决问题的想法。
-
@FelixSeifert 这似乎只是服务器端。我只是为我无法影响的现有 REST 接口编写客户端。
-
这就是我链接这个项目的原因:除了客户端之外,它还模拟了一个客户端。看看这个文件https://github.com/felix-seifert/network-programming/blob/main/task-4-encrypted-sockets-ssl-socket/src/main/java/ConnectionSimulation.java。
-
@FelixSeifert 知道了,试过了,但仍然出现同样的错误。证书加载正确,但仍然报错
标签: java ssl java-8 ssl-certificate truststore