Android 网络安全之https
简单说一下 http 和https 的区别 http是应用层协议 而https是http 的升级版 是网络安全大趋势之下的产物 在现有条件下 常规的http已经无法保证数据和平台的安全,所以https 已成大势 目前的http 通过一些工具是可以抓取到数据的 例如Fiddler ,charles 就能轻易抓取http的请求数据 而https 则可以轻松避免这种抓取数据的方式
http 请求是通过 http + tcp 进行的应用传输的 而https 则是在http 和 tcp 之间加了一层安全验证也就是 SSL/TLS的验证 数据证书验证是使用CA证书(数据证书颁发机构办法的证书)进行密码校验 通过验证才能进行网络请求
下面说一下android 的https (以okhttp为例)
先说下本人的策略 使用二级缓存的方法进行证书加载 现在很多朋友大都是以硬编码的方式或内置证书的方式加载 本人觉得这种方式是不安全的 第一不安全是因为有意破坏者可以通过反编译 或者 破解获取你的证书 第二 ca证书是由时间限制的 如果时间到期 再去更换证书 而你只能通过强制更新才能让用户进行使用 这个会给用户很不好的体验
我的策略是 内置加缓存的方式加载证书 首先缓存是需要权限的 若用户没有授权 则终端无法缓存 用户也就无法正常使用 此时我使用内置的 如果客户授权了读写权限 我就是用缓存的 以此避免证书过期问题
证书验证 也有两种方式 第一 单项验证(弱验证)(服务器验证客户端携带的证书是否有效)第二 双向验证(强验证) 客户端验证
服务端握手时返回的公钥信息进行host验证 服务器在验证客户端的数字签名是否正确 一般双向验证是 浏览器金融类app等使用的较多 也是非常安全的一种方式 ; 我使用的是第一种单项验证 因为我的host是我自己设定好的 所以我没有进行host 验证
public class SSLUtil {
/**
* 获取SSLSocketFactory
*
* @param certificates 证书流文件
* @return
*/
public static SSLSocketFactory getSSLSocketFactory(InputStream... certificates) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e) {
}
}
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) {
e.printStackTrace();
}
return null;
}
}
返回SSLSocketFactory
// 网络请求方法
OkHttpClient.Builder okBuilder = new OkHttpClient.Builder().addInterceptor(interceptor)
.writeTimeout(BaseConstant.NET_WRITE_TIME_OUT, TimeUnit.SECONDS)
.readTimeout(BaseConstant.NET_READ_TIME_OUT, TimeUnit.SECONDS)
.connectTimeout(BaseConstant.NET_REQUEST_TIME_OUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addInterceptor(new ErrorStatuInterceptor())
.addInterceptor(new PrintLogInterceptor());
if (CA.getCAFile()!=null){
if (SSLUtil.getSSLSocketFactory(CA.getCAByte()) != null){
okBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
}).sslSocketFactory(SSLUtil.getSSLSocketFactory(CA.getCAByte()));
}
}else {
if (SSLUtil.getSSLSocketFactory(CA.getCAByte()) != null){
try {
okBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
}).sslSocketFactory(SSLUtil.getSSLSocketFactory(UiUtils.getContext().getAssets().open("nade.cer")));
} catch (IOException e) {
e.printStackTrace();
}
// }).sslSocketFactory(SSLUtil.getSSLSocketFactory(CA.getCAByte()));
}
}
// 此方法用于验证host 返回true 是不验证
okBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
这个可以可以根据需要进行验证
好了 不多说 到此结束 有需要可以私信我