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 moreCaused 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 moreCaused 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