在 Java 客户机中接受服务器的自签名 SSL 证书

2022-08-31 05:36:08

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

我有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允许这种连接是可以的。

解决此问题的所有说明都假定我完全精通keytool,例如

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

有人可以发布详细说明吗?

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

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


答案 1

这里基本上有两个选项:将自签名证书添加到 JVM 信任库或将客户机配置为

备选案文1

从浏览器中导出证书并将其导入 JVM 信任库(以建立信任链):

<JAVA_HOME>\bin\keytool -import -v -trustcacerts
-alias server-alias -file server.cer
-keystore cacerts.jks -keypass changeit
-storepass changeit 

备选案文2

禁用证书验证:

// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { 
    new X509TrustManager() {     
        public java.security.cert.X509Certificate[] getAcceptedIssuers() { 
            return new X509Certificate[0];
        } 
        public void checkClientTrusted( 
            java.security.cert.X509Certificate[] certs, String authType) {
            } 
        public void checkServerTrusted( 
            java.security.cert.X509Certificate[] certs, String authType) {
        }
    } 
}; 

// Install the all-trusting trust manager
try {
    SSLContext sc = SSLContext.getInstance("SSL"); 
    sc.init(null, trustAllCerts, new java.security.SecureRandom()); 
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (GeneralSecurityException e) {
} 
// Now you can access an https URL without having the certificate in the truststore
try { 
    URL url = new URL("https://hostname/index.html"); 
} catch (MalformedURLException e) {
} 

请注意,我根本不推荐选项#2。禁用信任管理器会击败 SSL 的某些部分,并使您容易受到中间人攻击。首选选项#1,或者更好的是,让服务器使用由知名CA签名的“真实”证书。


答案 2

除了信任所有证书之外,还有更好的替代方法:创建一个专门信任给定证书的证书,并使用它来创建一个从中获取 .以下是完整的代码:TrustStoreSSLContextSSLSocketFactoryHttpsURLConnection

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());

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

请注意,使用此代码时,将不会使用 中的证书。此特定将仅信任此特定证书。cacertsHttpsURLConnection