共计 6847 个字符,预计需要花费 18 分钟才能阅读完成。
tcp 三次握手后,client 开始和 server 进行 SSL 连贯,默认 client 用 jdk 的证书库对服务器证书进行认证,如果证书非法抛异样。如下示例代码
public static void main(String[] args) throws Exception {HttpClientBuilder builder = HttpClients.custom();
SSLContext sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {return false;}
}).build();
// builder.setSSLContext(sslContext);
CloseableHttpClient client = builder.build();
HttpGet get = new HttpGet("https://expired.badssl.com/");
// HttpGet get = new HttpGet("https://baidu.com");
try (CloseableHttpResponse resp = client.execute(get)) {System.out.println(resp.getStatusLine());
}
}
抛出证书异样,异样堆栈如下
Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: timestamp check failed
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1509)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:436)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:384)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
at com.learn.http.HttpClientTest.main(HttpClientTest.java:39)
Caused by: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: timestamp check failed
at sun.security.validator.PKIXValidator.doValidate(PKIXValidator.java:352)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:249)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1491)
... 20 more
Caused by: java.security.cert.CertPathValidatorException: timestamp check failed
at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:135)
at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:219)
at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:140)
at sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:79)
at java.security.cert.CertPathValidator.validate(CertPathValidator.java:292)
at sun.security.validator.PKIXValidator.doValidate(PKIXValidator.java:347)
... 26 more
Caused by: java.security.cert.CertificateExpiredException: NotAfter: Mon Apr 13 07:59:59 CST 2015
at sun.security.x509.CertificateValidity.valid(CertificateValidity.java:274)
at sun.security.x509.X509CertImpl.checkValidity(X509CertImpl.java:629)
at sun.security.provider.certpath.BasicChecker.verifyTimestamp(BasicChecker.java:190)
at sun.security.provider.certpath.BasicChecker.check(BasicChecker.java:144)
at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:125)
... 31 more
证书验证相干类
类有两个局部组成,一个是 appache 的 httpclient,还有一个是 jsse.jar 包,httpclient 次要利用 jsse.jar 来实现 SSL 通信,浅蓝色背景是类是 jsse 的类,其余是 httpclient 包的类
从最开始的堆栈信息能够看出,httpclient 的 ssl handshake 过程最终委托给 SSLSocketImpl 的 ClientHandshaker,服务器证书校验最终在
private void serverCertificate(HandshakeMessage.CertificateMsg var1) throws IOException {
// 从 sslContent 获取 trustManager, 而 sslContent 的 trustMananger 在创立 httpclient 能够定制,从而管制证书校验过程
X509TrustManager var6 = this.sslContext.getX509TrustManager();
try {if (this.conn != null) {
// 进入这个逻辑
((X509ExtendedTrustManager)var6).checkServerTrusted((X509Certificate[])var2.clone(), var4, this.conn);
} else {((X509ExtendedTrustManager)var6).checkServerTrusted((X509Certificate[])var2.clone(), var4, this.engine);
}
} catch (CertificateException var5) {this.fatalSE((byte)46, var5);
}
}
TrustStrategy 的应用
应用 TrustStrategy 能够设置疏忽证书校验,实现形式是通过委托包装模式,先执行 TrustStrategy 用户的定制逻辑,如果返回 false,持续进行默认证书校验;如果返回 true,则认为证书通过验证,当然也能够抛异样,证书认证失败。
SSLSocketImpl 的 ClientHandshaker 最终调用到 AbstractTrustManagerWrapper 的 public void checkClientTrusted(X509Certificate[] var1, String var2, Socket var3) 进行认证
用户定制 TrustStrategy 包装过程如下
// SSLContextBuilder.java
public SSLContextBuilder loadTrustMaterial(KeyStore truststore, TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
...
TrustManager[] tms = tmfactory.getTrustManagers();
if (tms != null) {if (trustStrategy != null) {for(int i = 0; i < tms.length; ++i) {TrustManager tm = tms[i];
if (tm instanceof X509TrustManager) {
// 应用 delegate 委托
tms[i] = new TrustManagerDelegate((X509TrustManager)tm, trustStrategy);
}
}
}
...
}
// SSLContextImpl.java
private X509TrustManager chooseTrustManager(TrustManager[] var1) throws KeyManagementException {for(int var2 = 0; var1 != null && var2 < var1.length; ++var2) {
...
return new AbstractTrustManagerWrapper((X509TrustManager)var1[var2]);
}
}
// AbstractTrustManagerWrapper
public void checkClientTrusted(X509Certificate[] var1, String var2, Socket var3) throws CertificateException {this.tm.checkClientTrusted(var1, var2);
this.checkAdditionalTrust(var1, var2, var3, true);
}
// TrustManagerDelegate.java
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {// 先执行用户的定制逻辑 trustStrategy.isTrusted(chain, authType)
if (!this.trustStrategy.isTrusted(chain, authType)) {this.trustManager.checkServerTrusted(chain, authType);
}
}
jsse 源码
SSLSocketImpl
正文完
发表至: httpclient
2022-07-03