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