最近遇到网络安全方面的问题,所以要使用https,由于在工程中使用了Volley,所以对Volley的https做了一些研究,当然大部分是参考的网络上一些前辈们的成果,自己进行一下总结:
https涉及到证书的认证方式,我就按认证的类型来说明:
1,全部信任证书;
2,信任指定证书;
3,信任系统提供的证书(CA颁发);
1,全部信任证书
添加HTTPSTrustManager类,如下:
import android.content.Context;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.Certificate;
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.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
public class HTTPSTrustManager 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.
}
@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 HTTPSTrustManager() };
}
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());
}
}
再在创建 RequestQueue 时,添加一句:
HTTPSTrustManager.allowAllSSL();//信任所有证书
RequestQueue mQueue = Volley.newRequestQueue(getActivity());
后面就可以根据需要添加请求了:
mQueue.add(request);
2,信任指定证书
先要获取到证书,我们可以放到assert目录下,例如这里使用的证书的文件名为“root.crt”。
然后通过如下函数来读取,并返回SSLSocketFactory:
//生成SSLSocketFactory
private SSLSocketFactory initSSLSocketFactory() {
//生成证书:Certificate
CertificateFactory cf = null;
SSLSocketFactory factory = null;
try {
cf = CertificateFactory.getInstance("X.509");
InputStream caInput = getActivity().getAssets().open("root.crt");
Certificate ca = null;
try {
ca = cf.generateCertificate(caInput);
} finally {
try {
caInput.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//初始化公钥:keyStore
String keyType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
//初始化TrustManagerFactory
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory managerFactory = TrustManagerFactory.getInstance(algorithm);
managerFactory.init(keyStore);
//初始化sslContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, managerFactory.getTrustManagers(), null);
factory = sslContext.getSocketFactory();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return factory;
}
然后,在创建 RequestQueue 时,按如下方式来执行:
//生成SSLSocketFactory
SSLSocketFactory sslSocketFactory = initSSLSocketFactory();
//HurlStack两个参数默认都是null,如果传入SSLSocketFactory,那么会以Https的方式来请求网络
HurlStack stack = new HurlStack(null, sslSocketFactory);
//传入处理Https的HurlStack
mQueue = Volley.newRequestQueue(this.getActivity(),stack);
后面添加请求的方法是一样的:
mQueue.add(request);
3,信任系统提供的证书(CA颁发)
先了解下什么是系统提供的证书,看看下面的说明:
HTTPS通信所用到的证书由CA提供,需要在服务器中进行相应的设置才能生效。另外在我们的客户端设备中,只要访问的HTTPS的网站所用的证书是可信CA根证书签发的,如果这些CA又在浏览器或者操作系统的根信任列表中,就可以直接访问。
也就是说,Android系统有一个根信任列表,若我们的证书是这个列表中的某个根证书的子证书,就不需要特别指定了。
通常情况,就是使用https,而不做任何特别指定,就是使用系统的证书了。
可是Volley直接访问https,我遇到了问题:
最初我的工程,由http直接切换到https时,是不能运行的,通过上面的方法,信任所有证书,或者信任指定证书,可以进行https的访问了。
但是前面两种方法各有一些缺陷:
全部信任证书:不安全,Google也不推荐,据我个人了解,就是防不住中间人攻击。
信任指定证书:这种方式是可以防止中间人攻击的。
但是问题可能会出在App上:这个证书直接放在app中,若是被反编译出来,证书会被人获得,仍然会导致某些风险;
另外就是,若是指定的证书变化了,就需要更换所有的app,在用户量较大的情况下,这几乎是不可能完成的任务。
所以,若是在可能的情况下,最好是使用正式认证的证书。这时,也不需要我们去指定证书了,Android系统就已经维护了一套了。
怎么实现呢?经过一番搜索,有人说新版本的Volley已经支持https了。难道是版本的问题?
我从如下地址下载了一个较新的包(V1.0.19):
http://mvnrepository.com/artifact/com.mcxiaoke.volley/library
然后替换了系统中的旧的jar包,果然好了!
这时,使用Volley就是普通的操作方式:
先创建 RequestQueue :
RequestQueue mQueue = Volley.newRequestQueue(getActivity());
后面就是根据需要添加请求了:
mQueue.add(request);
最后总结一下:
1,全部信任证书:毕竟是https,要比http安全多了,但是还存在被中间人攻击的风险;
2,信任指定证书:保证了网络传输链路的安全,但是App本身存在泄漏证书的风险。另外,更换证书时比较麻烦。
3,信任系统提供的证书(CA颁发);这种方式最安全,但是可能需要花钱。也可以找些免费的证书,但是使用期限可能有较大的限制。
这三种方式各有特色,具体采用哪种方式,还是需要根据项目的实际情况来确定。
参考:
http://www.lai18.com/content/1040213.html
http://www.cnblogs.com/punkisnotdead/p/4788199.html