Java 从与HttpClient 4.2的连接尝试失败中获取服务器证书
我目前正在使用ApacheHttpClient(特别是4.2.3版)向我的应用程序添加https下载功能 如果证书验证失败,我想获得服务器证书链。引发的异常Java 从与HttpClient 4.2的连接尝试失败中获取服务器证书,java,ssl,certificate,httpclient,apache-httpclient-4.x,Java,Ssl,Certificate,Httpclient,Apache Httpclient 4.x,我目前正在使用ApacheHttpClient(特别是4.2.3版)向我的应用程序添加https下载功能 如果证书验证失败,我想获得服务器证书链。引发的异常SSLPeerUnverifiedException没有提供任何信息的字段 try { HttpResponse response = client.execute(get); } catch (SSLPeerUnverifiedException e) { // retrieve server certificate her
SSLPeerUnverifiedException
没有提供任何信息的字段
try {
HttpResponse response = client.execute(get);
} catch (SSLPeerUnverifiedException e) {
// retrieve server certificate here
}
有一种方法是将
TrustManager
(捕获证书)注入SSLContext
,并为每个请求重新创建SSLContext
、SSLSocketFactory
和HttpClient
。但是,我希望能够对多个可能的并行请求重用这些实例。我在本例中使用了HC 4.3,但应该与HC 4.2完全相同,尽管我建议升级
public static void main(final String[] args) throws Exception {
TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmfactory.init((KeyStore) null);
TrustManager[] tms = tmfactory.getTrustManagers();
if (tms != null) {
for (int i = 0; i < tms.length; i++) {
final TrustManager tm = tms[i];
if (tm instanceof X509TrustManager) {
tms[i] = new TrustManagerDelegate((X509TrustManager) tm);
}
}
}
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tms, null);
CloseableHttpClient httpClient = HttpClients.custom()
.setSslcontext(sslContext)
.build();
try {
CloseableHttpResponse response = httpClient.execute(new HttpGet("https://google.com/"));
try {
// do something usefull
} finally {
response.close();
}
} catch (SSLException ex) {
Throwable cause = ex.getCause();
if (cause instanceof MyCertificateException) {
X509Certificate[] chain = ((MyCertificateException) cause).getChain();
for (X509Certificate cert: chain) {
System.out.println(cert);
}
}
}
}
static class TrustManagerDelegate implements X509TrustManager {
private final X509TrustManager trustManager;
TrustManagerDelegate(final X509TrustManager trustManager) {
super();
this.trustManager = trustManager;
}
@Override
public void checkClientTrusted(
final X509Certificate[] chain, final String authType) throws CertificateException {
this.trustManager.checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(
final X509Certificate[] chain, final String authType) throws CertificateException {
try {
this.trustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ex) {
throw new MyCertificateException(chain, ex);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return this.trustManager.getAcceptedIssuers();
}
}
static class MyCertificateException extends CertificateException {
private final X509Certificate[] chain;
MyCertificateException(final X509Certificate[] chain, final CertificateException ex) {
super(ex);
this.chain = chain;
}
public X509Certificate[] getChain() {
return chain;
}
}
publicstaticvoidmain(最终字符串[]args)引发异常{
TrustManagerFactory tmfactory=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmfactory.init((密钥库)null);
TrustManager[]tms=tmfactory.getTrustManager();
如果(tms!=null){
对于(int i=0;i
您随后通过anSSLSocket描述的方式是我所知道的唯一方式。如果HttpClient
以某种方式提供了这种能力,我并不知道。基本上,我创建了一个虚拟的TrustManager
,它将证书链保留在checkServerTrusted
中,然后如果握手抛出证书验证错误,它将处理获得的证书链。如果您愿意,我可以在答案中显示一些代码,但您似乎已经知道如何通过HttpClient实现这些代码。谢谢,我已经用这种方式实现了。我只是想知道这是否可以通过重用HttpClient或者至少是SSLContext、SSLSocketFactory和TrustManager来实现。这样做的原因是为了让连接重用/缓存等功能在httpclient上发挥作用。哇,我应该自己想出来的;)谢谢@olegJust,这是对这个的补充。这是依赖于平台的行为。正确设置原因取决于内部SSL实现。例如,它在Android 4.4上不起作用。您可能想说这种行为依赖于JSSE提供商,而不是依赖于平台。此外,Androids提供商没有传播根本原因听起来也是一个缺陷,而不是“依赖平台的行为”。无论如何,证书链都可以通过使用自定义连接工厂和HttpContext传播到调用方