httpsUrlConnection 如何设置的默认sslcontext和 hostnameverifier?
Posted xiaoliuliu2050
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了httpsUrlConnection 如何设置的默认sslcontext和 hostnameverifier?相关的知识,希望对你有一定的参考价值。
https请求接口的时候java是什么时候默认添加ssl 上下文的? HttpURLConnection 子类HttpsURLConnection 子类HttpsURLConnectionImpl
1 url.openConnection())
2 https 的Handler 调用
protected URLConnection openConnection(URL var1) throws IOException
return this.openConnection(var1, (Proxy)null);
protected URLConnection openConnection(URL var1, Proxy var2) throws IOException
return new HttpsURLConnectionImpl(var1, var2, this);
3 HttpsURLConnectionImpl类实例化的时候会默认添加 sslcontext和hostnameverifier.并生成delegate对象
HttpsURLConnectionImpl(URL var1, Proxy var2, Handler var3) throws IOException
super(var1);
this.delegate = new DelegateHttpsURLConnection(this.url, var2, var3, this);
4 HttpsURLConnection的构造方法里
protected HttpsURLConnection(URL var1)
super(var1);
this.hostnameVerifier = defaultHostnameVerifier;
this.sslSocketFactory = getDefaultSSLSocketFactory();
HttpsURLConnection构造方法里 就会初始化默认的sslcontext 和hostnameverifier
默认default hostnameverifier校验是不通过的。
5 看一下getDefaultSSLSocketFactory 的源码如下:
public static SSLSocketFactory getDefaultSSLSocketFactory()
if (defaultSSLSocketFactory == null)
defaultSSLSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
return defaultSSLSocketFactory;
所以如果想要设置一个默认的sslsocketFactory 可以调用
HttpsURLConnection.defaultSSLSocketFactory=? 设置。或者调用 HttpsURLConnection.setDefaultSSLSocketFactory(?) 方法设置
如果没有设置,就会调用系统默认的SSLSocketFactory。
6 继续看一下SSLSocketFactory.getDefault方法的源码:
public static synchronized SocketFactory getDefault()
if (theFactory != null)
return theFactory;
else
if (!propertyChecked)
propertyChecked = true;
String var0 = getSecurityProperty("ssl.SocketFactory.provider");
if (var0 != null)
log("setting up default SSLSocketFactory");
try
Class var1 = null;
try
var1 = Class.forName(var0);
catch (ClassNotFoundException var5)
ClassLoader var3 = ClassLoader.getSystemClassLoader();
if (var3 != null)
var1 = var3.loadClass(var0);
log("class " + var0 + " is loaded");
SSLSocketFactory var2 = (SSLSocketFactory)var1.newInstance();
log("instantiated an instance of class " + var0);
theFactory = var2;
return var2;
catch (Exception var6)
log("SSLSocketFactory instantiation failed: " + var6.toString());
theFactory = new DefaultSSLSocketFactory(var6);
return theFactory;
try
return SSLContext.getDefault().getSocketFactory();
catch (NoSuchAlgorithmException var4)
return new DefaultSSLSocketFactory(var4);
如果theFactory 不为null,则直接返回theFactory,
所以我们可以通过
SSLSocketFactory.theFactory=? 设置 默认的 sslsocketFactory
下面如果propertyChecked=false, 则获取java.home/lib/security/
java.security 文件中的属性ssl.SocketFactory.provider,如果属性存在,则动态加载该类,并实例化为sslsocketFactory.
如果不存在,则获取默认sslsocketFactory,以下是获取默认sslcontext 的逻辑。
public static synchronized SSLContext getDefault() throws NoSuchAlgorithmException
if (defaultContext == null)
defaultContext = getInstance("Default");
return defaultContext;
public static SSLContext getInstance(String var0) throws NoSuchAlgorithmException
Instance var1 = GetInstance.getInstance("SSLContext", SSLContextSpi.class, var0);
return new SSLContext((SSLContextSpi)var1.impl, var1.provider, var0);
SSLContext 类包含了四个属性
private final Provider provider;//供应商
private final SSLContextSpi contextSpi;// sslcontext服务的某个供应商某种协议的实现
private final String protocol;//算法
private static SSLContext defaultContext;//sslcontext
sslcontext 类的主要作用就是 根据指定指定供应商 和算法 获取到一个SSLContext服务接口 Service 对象,
然后实例化这个服务接口 获取到一个SSLContextSpi 对象。
然后把SSLContextSpi 对象,供应商,算法 封装到SSLContext 里 构成了sslcontext。
sslcontext有一个方法是初始化 KeyManager和TrustManager
public final void init(KeyManager[] var1, TrustManager[] var2, SecureRandom var3) throws KeyManagementException
this.contextSpi.engineInit(var1, var2, var3);
这个方法的实现逻辑其实就是调用contextSpi服务的engineInit方法初始化而已。
SSLParameters 封装了ssl协议版本,加密套件等。
FIPS :FIPS是美国联邦信息处理标准(Federal Information Processing Standard)的缩写。
AbstractSSLContext
如果是fips ,则支持的协议版本为TLSv1,TLSv1.1,TLSv1.2
如果不是fips,则还要额外支持更多协议版本,比如SSLv2Hello,SSLv3,
默认使用协议版本为
CustomizedSSLContext
可以自己制定使用的协议 存到系统属性jdk.tls.client.protocols,如果没有指定的话,如果是fips ,则支持的协议版本为TLSv1,TLSv1.1,TLSv1.2 如果不是fips,则还要额外支持更多协议版本,比如SSLv3,
TLS12Context
如果是fips ,则支持的协议版本为TLSv1,TLSv1.1,TLSv1.2 如果不是fips,则还要额外支持更多协议版本,比如SSLv3,
TLS11Context
如果是fips ,则支持的协议版本为TLSv1,TLSv1.1 如果不是fips,则还要额外支持更多协议版本,比如SSLv3,
TLS10Context
如果是fips ,则支持的协议版本为TLSv1 如果不是fips,则还要额外支持更多协议版本,比如SSLv3,
TLSContext 和 CustomizedSSLContext一样
DefaultSSLContext
默认从系统属性javax.net.ssl.keyStore* 里读取keyManager 列表。
默认从系统属性javax.net.ssl.trustStore* 里读取trustManger列表。
return SSLContext.getDefault().getSocketFactory();
SSLContext.getDefault() 内部实现就是 根绝供应商和协议获取sslcontext 的实现类sslcontextSpi,然后使用调用实现类的getSocketFactory()方法, 这个方法内部的逻辑就是 构造一个SocketFactory对象 把sslcontextSpi 作为属性构造进去。
SSLSocketFactory 对象可以createSocket生成 SslSocket 实例。
以上的逻辑是openconnection 创建连接对象的过程,也就是初始化的过程。
httpsurlconnection.execute()方法执行以后的逻辑是什么?
截图看流程
httpsclient 的afterconnect 方法里会创建socket 然后开始握手
public void afterConnect() throws IOException, UnknownHostException
if (!this.isCachedConnection())
SSLSocket var1 = null;
SSLSocketFactory var2 = this.sslSocketFactory;
try
if (!(this.serverSocket instanceof SSLSocket))
var1 = (SSLSocket)var2.createSocket(this.serverSocket, this.host, this.port, true);
else
var1 = (SSLSocket)this.serverSocket;
if (var1 instanceof SSLSocketImpl)
((SSLSocketImpl)var1).setHost(this.host);
catch (IOException var11)
try
var1 = (SSLSocket)var2.createSocket(this.host, this.port);
catch (IOException var10)
throw var11;
String[] var3 = this.getProtocols();
String[] var4 = this.getCipherSuites();
if (var3 != null)
var1.setEnabledProtocols(var3);
if (var4 != null)
var1.setEnabledCipherSuites(var4);
var1.addHandshakeCompletedListener(this);
boolean var5 = true;
String var6 = var1.getSSLParameters().getEndpointIdentificationAlgorithm();
if (var6 != null && var6.length() != 0)
if (var6.equalsIgnoreCase("HTTPS"))
var5 = false;
else
boolean var7 = false;
if (this.hv != null)
String var8 = this.hv.getClass().getCanonicalName();
if (var8 != null && var8.equalsIgnoreCase("javax.net.ssl.HttpsURLConnection.DefaultHostnameVerifier"))
var7 = true;
else
var7 = true;
if (var7)
SSLParameters var12 = var1.getSSLParameters();
var12.setEndpointIdentificationAlgorithm("HTTPS");
var1.setSSLParameters(var12);
var5 = false;
var1.startHandshake();
this.session = var1.getSession();
this.serverSocket = var1;
try
this.serverOutput = new PrintStream(new BufferedOutputStream(this.serverSocket.getOutputStream()), false, encoding);
catch (UnsupportedEncodingException var9)
throw new InternalError(encoding + " encoding not found");
if (var5)
this.checkURLSpoofing(this.hv);
else
this.session = ((SSLSocket)this.serverSocket).getSession();
如上图 创建socket,添加握手完成监听器,开始握手。握手之后,如果发现不是默认的hostnameverifier ,又通过 this.checkURLSpoofing(this.hv); 这个方法实现了hostnameverifer 的自定义验证。
Httpsclient:
private void checkURLSpoofing(HostnameVerifier var1) throws IOException
String var2 = this.url.getHost();
if (var2 != null && var2.startsWith("[") && var2.endsWith("]"))
var2 = var2.substring(1, var2.length() - 1);
Certificate[] var3 = null;
String var4 = this.session.getCipherSuite();
try
HostnameChecker var5 = HostnameChecker.getInstance((byte)1);
if (var4.startsWith("TLS_KRB5"))
if (!HostnameChecker.match(var2, this.getPeerPrincipal()))
throw new SSLPeerUnverifiedException("Hostname checker failed for Kerberos");
else
var3 = this.session.getPeerCertificates();
if (!(var3[0] instanceof X509Certificate))
throw new SSLPeerUnverifiedException("");
X509Certificate var6 = (X509Certificate)var3[0];
var5.match(var2, var6);
return;
catch (SSLPeerUnverifiedException var7)
;
catch (CertificateException var8)
;
if (var4 == null || var4.indexOf("_anon_") == -1)
if (var1 == null || !var1.verify(var2, this.session))
this.serverSocket.close();
this.session.invalidate();
throw new IOException("HTTPS hostname wrong: should be <" + this.url.getHost() + ">");
其中 var1.verify(var2, this.session)这句就是调用的hostnameverifier 做校验了。
sslsocketImpl 类readRecord
private void readRecord(InputRecord var1, boolean var2) throws IOException
Object var4 = this.readLock;
synchronized(this.readLock)
while(true)
int var3;
if ((var3 = this.getConnectionState()) != 6 && var3 != 4 && var3 != 7)
try
var1.setAppDataValid(false);
var1.read(this.sockInput, this.sockOutput);
catch (SSLProtocolException var12)
SSLProtocolException var5 = var12;
try
this.fatal((byte)10, (Throwable)var5);
catch (IOException var11)
;
throw var12;
catch (EOFException var13)
boolean var6 = this.getConnectionState() <= 1;
boolean var7 = requireCloseNotify || var6;
if (debug != null && Debug.isOn("ssl"))
System.out.println(Thread.currentThread().getName() + ", received EOFException: " + (var7 ? "error" : "ignored"));
if (var7)
Object var8;
if (var6)
var8 = new SSLHandshakeException("Remote host closed connection during handshake");
else
var8 = new SSLProtocolException("Remote host closed connection incorrectly");
((SSLException)var8).initCause(var13);
throw var8;
this.closeInternal(false);
continue;
try
var1.decrypt(this.readAuthenticator, this.readCipher);
catch (BadPaddingException var15)
int var17 = var1.contentType() == 22 ? 40 : 20;
this.fatal((byte)var17, var15.getMessage(), var15);
synchronized(this)
switch(var1.contentType())
case 20:
if ((this.connectionState == 1 || this.connectionState == 3) && this.handshaker.sessionKeysCalculated() && !this.receivedCCS)
if (var1.available() != 1 || var1.read() != 1)
this.fatal((byte)10, (String)"Malformed change cipher spec msg");
else
this.fatal((byte)10, (String)("illegal change cipher spec msg, conn state = " + this.connectionState + ", handshake state = " + this.handshaker.state));
this.receivedCCS = true;
this.changeReadCiphers();
this.expectingFinished = true;
continue;
case 21:
this.recvAlert(var1);
continue;
case 22:
this.initHandshaker();
if (!this.handshaker.activated())
if (this.connectionState == 3)
this.handshaker.activate(this.protocolVersion);
else
this.handshaker.activate((ProtocolVersion)null);
this.handshaker.process_record(var1, this.expectingFinished);
this.expectingFinished = false;
if (this.handshaker.invalidated)
this.handshaker = null;
this.receivedCCS = false;
if (this.connectionState == 3)
this.connectionState = 2;
else if (this.handshaker.isDone())
this.secureRenegotiation = this.handshaker.isSecureRenegotiation();
this.clientVerifyData = this.handshaker.getClientVerifyData();
this.serverVerifyData = this.handshaker.getServerVerifyData();
this.sess = this.handshaker.getSession();
this.handshakeSession = null;
this.handshaker = null;
this.connectionState = 2;
this.receivedCCS = false;
if (this.handshakeListeners != null)
HandshakeCompletedEvent var18 = new HandshakeCompletedEvent(this, this.sess);
SSLSocketImpl.NotifyHandshakeThread var19 = new SSLSocketImpl.NotifyHandshakeThread(this.handshakeListeners.entrySet(), var18);
var19.start();
if (var2 || this.connectionState != 2)
continue;
break;
case 23:
if (this.connectionState != 2 && this.connectionState != 3 && this.connectionState != 5)
throw new SSLProtocolException("Data received in non-data state: " + this.connectionState);
if (this.expectingFinished)
throw new SSLProtocolException("Expecting finished message, received data");
if (!var2)
throw new SSLException("Discarding app data");
var1.setAppDataValid(true);
break;
default:
if (debug != null && Debug.isOn("ssl"))
System.out.println(Thread.currentThread().getName() + ", Received record type: " + var1.contentType());
continue;
if (this.connectionState < 4)
this.checkSequenceNumber(this.readAuthenticator, var1.contentType());
return;
var1.close();
return;
主要是有一句 this.handshaker.process_record(var1, this.expectingFinished);处理记录
ClientHandshaker 类processMessge
void processMessage(byte var1, int var2) throws IOException
if (this.state >= var1 && var1 != 0)
throw new SSLProtocolException("Handshake message sequence violation, " + var1);
else
label109:
switch(var1)
case 0:
this.serverHelloRequest(new HelloRequest(this.input));
break;
case 1:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 15:
case 16:
case 17:
case 18:
case 19:
default:
throw new SSLProtocolException("Illegal client handshake msg, " + var1);
case 2:
this.serverHello(new ServerHello(this.input, var2));
break;
case 11:
if (this.keyExchange == KeyExchange.K_DH_ANON || this.keyExchange == KeyExchange.K_ECDH_ANON || this.keyExchange == KeyExchange.K_KRB5 || this.keyExchange == KeyExchange.K_KRB5_EXPORT)
this.fatalSE((byte)10, "unexpected server cert chain");
this.serverCertificate(new CertificateMsg(this.input));
this.serverKey = this.session.getPeerCertificates()[0].getPublicKey();
break;
case 12:
this.serverKeyExchangeReceived = true;
switch(this.keyExchange)
case K_RSA_EXPORT:
if (this.serverKey == null)
throw new SSLProtocolException("Server did not send certificate message");
if (!(this.serverKey instanceof RSAPublicKey))
throw new SSLProtocolException("Protocol violation: the certificate type must be appropriate for the selected cipher suite's key exchange algorithm");
if (JsseJce.getRSAKeyLength(this.serverKey) <= 512)
throw new SSLProtocolException("Protocol violation: server sent a server key exchange message for key exchange " + this.keyExchange + " when the public key in the server certificate" + " is less than or equal to 512 bits in length");
try
this.serverKeyExchange(new RSA_ServerKeyExchange(this.input));
catch (GeneralSecurityException var8)
throwSSLException("Server key", var8);
break label109;
case K_DH_ANON:
try
this.serverKeyExchange(new DH_ServerKeyExchange(this.input, this.protocolVersion));
catch (GeneralSecurityException var7)
throwSSLException("Server key", var7);
break label109;
case K_DHE_DSS:
case K_DHE_RSA:
try
this.serverKeyExchange(new DH_ServerKeyExchange(this.input, this.serverKey, this.clnt_random.random_bytes, this.svr_random.random_bytes, var2, this.localSupportedSignAlgs, this.protocolVersion));
catch (GeneralSecurityException var6)
throwSSLException("Server key", var6);
break label109;
case K_ECDHE_ECDSA:
case K_ECDHE_RSA:
case K_ECDH_ANON:
try
this.serverKeyExchange(new ECDH_ServerKeyExchange(this.input, this.serverKey, this.clnt_random.random_bytes, this.svr_random.random_bytes, this.localSupportedSignAlgs, this.protocolVersion));
catch (GeneralSecurityException var5)
throwSSLException("Server key", var5);
break label109;
case K_RSA:
case K_DH_RSA:
case K_DH_DSS:
case K_ECDH_ECDSA:
case K_ECDH_RSA:
throw new SSLProtocolException("Protocol violation: server sent a server key exchangemessage for key exchange " + this.keyExchange);
case K_KRB5:
case K_KRB5_EXPORT:
throw new SSLProtocolException("unexpected receipt of server key exchange algorithm");
default:
throw new SSLProtocolException("unsupported key exchange algorithm = " + this.keyExchange);
case 13:
if (this.keyExchange != KeyExchange.K_DH_ANON && this.keyExchange != KeyExchange.K_ECDH_ANON)
if (this.keyExchange != KeyExchange.K_KRB5 && this.keyExchange != KeyExchange.K_KRB5_EXPORT)
this.certRequest = new CertificateRequest(this.input, this.protocolVersion);
if (debug != null && Debug.isOn("handshake"))
this.certRequest.print(System.out);
if (this.protocolVersion.v < ProtocolVersion.TLS12.v)
break;
Collection var3 = this.certRequest.getSignAlgorithms();
if (var3 != null && !var3.isEmpty())
Collection var4 = SignatureAndHashAlgorithm.getSupportedAlgorithms(var3);
if (var4.isEmpty())
throw new SSLHandshakeException("No supported signature and hash algorithm in common");
this.setPeerSupportedSignAlgs(var4);
this.session.setPeerSupportedSignatureAlgorithms(var4);
break;
throw new SSLHandshakeException("No peer supported signature algorithms");
throw new SSLHandshakeException("Client certificate requested for kerberos cipher suite.");
throw new SSLHandshakeException("Client authentication requested for anonymous cipher suite.");
case 14:
this.serverHelloDone(new ServerHelloDone(this.input));
break;
case 20:
if (!this.receivedChangeCipherSpec())
this.fatalSE((byte)40, "Received Finished message before ChangeCipherSpec");
this.serverFinished(new Finished(this.protocolVersion, this.input, this.cipherSuite));
if (this.state < var1)
this.state = var1;
ClientHandshaker.serverCertificate:
private void serverCertificate(CertificateMsg var1) throws IOException
if (debug != null && Debug.isOn("handshake"))
var1.print(System.out);
X509Certificate[] var2 = var1.getCertificateChain();
if (var2.length == 0)
this.fatalSE((byte)42, "empty certificate chain");
if (this.reservedServerCerts != null)
String var3 = this.getEndpointIdentificationAlgorithmSE();
if ((var3 == null || var3.length() == 0) && !isIdentityEquivalent(var2[0], this.reservedServerCerts[0]))
this.fatalSE((byte)42, "server certificate change is restricted during renegotiation");
X509TrustManager var6 = this.sslContext.getX509TrustManager();
try
String var4;
if (this.keyExchange == KeyExchange.K_RSA_EXPORT && !this.serverKeyExchangeReceived)
var4 = KeyExchange.K_RSA.name;
else
var4 = this.keyExchange.name;
if (!(var6 instanceof X509ExtendedTrustManager))
throw new CertificateException("Improper X509TrustManager implementation");
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);
this.session.setPeerCertificates(var2);
X509TrustManagerImpl 类 checkServerTrusted:
X509TrustManagerImpl 类checkTrusted:(校验dns 和 证书)
private void checkTrusted(X509Certificate[] var1, String var2, Socket var3, boolean var4) throws CertificateException
Validator var5 = this.checkTrustedInit(var1, var2, var4);
SSLAlgorithmConstraints var6 = null;
SSLSocket var7;
if (var3 != null && var3.isConnected() && var3 instanceof SSLSocket)
var7 = (SSLSocket)var3;
SSLSession var8 = var7.getHandshakeSession();
if (var8 == null)
throw new CertificateException("No handshake session");
String var9 = var7.getSSLParameters().getEndpointIdentificationAlgorithm();
if (var9 != null && var9.length() != 0)
checkIdentity(var8, var1[0], var9, var4, getRequestedServerNames(var3));
ProtocolVersion var10 = ProtocolVersion.valueOf(var8.getProtocol());
if (var10.v >= ProtocolVersion.TLS12.v)
if (var8 instanceof ExtendedSSLSession)
ExtendedSSLSession var11 = (ExtendedSSLSession)var8;
String[] var12 = var11.getLocalSupportedSignatureAlgorithms();
var6 = new SSLAlgorithmConstraints(var7, var12, false);
else
var6 = new SSLAlgorithmConstraints(var7, false);
else
var6 = new SSLAlgorithmConstraints(var7, false);
var7 = null;
X509Certificate[] var13;
if (var4)
var13 = validate(var5, var1, var6, (String)null);
else
var13 = validate(var5, var1, var6, var2);
if (debug != null && Debug.isOn("trustmanager"))
System.out.println("Found trusted certificate:");
System.out.println(var13[var13.length - 1]);
X509TrustManagerImpl 类 checkIdentity
private static void checkIdentity(SSLSession var0, X509Certificate var1, String var2, boolean var3, List<SNIServerName> var4) throws CertificateException
boolean var5 = false;
String var6 = var0.getPeerHost();
if (var3)
String var7 = getHostNameInSNI(var4);
if (var7 != null)
try
checkIdentity(var7, var1, var2);
var5 = true;
catch (CertificateException var9)
if (var7.equalsIgnoreCase(var6))
throw var9;
if (!var5)
checkIdentity(var6, var1, var2);
static void checkIdentity(String var0, X509Certificate var1, String var2) throws CertificateException
if (var2 != null && var2.length() != 0)
if (var0 != null && var0.startsWith("[") && var0.endsWith("]"))
var0 = var0.substring(1, var0.length() - 1);
if (var2.equalsIgnoreCase("HTTPS"))
HostnameChecker.getInstance((byte)1).match(var0, var1);
else
if (!var2.equalsIgnoreCase("LDAP") && !var2.equalsIgnoreCase("LDAPS"))
throw new CertificateException("Unknown identification algorithm: " + var2);
HostnameChecker.getInstance((byte)2).match(var0, var1);
HostnameChecker.getInstance((byte)1).match(var0, var1);
private static final HostnameChecker INSTANCE_TLS = new HostnameChecker((byte)1);
public static final byte TYPE_LDAP = 2;
private static final HostnameChecker INSTANCE_LDAP = new HostnameChecker((byte)2);
public static HostnameChecker getInstance(byte var0)
if (var0 == 1)
return INSTANCE_TLS;
else if (var0 == 2)
return INSTANCE_LDAP;
else
throw new IllegalArgumentException("Unknown check type: " + var0);
private void matchDNS(String var1, X509Certificate var2) throws CertificateException
Collection var3 = var2.getSubjectAlternativeNames();
if (var3 != null)
boolean var4 = false;
Iterator var5 = var3.iterator();
while(var5.hasNext())
List var6 = (List)var5.next();
if (((Integer)var6.get(0)).intValue() == 2)
var4 = true;
String var7 = (String)var6.get(1);
if (this.isMatched(var1, var7))
return;
if (var4)
throw new CertificateException("No subject alternative DNS name matching " + var1 + " found.");
X500Name var9 = getSubjectX500Name(var2);
DerValue var10 = var9.findMostSpecificAttribute(X500Name.commonName_oid);
if (var10 != null)
try
if (this.isMatched(var1, var10.getAsString()))
return;
catch (IOException var8)
;
String var11 = "No name matching " + var1 + " found";
throw new CertificateException(var11);
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package sun.security.util;
import java.io.IOException;
import java.security.Principal;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import javax.security.auth.x500.X500Principal;
import sun.net.util.IPAddressUtil;
import sun.security.ssl.Krb5Helper;
import sun.security.x509.X500Name;
public class HostnameChecker
public static final byte TYPE_TLS = 1;
private static final HostnameChecker INSTANCE_TLS = new HostnameChecker((byte)1);
public static final byte TYPE_LDAP = 2;
private static final HostnameChecker INSTANCE_LDAP = new HostnameChecker((byte)2);
private static final int ALTNAME_DNS = 2;
private static final int ALTNAME_IP = 7;
private final byte checkType;
private HostnameChecker(byte var1)
this.checkType = var1;
public static HostnameChecker getInstance(byte var0)
if (var0 == 1)
return INSTANCE_TLS;
else if (var0 == 2)
return INSTANCE_LDAP;
else
throw new IllegalArgumentException("Unknown check type: " + var0);
public void match(String var1, X509Certificate var2) throws CertificateException
if (isIpAddress(var1))
matchIP(var1, var2);
else
this.matchDNS(var1, var2);
public static boolean match(String var0, Principal var1)
String var2 = getServerName(var1);
return var0.equalsIgnoreCase(var2);
public static String getServerName(Principal var0)
return Krb5Helper.getPrincipalHostName(var0);
private static boolean isIpAddress(String var0)
return IPAddressUtil.isIPv4LiteralAddress(var0) || IPAddressUtil.isIPv6LiteralAddress(var0);
private static void matchIP(String var0, X509Certificate var1) throws CertificateException
Collection var2 = var1.getSubjectAlternativeNames();
if (var2 == null)
throw new CertificateException("No subject alternative names present");
else
Iterator var3 = var2.iterator();
while(var3.hasNext())
List var4 = (List)var3.next();
if (((Integer)var4.get(0)).intValue() == 7)
String var5 = (String)var4.get(1);
if (var0.equalsIgnoreCase(var5))
return;
throw new CertificateException("No subject alternative names matching IP address " + var0 + " found");
private void matchDNS(String var1, X509Certificate var2) throws CertificateException
Collection var3 = var2.getSubjectAlternativeNames();
if (var3 != null)
boolean var4 = false;
Iterator var5 = var3.iterator();
while(var5.hasNext())
List var6 = (List)var5.next();
if (((Integer)var6.get(0)).intValue() == 2)
var4 = true;
String var7 = (String)var6.get(1);
if (this.isMatched(var1, var7))
return;
if (var4)
throw new CertificateException("No subject alternative DNS name matching " + var1 + " found.");
X500Name var9 = getSubjectX500Name(var2);
DerValue var10 = var9.findMostSpecificAttribute(X500Name.commonName_oid);
if (var10 != null)
try
if (this.isMatched(var1, var10.getAsString()))
return;
catch (IOException var8)
;
String var11 = "No name matching " + var1 + " found";
throw new CertificateException(var11);
public static X500Name getSubjectX500Name(X509Certificate var0) throws CertificateParsingException
try
Principal var1 = var0.getSubjectDN();
if (var1 instanceof X500Name)
return (X500Name)var1;
else
X500Principal var2 = var0.getSubjectX500Principal();
return new X500Name(var2.getEncoded());
catch (IOException var3)
throw (CertificateParsingException)(new CertificateParsingException()).initCause(var3);
private boolean isMatched(String var1, String var2)
if (this.checkType == 1)
return matchAllWildcards(var1, var2);
else
return this.checkType == 2 ? matchLeftmostWildcard(var1, var2) : false;
private static boolean matchAllWildcards(String var0, String var1)
var0 = var0.toLowerCase(Locale.ENGLISH);
var1 = var1.toLowerCase(Locale.ENGLISH);
StringTokenizer var2 = new StringTokenizer(var0, ".");
StringTokenizer var3 = new StringTokenizer(var1, ".");
if (var2.countTokens() != var3.countTokens())
return false;
else
do
if (!var2.hasMoreTokens())
return true;
while(matchWildCards(var2.nextToken(), var3.nextToken()));
return false;
private static boolean matchLeftmostWildcard(String var0, String var1)
var0 = var0.toLowerCase(Locale.ENGLISH);
var1 = var1.toLowerCase(Locale.ENGLISH);
int var2 = var1.indexOf(".");
int var3 = var0.indexOf(".");
if (var2 == -1)
var2 = var1.length();
if (var3 == -1)
var3 = var0.length();
return matchWildCards(var0.substring(0, var3), var1.substring(0, var2)) ? var1.substring(var2).equals(var0.substring(var3)) : false;
private static boolean matchWildCards(String var0, String var1)
int var2 = var1.indexOf("*");
if (var2 == -1)
return var0.equals(var1);
else
boolean var3 = true;
String var4 = "";
String var5;
for(var5 = var1; var2 != -1; var2 = var5.indexOf("*"))
var4 = var5.substring(0, var2);
var5 = var5.substring(var2 + 1);
int var6 = var0.indexOf(var4);
if (var6 == -1 || var3 && var6 != 0)
return false;
var3 = false;
var0 = var0.substring(var6 + var4.length());
return var0.endsWith(var5);
可以确定在调用HTTPS接口创建连接的时候会添加默认的sslcontextFactory和hostnameverifier,然后应用程序如果不重新设置这2个属性,则会使用默认值。默认的hostnameverifier校验不通过,默认的sslcontextFactory的校验逻辑是啥样的?
默认sslcontextFactory 的trustManager 的校验逻辑是 判断请求服务器的证书的根证书是否在我们信任的根证书列表内,如果在,则通过,如果不在,则拒绝。然后判断请求服务器的域名是否在下发证书的可选别名列表内。如果在,则通过,否则拒绝。
所以即使我们没有把请求服务器的证书添加到信任列表也是可以通过证书校验的。
当使用默认的hostnameverifier 的 时候,java 会判断如果是默认hostnameverifier 就跳过验证,所以会https接口调用会通过。
问题:
1 在restTemplate使用SimpleClientHttpRequestFactory的情况下,按理说是不应该掉的通腾讯接口的,为什么能调通?
没有被hostnameverifier 拦截掉?因为默认的hostnameverifier 别忽略了,没有执行,所以不会被拦截。而服务器证书校验会通过是因为信任管理器 校验的逻辑是如果请求服务器的根证书在信任的根证书列表内,则通过,然后如果请求服务器的域名在证书的别名列表内,则通过,否则不通过。
2 同一套代码,为什么本地的restTemplate 校验通过,服务器的证书校验不通过?提示Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching testapi.telesafe.qq.com found.
无论使用什么协议,算法,通讯,应该不影响才对。
1 因为jdk 版本有所不同,1.8.0_271 和1.8.0_40 的 ssl握手这块代码的逻辑变动很大。
2 本地可以是因为本地请求了正确的服务器,测试不可以是因为测试环境请求被拦截,然后转发到了错误的服务器,所以握手失败。 如果是这个逻辑,测试服务器上curl post 出去的请求应该也会提示握手失败。
curl -X POST -d '' https://testapi.telesafe.qq.com/BlackLibQuery/
如果curl 握手成功,则确定没有被拦截,如果也失败,可以查看ping testapi.telesafe.qq.com 返回的IP 和服务端确认是否是 对方的IP地址。
3 为什么测试服务器之前接口是通的,为什么突然就不行了呢?然后突然又好了(确定jdk没有变化)
而线上环境从头至尾都是OK的。
只能是证书发生变化 ,如果是证书发生变化,为什么线上环境没有问题,而且请求服务器的证书没有到期没有理由会更换证书的,
只能理解为 测试环境和线上环境是 2套环境,2套nginx ,所以有可能线上没有问题,测试有问题。
怎么确定是证书发生了变化? 在浏览器打开地址确认证书是否真有问题。
以上是关于httpsUrlConnection 如何设置的默认sslcontext和 hostnameverifier?的主要内容,如果未能解决你的问题,请参考以下文章
如何为单个 HttpsURLConnections 设置 https.proxyHost 和 https.proxyPort?
如何让 Jersey 客户端使用 HttpsURLConnection 中设置的 defaultSSLSocketFactory?
如何通过 HttpsURLConnection 使用证书身份验证?
Android HttpsURLConnection,如何判断响应是不是被缓存?
使用HttpsURLConnection时如何覆盖Android发送给服务器的密码列表?
如何在 Android 上使用 HttpsURLConnection 和 HttpResponseCache 强制缓存?