这看起来是一个标准问题,但我在任何地方都找不到明确的方向。

我有java代码试图连接到一个可能自签名(或过期)证书的服务器。代码报告以下错误:

[HttpMethodDirector] I/O exception (javax.net.ssl.SSLHandshakeException) caught 
when processing request: sun.security.validator.ValidatorException: PKIX path 
building failed: sun.security.provider.certpath.SunCertPathBuilderException: 
unable to find valid certification path to requested target

根据我的理解,我必须使用keytool并告诉java允许此连接是OK的。

解决此问题的所有说明都假设我完全熟练使用keytool,例如

为服务器生成私有密钥并将其导入密钥存储库

有人能给我详细说明吗?

我正在运行unix,所以bash脚本将是最好的。

不确定这是否重要,但在jboss中执行的代码。


当前回答

使用浏览器从目标页面下载自签名证书,并使用默认密码将其添加到默认存储区:

keytool -import -v -trustcacerts -file selfsigned.crt -alias myserver -keystore /etc/alternatives/jre/lib/security/cacerts -storepass changeit

使用$JAVA_HOME/jre/lib/security/cacerts文件,我这里的例子来自Oracle linux 7.7。

其他回答

接受的答案很好,但我想添加一些东西,因为我在Mac上使用IntelliJ,无法使用JAVA_HOME路径变量让它工作。

事实证明,从IntelliJ运行应用程序时,Java Home是不同的。

要找出它的确切位置,只需执行System.getProperty("java.home"),因为可信证书就是从那里读取的。

这不是解决全部问题的解决方案,但是oracle有关于如何使用这个keytool的详细文档。这解释了如何

使用keytool。 使用keytool生成证书/自签名证书。 将生成的证书导入Java客户端。

https://docs.oracle.com/cd/E54932_01/doc.705/e54936/cssg_create_ssl_cert.htm#CSVSG178

信任所有SSL证书:— 如果希望在测试服务器上进行测试,可以绕过SSL。 但是不要将此代码用于生产。

public static class NukeSSLCerts {
protected static final String TAG = "NukeSSLCerts";

public static void nuke() {
    try {
        TrustManager[] trustAllCerts = new TrustManager[] { 
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    X509Certificate[] myTrustedAnchors = new X509Certificate[0];  
                    return myTrustedAnchors;
                }

                @Override
                public void checkClientTrusted(X509Certificate[] certs, String authType) {}

                @Override
                public void checkServerTrusted(X509Certificate[] certs, String authType) {}
            }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        });
    } catch (Exception e) { 
    }
}

}

请在Activity或应用程序类的onCreate()函数中调用此函数。

NukeSSLCerts.nuke();

这可以用于Android的Volley。

有一个更好的替代信任所有证书的方法:创建一个TrustStore,它专门信任给定的证书,并使用它来创建一个SSLContext,从中获取SSLSocketFactory在HttpsURLConnection上进行设置。以下是完整的代码:

File crtFile = new File("server.crt");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));
// Or if the crt-file is packaged into a jar file:
// CertificateFactory.getInstance("X.509").generateCertificate(this.class.getClassLoader().getResourceAsStream("server.crt"));


KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("server", certificate);

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());

您也可以直接从文件加载KeyStore,或者从任何受信任的来源检索X.509证书。

注意,对于这段代码,将不使用cacerts中的证书。这个特定的HttpsURLConnection将只信任这个特定的证书。

您可以通过在RestTemplate级别禁用它来实现这一点。 注意,此TrustStrategy将信任所有证书,并且使用NoopHostnameVerifier()禁用了主机名验证。

public RestTemplate getRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
    SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
    return new RestTemplate(requestFactory);
}