Android 使Volley完美支援自定義證書的Https

希爾瓦娜斯女神發表於2015-09-07

其實在最早的版本里,Volley甚至是不支援https協議的,只能跑http,當然你也可以自己修改他的原始碼讓他支援,如今volley的程式碼經過一些改進以後,

已經可以完美支援https協議了,無論是在2.3版本以上還是在2.3版本以下,大家可以嘗試用volley去訪問github 是成功的,但是你如果用volley去訪問

12306這種類似的 用自定義證照的網站 就很容易失敗。那我下面就把volley 程式碼稍作修改,讓volley也可以完美支援自定義證照的https請求。

當然程式碼只是展示功能使用,你們可以用更優雅的方式 ----實現一個HttpStack,然後直接傳你自定義好的stack即可。我這裡圖簡便就寫了個最簡單

的演示程式碼。其實難倒是也不難,主要還是要考慮2.3版本以上和以下的兩種情況。

 

第一步,把你的自定義證照 拷貝到res/raw/下。

 

第二步,稍微修改下volley的原始碼

 

  1 /*
  2  * Copyright (C) 2012 The Android Open Source Project
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package com.android.volley.toolbox;
 18 
 19 import android.content.Context;
 20 import android.content.pm.PackageInfo;
 21 import android.content.pm.PackageManager.NameNotFoundException;
 22 import android.net.http.AndroidHttpClient;
 23 import android.os.Build;
 24 import android.util.Log;
 25 
 26 import com.android.volley.Network;
 27 import com.android.volley.RequestQueue;
 28 
 29 import org.apache.http.client.HttpClient;
 30 import org.apache.http.conn.scheme.PlainSocketFactory;
 31 import org.apache.http.conn.scheme.Scheme;
 32 import org.apache.http.conn.scheme.SchemeRegistry;
 33 import org.apache.http.impl.client.DefaultHttpClient;
 34 import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
 35 import org.apache.http.params.BasicHttpParams;
 36 import org.apache.http.params.HttpParams;
 37 
 38 import java.io.File;
 39 import java.io.IOException;
 40 import java.io.InputStream;
 41 import java.security.KeyManagementException;
 42 import java.security.KeyStore;
 43 import java.security.KeyStoreException;
 44 import java.security.NoSuchAlgorithmException;
 45 import java.security.UnrecoverableKeyException;
 46 import java.security.cert.Certificate;
 47 import java.security.cert.CertificateException;
 48 import java.security.cert.CertificateFactory;
 49 
 50 import javax.net.ssl.SSLContext;
 51 import javax.net.ssl.SSLSocketFactory;
 52 import javax.net.ssl.TrustManagerFactory;
 53 
 54 public class Volley {
 55 
 56     /**
 57      * Default on-disk cache directory.
 58      */
 59     private static final String DEFAULT_CACHE_DIR = "volley";
 60 
 61     private Context mContext;
 62 
 63     /**
 64      * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
 65      *
 66      * @param context A {@link Context} to use for creating the cache dir.
 67      * @param stack   An {@link HttpStack} to use for the network, or null for default.
 68      * @return A started {@link RequestQueue} instance.
 69      */
 70     public static RequestQueue newRequestQueue(Context context, HttpStack stack, boolean selfSignedCertificate, int rawId) {
 71         File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
 72 
 73         String userAgent = "volley/0";
 74         try {
 75             String packageName = context.getPackageName();
 76             PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
 77             userAgent = packageName + "/" + info.versionCode;
 78         } catch (NameNotFoundException e) {
 79         }
 80 
 81         if (stack == null) {
 82             if (Build.VERSION.SDK_INT >= 9) {
 83                 if (selfSignedCertificate) {
 84                     stack = new HurlStack(null, buildSSLSocketFactory(context, rawId));
 85                 } else {
 86                     stack = new HurlStack();
 87                 }
 88             } else {
 89                 // Prior to Gingerbread, HttpUrlConnection was unreliable.
 90                 // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
 91                 if (selfSignedCertificate)
 92                     stack = new HttpClientStack(getHttpClient(context, rawId));
 93                 else {
 94                     stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
 95                 }
 96             }
 97         }
 98 
 99         Network network = new BasicNetwork(stack);
100 
101         RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
102         queue.start();
103 
104         return queue;
105     }
106 
107     /**
108      * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
109      *
110      * @param context A {@link Context} to use for creating the cache dir.
111      * @return A started {@link RequestQueue} instance.
112      */
113     public static RequestQueue newRequestQueue(Context context) {
114         return newRequestQueue(context, null, false, 0);
115     }
116 
117     private static SSLSocketFactory buildSSLSocketFactory(Context context, int certRawResId) {
118         KeyStore keyStore = null;
119         try {
120             keyStore = buildKeyStore(context, certRawResId);
121         } catch (KeyStoreException e) {
122             e.printStackTrace();
123         } catch (CertificateException e) {
124             e.printStackTrace();
125         } catch (NoSuchAlgorithmException e) {
126             e.printStackTrace();
127         } catch (IOException e) {
128             e.printStackTrace();
129         }
130 
131         String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
132         TrustManagerFactory tmf = null;
133         try {
134             tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
135             tmf.init(keyStore);
136 
137         } catch (NoSuchAlgorithmException e) {
138             e.printStackTrace();
139         } catch (KeyStoreException e) {
140             e.printStackTrace();
141         }
142 
143         SSLContext sslContext = null;
144         try {
145             sslContext = SSLContext.getInstance("TLS");
146         } catch (NoSuchAlgorithmException e) {
147             e.printStackTrace();
148         }
149         try {
150             sslContext.init(null, tmf.getTrustManagers(), null);
151         } catch (KeyManagementException e) {
152             e.printStackTrace();
153         }
154 
155         return sslContext.getSocketFactory();
156 
157     }
158 
159     private static HttpClient getHttpClient(Context context, int certRawResId) {
160         KeyStore keyStore = null;
161         try {
162             keyStore = buildKeyStore(context, certRawResId);
163         } catch (KeyStoreException e) {
164             e.printStackTrace();
165         } catch (CertificateException e) {
166             e.printStackTrace();
167         } catch (NoSuchAlgorithmException e) {
168             e.printStackTrace();
169         } catch (IOException e) {
170             e.printStackTrace();
171         }
172         if (keyStore != null) {
173         }
174         org.apache.http.conn.ssl.SSLSocketFactory sslSocketFactory = null;
175         try {
176             sslSocketFactory = new org.apache.http.conn.ssl.SSLSocketFactory(keyStore);
177         } catch (NoSuchAlgorithmException e) {
178             e.printStackTrace();
179         } catch (KeyManagementException e) {
180             e.printStackTrace();
181         } catch (KeyStoreException e) {
182             e.printStackTrace();
183         } catch (UnrecoverableKeyException e) {
184             e.printStackTrace();
185         }
186 
187         HttpParams params = new BasicHttpParams();
188 
189         SchemeRegistry schemeRegistry = new SchemeRegistry();
190         schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
191         schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
192 
193         ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
194 
195 
196         return new DefaultHttpClient(cm, params);
197     }
198 
199     private static KeyStore buildKeyStore(Context context, int certRawResId) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
200         String keyStoreType = KeyStore.getDefaultType();
201         KeyStore keyStore = KeyStore.getInstance(keyStoreType);
202         keyStore.load(null, null);
203 
204         Certificate cert = readCert(context, certRawResId);
205         keyStore.setCertificateEntry("ca", cert);
206 
207         return keyStore;
208     }
209 
210     private static Certificate readCert(Context context, int certResourceID) {
211         InputStream inputStream = context.getResources().openRawResource(certResourceID);
212         Certificate ca = null;
213 
214         CertificateFactory cf = null;
215         try {
216             cf = CertificateFactory.getInstance("X.509");
217             ca = cf.generateCertificate(inputStream);
218 
219         } catch (CertificateException e) {
220             e.printStackTrace();
221         }
222         return ca;
223     }
224 }

 

 

第三步就是呼叫方式稍微做下修改:

 

其實主要就是你如果想使用自定義的證照https的時候 第三個引數記得傳true,並且把證照也傳進去,

當然寫的優雅的話還是最好自己寫個httpstack,然後volley的原始碼可以不用改,只需要在使用的時候

傳自己的stack即可

 

 

相關文章