背景
作为开发人员,我们需要对网络访问的安全性加以保证,这样才能在基本上保证我们的数据不受到修改和攻击。笔者的项目之前用的是Volley框架访问的网络,基于http协议。现在我们需要使用更为安全的https。https简单的理解就是http+ssl,对于SSL证书,自己签发也行,花钱购买也可以,孰优孰劣,大家自行百度了解。
因为是基于Volley框架访问的网络,所以网上搜到的许多方法都不能用了,经过大量筛选测试,最靠谱的方法就是修改Volley源码了。
PASS
刚开始想着下载个Volley的jar包,修改完毕再直接生成新的jar来使用,可是生成新jar后的签名和源jar包不一致,所以无法使用了。此方案PASS。
改源码
后来直接下载Volley的zip包,( 点击下载,侵删)。先把它运行起来,(该Volley项目是用Android Studio写的),如果你的项目同样是Studio写的,那就比较省事了。笔者目前还用的是老旧的Eclipse,直接导入就行,差别也不太大。找到android.volley.toolbox下面的Volley.java文件,对里面的内容进行修改替换。替换后的代码如下:
Volley.java代码
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.volley.toolbox;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.http.AndroidHttpClient;
import android.os.Build;
import com.android.volley.RequestQueue;
import com.jiemi.waiterpad.R;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
public class Volley {
/**
* Default on-disk cache directory.
*/
private static final String DEFAULT_CACHE_DIR = "volley";
private static BasicNetwork network;
private static RequestQueue queue;
private Context mContext;
/**
* Creates a default instance of the worker pool and calls
* {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @param stack An {@link HttpStack} to use for the network, or null for
* default.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context,
HttpStack stack, boolean selfSignedCertificate, int rawId) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(
packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
if (selfSignedCertificate) {
stack = new HurlStack(null, buildSSLSocketFactory(context,
rawId));
} else {
stack = new HurlStack();
}
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See:
// http://android-developers.blogspot.com/2011/09/androids-http-clients.html
if (selfSignedCertificate)
stack = new HttpClientStack(getHttpClient(context, rawId));
else {
stack = new HttpClientStack(
AndroidHttpClient.newInstance(userAgent));
}
}
}
//此处是坑啊,network和queue 是static的,不用一直new
if (network == null) {
network = new BasicNetwork(stack);
}
if (queue == null) {
queue = new RequestQueue(new DiskBasedCache(cacheDir),network);
}
queue.start();
return queue;
}
/**
* Creates a default instance of the worker pool and calls
* {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context) {
// 如果你目前还没有证书,那么先用下面的这行代码,http可以照常使用.
// return newRequestQueue(context, null, false, 0);
// 此处R.raw.certificateName 表示你的证书文件,替换为自己证书文件名字就好
return newRequestQueue(context, null, true, R.raw.jiemica);
}
private static SSLSocketFactory buildSSLSocketFactory(Context context,
int certRawResId) {
KeyStore keyStore = null;
try {
keyStore = buildKeyStore(context, certRawResId);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = null;
try {
tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
sslContext.init(null, tmf.getTrustManagers(), null);
} catch (KeyManagementException e) {
e.printStackTrace();
}
return sslContext.getSocketFactory();
}
private static HttpClient getHttpClient(Context context, int certRawResId) {
KeyStore keyStore = null;
try {
keyStore = buildKeyStore(context, certRawResId);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (keyStore != null) {
}
org.apache.http.conn.ssl.SSLSocketFactory sslSocketFactory = null;
try {
sslSocketFactory = new org.apache.http.conn.ssl.SSLSocketFactory(
keyStore);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
HttpParams params = new BasicHttpParams();
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory
.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(
params, schemeRegistry);
return new DefaultHttpClient(cm, params);
}
private static KeyStore buildKeyStore(Context context, int certRawResId)
throws KeyStoreException, CertificateException,
NoSuchAlgorithmException, IOException {
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
Certificate cert = readCert(context, certRawResId);
keyStore.setCertificateEntry("ca", cert);
return keyStore;
}
private static Certificate readCert(Context context, int certResourceID) {
InputStream inputStream = context.getResources().openRawResource(
certResourceID);
Certificate ca = null;
CertificateFactory cf = null;
try {
cf = CertificateFactory.getInstance("X.509");
ca = cf.generateCertificate(inputStream);
} catch (CertificateException e) {
e.printStackTrace();
}
return ca;
}
}
改自己的项目
然后就是大刀阔斧的改自己的项目了。首先把证书文件拷贝到res/raw文件夹下。然后把libs下面的Volley.jar包删除,会发现多处报错(把访问网络的代码删除了当然会报错),先不用管,接着把自己刚才修改的Volley源码添加过来。直接把com.android.volley直接copy到自己项目的src下,把上面源码中提到的证书文件的名字替换为自己项目证书的名字就可以了。
小结
起初也是一头雾水,查了好多文章,咨询了许多朋友,总算是顺利的用上了https。所以呢,世上本没有路,尝试的多了,也会踏出一条路。Go on !