zno2

https 请求不通引发的思考(异常 SSLPeerUnverifiedException)

推荐书籍:

《HTTP权威指南》、《HTTPS权威指南》、《Bulletproof SSL and TLS》

需求是新旧域名切换

https://etc.scjtonline.cn/xxx(旧)
https://etc.its-sc.com.cn/xxx(新)

遇到的问题是切换后基于HttpClient请求不通,报异常如下

javax.net.ssl.SSLPeerUnverifiedException: Certificate for <etc.its-sc.com.cn> doesn't match any of the subject alternative names: [*.scjtonline.cn, scjtonline.cn]

 

解决思路先开启debug:

            org.apache.http.client.protocol.RequestAddCookies - CookieSpec selected: default
             org.apache.http.client.protocol.RequestAuthCache - Auth cache not set in the context
 org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection request: [route: {s}->https://etc.its-sc.com.cn:443][total available: 0; route allocated: 0 of 2; total allocated: 0 of 20]
 org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection leased: [id: 20][route: {s}->https://etc.its-sc.com.cn:443][total available: 0; route allocated: 1 of 2; total allocated: 1 of 20]
                org.apache.http.impl.execchain.MainClientExec - Opening connection {s}->https://etc.its-sc.com.cn:443
org.apache.http.impl.conn.DefaultHttpClientConnectionOperator - Connecting to etc.its-sc.com.cn/10.153.0.131:443
          org.apache.http.conn.ssl.SSLConnectionSocketFactory - Connecting socket to etc.its-sc.com.cn/10.153.0.131:443 with timeout 0
          org.apache.http.conn.ssl.SSLConnectionSocketFactory - Enabled protocols: [TLSv1, TLSv1.1, TLSv1.2]
          org.apache.http.conn.ssl.SSLConnectionSocketFactory - Enabled cipher suites:[TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, ..., TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, ...]
          org.apache.http.conn.ssl.SSLConnectionSocketFactory - Starting handshake
          org.apache.http.conn.ssl.SSLConnectionSocketFactory - Secure session established
          org.apache.http.conn.ssl.SSLConnectionSocketFactory - negotiated protocol: TLSv1.2
          org.apache.http.conn.ssl.SSLConnectionSocketFactory - negotiated cipher suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
          org.apache.http.conn.ssl.SSLConnectionSocketFactory - peer principal: CN=*.scjtonline.cn, O=四川智能交通系统管理有限责任公司, L=成都市, ST=四川省, C=CN
          org.apache.http.conn.ssl.SSLConnectionSocketFactory - peer alternative names: [*.scjtonline.cn, scjtonline.cn]
          org.apache.http.conn.ssl.SSLConnectionSocketFactory - issuer principal: CN=Xcc Trust OV SSL CA, O="Beijing Xinchacha Credit Management Co., Ltd.", C=CN
             org.apache.http.conn.ssl.DefaultHostnameVerifier - Certificate for <etc.its-sc.com.cn> doesn't match any of the subject alternative names: [*.scjtonline.cn, scjtonline.cn] 
 org.apache.http.impl.conn.DefaultManagedHttpClientConnection - http-outgoing-20: Shutdown connection
                org.apache.http.impl.execchain.MainClientExec - Connection discarded

 

hostname 是自己发起请求时url中解析的

session 是握手时创建的,证书是握手时服务端发送给client的

    @Override
    public boolean verify(final String host, final SSLSession session) {
        try {
            final Certificate[] certs = session.getPeerCertificates();
            final X509Certificate x509 = (X509Certificate) certs[0];
            verify(host, x509);
            return true;
        } catch (final SSLException ex) {
            if (log.isDebugEnabled()) {
                log.debug(ex.getMessage(), ex);
            }
            return false;
        }
    }

 

 sun.security.ssl.ClientHandshaker.processMessage(byte, int) 有个具体实现,代码如下:

    @Override
    void processMessage(byte type, int messageLen) throws IOException {
        switch (type) {
        case HandshakeMessage.ht_hello_request:
            this.serverHelloRequest(new HelloRequest(input));
            break;
        case HandshakeMessage.ht_server_hello:
            this.serverHello(new ServerHello(input, messageLen));
            break;
        case HandshakeMessage.ht_certificate:
            ...
            this.serverCertificate(new CertificateMsg(input));
            serverKey = session.getPeerCertificates()[0].getPublicKey();
            break;
        case HandshakeMessage.ht_server_key_exchange:
            serverKeyExchangeReceived = true;
            ...
            break;
        case HandshakeMessage.ht_certificate_request:
            ...
            break;
        case HandshakeMessage.ht_server_hello_done:
            this.serverHelloDone(new ServerHelloDone(input));
            break;
        case HandshakeMessage.ht_finished:
            ...
            this.serverFinished(new Finished(protocolVersion, input, cipherSuite));
            break;
        default:
            throw new SSLProtocolException("Illegal client handshake msg, " + type);
        }
        ...
    }

 

1. Client begins a new handshake and submits its capabilities to the server.

2. Server selects connection parameters.

3. Server sends its certificate chain (only if server authentication is required).

4. Depending on the selected key exchange, the server sends additional information required to generate the master secret.

5. Server indicates completion of its side of the negotiation.

6. Client sends additional information required to generate the master secret.

7. Client switches to encryption and informs the server.

8. Client sends a MAC of the handshake messages it sent and received.

9. Server switches to encryption and informs the client.

10. Server sends a MAC of the handshake messages it received and sent.

 

名词解释:

TLS (transport layer security)  安全传输层

Enabled protocols 可用协议版本

enabled cipher suites 可用密码套件列表

ecdhe_rsa Ephemeral ECDH key exchange with RSA authentication (RFC 4492) 

negotiated 协商的

peer principal  ?什么是peer ,初步看是被调用方

 

暂时解决方案是:

1) 构建http client 时指定SSLSocketFactory

2) factory 使用 org.apache.http.conn.ssl.NoopHostnameVerifier.INSTANCE 

 

org.apache.http.impl.client.HttpClientBuilder.setSSLSocketFactory(LayeredConnectionSocketFactory)

...

    /**
     * 如果某些场景需要忽略证书验证
     * @return
     */
    public static SSLConnectionSocketFactory createIgnoreVerifySSL() {
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            X509TrustManager trustManager = new X509TrustManager() {
                @Override
                public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                        String paramString) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                        String paramString) throws CertificateException {
                }

                @Override
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };

            sc.init(null, new TrustManager[] { trustManager }, null);
            SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sc, NoopHostnameVerifier.INSTANCE);
            return csf;
        } catch (Exception e) {
            return null;
        }
    }

 

追问:为什么访问新域名却在握手时返回的旧证书?

经过实际排查发现是业主使用了waf防火墙,里面用的cname(canonical name) (域名别名)

 

补充:

查看https的证书

 点击浏览器网址前面的锁 -> 连接是安全的->证书有效->详细信息->具体域名->导出

然后使用keytool 打印证书信息

keytool -printcert -file D://a.crt -v

 

posted on 2023-06-01 14:23  zno2  阅读(1511)  评论(0编辑  收藏  举报

导航