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

我有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中执行的代码。


当前回答

如果“他们”使用自签名证书,则由他们采取必要的步骤使他们的服务器可用。具体来说,这意味着以一种值得信赖的方式离线向您提供他们的证书。所以让他们这么做。然后,按照JSSE参考指南中的描述,使用keytool将其导入到您的信任库中。不要想这里发布的不安全的TrustManager。

EDIT For the benefit of the seventeen (!) downvoters, and numerous commenters below, who clearly have not actually read what I have written here, this is not a jeremiad against self-signed certificates. There is nothing wrong with self-signed certificates when implemented correctly. But, the correct way to implement them is to have the certificate delivered securely via an offline process, rather than via the unauthenticated channel they are going to be used to authenticate. Surely this is obvious? It is certainly obvious to every security-aware organization I have ever worked for, from banks with thousands of branches to my own companies. The client-side code-base 'solution' of trusting all certificates, including self-signed certificates signed by absolutely anybody, or any arbitary body setting itself up as a CA, is ipso facto not secure. It is just playing at security. It is pointless. You are having a private, tamperproof, reply-proof, injection-proof conversation with ... somebody. Anybody. A man in the middle. An impersonator. Anybody. You may as well just use plaintext.

其他回答

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

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

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

我遇到了这个问题,因为依赖项来自maven repo,它是我的本地服务器,具有自签名证书和自签名CA证书。为了解决这个错误,我必须运行以下两个命令:

<my_java_install_dir>\bin\keytool.exe -importcert -file <my-self-signed-CA-cert>.crt -keystore <my_java_install_dir>\lib\security\cacerts -alias my-CA-cert

然后

<my_java_install_dir>\jdk11.0.14_10\bin\keytool.exe -importcert -file <my-self-signed-maven-repo-cert>.crt -keystore <my_java_install_dir>\lib\security\cacerts -alias my-maven-repo-cert

我找到了一个证书提供者,该提供者不属于JDK 8u74的默认JVM受信任主机。提供者是www.identrust.com,但这不是我试图连接的域。该域已从该提供者获得其证书。请参见交叉根覆盖信任JDK/JRE中的默认列表?——读几个条目。另请参阅哪些浏览器和操作系统支持Let 's Encrypt。

因此,为了连接到我感兴趣的域名,它有一个由identrust.com颁发的证书,我执行了以下步骤。基本上,我必须获得identrust.com (DST根CA X3)证书才能被JVM信任。我可以使用Apache HttpComponents 4.5这样做:

1:从证书链下载说明中的indettrust获取证书。单击DST根CA X3链接。

2:保存到“DST Root CA X3.pem”文件中。确保在文件的开头和结尾添加行“-----BEGIN CERTIFICATE-----”和“-----END CERTIFICATE-----”。

3:创建java的keystore文件cacerts。JKS,命令如下:

keytool -import -v -trustcacerts -alias IdenTrust -keypass yourpassword -file dst_root_ca_x3.pem -keystore cacerts.jks -storepass yourpassword

4:复制生成的cacerts。将JKS密钥存储库保存到java/(maven)应用程序的资源目录中。

5:使用以下代码加载该文件,并将其附加到Apache 4.5 HttpClient。这将解决所有拥有indetrust.com颁发的证书的域的问题,util oracle将证书包含到JRE默认的密钥存储库中。

SSLContext sslcontext = SSLContexts.custom()
        .loadTrustMaterial(new File(CalRestClient.class.getResource("/cacerts.jks").getFile()), "yourpasword".toCharArray(),
                new TrustSelfSignedStrategy())
        .build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
        sslcontext,
        new String[] { "TLSv1" },
        null,
        SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
        .setSSLSocketFactory(sslsf)
        .build();

当项目构建时,cacerts。JKS将被复制到类路径中并从那里加载。我没有,在这个时间点上,测试其他ssl站点,但如果上面的代码“链”在这个证书,那么他们也会工作,但是,我不知道。

参考:自定义SSL上下文和如何使用Java HttpsURLConnection接受自签名证书?

Kotlin中的变体

    @SuppressLint("CustomX509TrustManager", "TrustAllX509TrustManager")
    fun ignoreSsl() {
        val trustAllCerts: Array<TrustManager> = arrayOf(
            object : X509TrustManager {
                override fun getAcceptedIssuers(): Array<X509Certificate>? = null
                override fun checkClientTrusted(certs: Array<X509Certificate?>?, authType: String?) {}
                override fun checkServerTrusted(certs: Array<X509Certificate?>?, authType: String?) {}
            })
        val sc = SSLContext.getInstance("SSL")
        sc.init(null, trustAllCerts, SecureRandom())
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.socketFactory)
    }

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

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。