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 包的类

image.png

从最开始的堆栈信息可以看出,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,则认为证书通过验证,当然也可以抛异常,证书认证失败。

    // 定制TrustStrategy的sslContext来忽略证书校验
    private static SSLContext getIgnoreStrategy() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
        SSLContext sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

                return false;
            }
        }).build();
        return sslContext;
    }

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);
            }

        }

忽略证书校验之X509TrustManager

忽略证书校验还可以通过创建一个X509TrustManager空操作类,什么都不做,所有证书都通过。

    // 定制sslContext的TrustManager,忽略证书校验
    private static SSLContext getIgnoreTrustManager() throws Exception {
        SSLContext sc = SSLContext.getInstance("TLS");
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            }
            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                return;
            }
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
        sc.init(null, new TrustManager[] { trustManager }, null);
        return sc;
    }

jsse源码

SSLSocketImpl


ThinkFault
13 声望1 粉丝