Java未使用信任库



我正在用java为REST API创建一个HTTPS客户端,有些问题我无法以良好的方式解决

需要注意的几点:

  1. 服务器正在使用自签名证书(在某些环境中,客户可能会使用可信机构对其进行签名(

  2. 我已经从服务器下载了根证书,并使用keytool命令将其添加到truststore

  3. 我正在将系统属性设置为System.setProperty("javax.net.ssl.trustStore", path);

  4. 我可以列出服务器证书来验证它是否正确导入并可用于我的客户端,如:

    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());
    }
    }
    
  5. 服务器证书在其他使用者名称中没有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());

您需要将其作为受信任的服务器进行安装。如果它是由不受信任的CA签名的,您也必须安装CA的证书。

根据我评论中GitHub repo中显示的文件,我选择了相关部分并在下面显示。本例使用您为客户端创建并设置的SslContext

HttpClient client = HttpClient.newBuilder()      // Type java.net.http.HttpClient
.version(HttpClient.Version.HTTP_1_1)    // Change to needed HTTP version
.sslContext(getSSLContext(filePath))     // Function from above
.build();
String[] headerArray = {
"Accept", "text/html",
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 " + 
"Safari/537.36"};                // Extend header if needed
HttpRequest request = HttpRequest.newBuilder()   // Type java.net.http.HttpRequest
.uri(uri)                                // URI of type java.net.URI
.headers(headerArray)
.GET()
.build();
HttpResponse<String> response = null;            // Type java.net.http.HttpResponse
try {
response = client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (InterruptedException e) {
e.printStackTrace();
}

最新更新