使用 HttpClient 4.2 从失败的连接尝试中获取服务器证书
Get server certificate from failed connection attempt with HttpClient 4.2
我目前正在使用 Apache HttpClient(特别是版本 4.2.3)向我的应用程序添加 https 下载功能。
如果证书验证失败,我想获取服务器证书链。抛出的异常 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;
}
}
我目前正在使用 Apache HttpClient(特别是版本 4.2.3)向我的应用程序添加 https 下载功能。
如果证书验证失败,我想获取服务器证书链。抛出的异常 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;
}
}