老版本的Volley是不支持https的,新版本的Volley是可以支持https的,不过呢,如果服务器是自签名SSL,并且app端需要检验证书,Volley就不行了,需要借助okhttp。本文将对服务器自签名的情况下,app端检验分三种情况:第一,app不检验证书(Volley+okhttp);第二,app端不检验证书(新版Volley);第三,app需要检验证书(Volley+okhttp)。
先来看一下Volley创建请求队列的方法:
RequestQueue newRequestQueue (Context context) 和 RequestQueue newRequestQueue (Context context, HttpStack stack)
第一个方法不多说了,创建的队列用的是默认的HttpStack。要让Volley借助ok支持https,我们需要自定HttpStack,如下代码:
import android.util.Log; import com.android.volley.toolbox.HurlStack; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.OkUrlFactory; import java.io.IOException; import java.io.InputStream; import java.net.CookieManager; import java.net.CookiePolicy; import java.net.HttpURLConnection; import java.net.URL; import java.security.KeyStore; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; /** * Created by cyq on 2016/6/17. */ public class SelfHttpsStack extends HurlStack { private static String TAG = "SelfHttpsStack"; private OkHttpClient okHttpClient; public SelfHttpsStack(InputStream is) { buildOkHttpClient(is); } @Override protected HttpURLConnection createConnection(URL url) throws IOException { if ("https".equals(url.getProtocol()) ) { HttpsURLConnection connection = (HttpsURLConnection) new OkUrlFactory(okHttpClient).open(url); connection.setSSLSocketFactory(okHttpClient.getSslSocketFactory()); return connection; } else { return new OkUrlFactory(okHttpClient).open(url); } } private void buildOkHttpClient(InputStream cerInputStream) { if (okHttpClient == null) { okHttpClient = new OkHttpClient(); okHttpClient.setHostnameVerifier(new TrustAllHostnameVerifier()); okHttpClient.setSslSocketFactory(createSSLSocketFactory()); if (cerInputStream != null) { //okHttpClient.setSslSocketFactory(createSSLSocketFactory(cerInputStream)); } //CookieManager 是 CookieHandler 的子类 okHttpClient.setCookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ALL)); } } // 创建不检验证书的SSLSokectFactory private static SSLSocketFactory createSSLSocketFactory() { SSLSocketFactory sSLSocketFactory = null; try { SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, new TrustManager[]{new TrustAllManager()}, new SecureRandom()); sSLSocketFactory = sc.getSocketFactory(); } catch (Exception e) { Log.e(TAG, e.getMessage()); } return sSLSocketFactory; } // 创建需要检验证书的SSLSokectFactory private static SSLSocketFactory createSSLSocketFactory(InputStream cerInputStream) { try { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); keyStore.setCertificateEntry("qxueyou", certificateFactory.generateCertificate(cerInputStream)); cerInputStream.close(); SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); return sslContext.getSocketFactory(); } catch (Exception e) { Log.e(TAG, e.getMessage()); } return null; } private static class TrustAllManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } // 自定义域名检验类 private static class TrustAllHostnameVerifier implements HostnameVerifier { @Override public boolean verify(String hostname, SSLSession session) { return true; } } }使用示例: RequestQueue queue = Volley.newRequestQueue(context, new SelfHttpsStack(null));
quene.start();
StringRequest stringRequest = new StringRequest(...)
queue.add(stringRequest);
以上即是第一种情况:app不检验证书。
如果app需要校验证书怎么办呢?很简单,上面的类已经支持了,只要把buildOkHttpClient方法的okHttpClient.setSslSocketFactory(createSSLSocketFactory());改为okHttpClient.setSslSocketFactory(createSSLSocketFactory(cerInputStream));(代码中已注释)
使用时只要将证书传入即可,如RequestQueue queue = Volley.newRequestQueue(context, new SelfHttpsStack(context.getAssets().open("tomcat.cer")));
以上便是第二种情况:app需要校验证书。
最后再来看看使用新版的Volley不校验证书支持https(需要支持校验证书同上)
原理,创建X509TrustManager的子类,忽略域名校验,完整代码如下:
import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; public class FakeTrustManager implements X509TrustManager { private static TrustManager[] trustManagers; private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {}; @Override public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException { //To change body of implemented methods use File | Settings | File Templates. } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException { //To change body of implemented methods use File | Settings | File Templates. } public boolean isClientTrusted(X509Certificate[] chain) { return true; } public boolean isServerTrusted(X509Certificate[] chain) { return true; } @Override public X509Certificate[] getAcceptedIssuers() { return _AcceptedIssuers; } public static void allowAllSSL() { HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String arg0, SSLSession arg1) { // TODO Auto-generated method stub return true; } }); SSLContext context = null; if (trustManagers == null) { trustManagers = new TrustManager[] { new FakeTrustManager() }; } try { context = SSLContext.getInstance("TLS"); context.init(null, trustManagers, new SecureRandom()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory()); } }在发送请求前调用即可,如:
FakeTrustManager.allowAllSSL();
RequestQueue queue = Volley.newRequestQueue(context);
quene.start();
StringRequest stringRequest = new StringRequest(...)
queue.add(stringRequest);
水平有限,欢迎指正~