找不到SSL证书的Java HttpClient错误,在代码中使用证书作为String?

我在尝试使用HttpClient来调用使用自签名证书的https站点时感到有点困惑。 我有下面的代码,这使我能够进行调用,但后来我收到错误,如javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found我已经从我下载了证书网页浏览器,并了解我可以将其导入密钥库,但我宁愿把它放入代码并以这种方式使用它,有没有办法做到这一点?

  HttpClient client = new HttpClient(); EasySSLProtocolSocketFactory easySSLProtocolSocketFactory = new EasySSLProtocolSocketFactory(); Protocol https = new Protocol("https", easySSLProtocolSocketFactory, 443); Protocol.registerProtocol("https", https); BufferedReader br = null; String responseString = ""; GetMethod method = new GetMethod(path); int returnCode = client.executeMethod(method); 

假设您的证书是PEM格式。 您可以将其嵌入代码中,并使用BouncyCastle的PEMReader将其转换为X509Certificate实例。 完成此操作后,在内存中创建一个KeyStore实例并将此X.509证书放入其中。 然后,使用该KeyStore作为信任存储实例化新的SSLContext ,并使您的HTTP客户端使用它。

这看起来像这样(没试过,记得关闭读者并捕捉exception……):

 PEMReader pemReader = new PEMReader(new StringReader("----- BEGIN ......"); X509Certificate cert = (X509Certificate) pemReader.readObject(); KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(null, null); ks.setCertificateEntry("some name", cert); TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, tmf.getTrustManagers(), null); 

然后,使用此SSLContext进行连接。 如果您使用的是版本4.x(或者如果您使用的是版本3.x,则可以使用Apache HttpClient的SSLSocketFactory )。 我建议现在使用Apache HttpClient 4.x.

在Alexander Chzhen和HttpClient 4.3的回答基础上,我首先创建了一个信任所有人的上下文:

 SSLContextBuilder sslctxb = new SSLContextBuilder(); sslctxb.loadTrustMaterial(KeyStore.getInstance(KeyStore.getDefaultType()), new TrustSelfSignedStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); SSLContext sslctx = sslctxb.build(); 

然后是客户端构建器:

 HttpClientBuilder hcb = HttpClients.custom(); 

我只设置了上下文。 我不使用setSSLSocketFactory,因为它会干扰下面的setHostnameVerifier :

 hcb.setSslcontext(sslctx); 

最后,我设置了一个validation所有的主机名validation程序:

 hcb.setHostnameVerifier(new X509HostnameVerifier() { @Override public void verify(String host, SSLSocket ssl) throws IOException { } @Override public void verify(String host, X509Certificate cert) throws SSLException { } @Override public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException { } @Override public boolean verify(String hostname, SSLSession session) { return true; } }); 

最后建立客户端:

 HttpClient c = hcb.build(); 

如果您只想接受此单个证书而不是所有自签名证书,则应下载证书并将pem文件存储在某处。

现在,您可以使用此代码加载pem文件,使用此证书创建新的信任库,并使用您的HttpClient信任库。

 //use java. ... .X509Certificate, not javax. ... .X509Certificate import java.security.cert.X509Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; @Test public void testSslCertificate() throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, KeyManagementException { X509Certificate cert; try (FileInputStream pemFileStream = new FileInputStream(newFile("your.pem"))) { CertificateFactory certFactory = CertificateFactory.getInstance("X509"); cert = (X509Certificate) certFactory.generateCertificate(pemFileStream); } //create truststore KeyStore trustStore = KeyStore.getInstance("JKS"); trustStore.load(null); //create an empty trustore //add certificate to truststore - you can use a simpler alias String alias = cert.getSubjectX500Principal().getName() + "[" + cert.getSubjectX500Principal().getName().hashCode() + "]"; trustStore.setCertificateEntry(alias, cert); //configure http client TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerFactory.getTrustManagers(), null); HttpClientBuilder httpClientBuilder = HttpClientBuilder. create().setSslcontext(sslContext); try (CloseableHttpClient httpClient = httpClientBuilder.build()) { HttpGet httpGetRequest = new HttpGet("https://yourServer"); try (CloseableHttpResponse httpResponse = httpClient.execute(httpGetRequest)) { Assert.assertEquals(200, httpResponse.getStatusLine().getStatusCode()); } } } 

这是如何使Apache HttpClient 4.3接受自签名证书:

 HttpClientBuilder cb = HttpClientBuilder.create(); SSLContextBuilder sslcb = new SSLContextBuilder(); sslcb.loadTrustMaterial(KeyStore.getInstance(KeyStore.getDefaultType()), new TrustSelfSignedStrategy()); cb.setSslcontext(sslcb.build()); HttpClient client = cb.build() 

现在要执行POST或GET请求,请使用标准的execute方法:

 HttpResponse response = client.execute(...); 

提醒:当您信任自签名证书时,您很容易受到攻击。

在许多情况下,证书锁定可能比硬编码特定证书更可取。

是的,您可以将证书硬编码到您的代码中,这将有效并且安全。 这是一种非常合理的方法。 但是,它确实有一些缺点。 一个陷阱是最终证书将过期,然后您的应用程序将停止工作。 此外,如果您想更改您的私钥,您将无法。

因此,在许多情况下,使用证书锁定更灵活,可能更可取。 请参阅此处以获取有关如何实现证书固定的参考。