Android7.0的SSL自签名证书问题

本文探讨了Android7.0中SSL证书处理的变化及应对方法。重点介绍了自签名证书的应用场景及其在APP端的配置步骤,并讨论了可能出现的问题如证书过期与多服务器证书管理。

今年春节前夕客户反映在升级到Android7.0后APP不能通讯了。本人这个春节算是没过好了-_-!。
简单研究后发现是Android7.0对SSL证书的处理有了改进,更合理或者说更严格了。而原先的安卓前端使用的允许所有证书的写法失效了(本人没人发现有可以用的方法)。当然这个方法确实不推荐使用的,一旦这么做会产生很大的安全隐患。
其实官方的文档对SSL证书问题已经说明的非常清楚了,还是中文的。以下是链接
https://developer.android.com/training/articles/security-config.html#CustomTrust
https://developer.android.com/training/articles/security-ssl.html#HttpsExample
(需要翻墙)

首先Android系统自带了大量证书的,如果你有证书颁发机构 (CA)发放的证书,那么你的APP端不需要做什么特殊的事了。不过一般需要花钱才能买到这种证书。
第二就是采用自签名证书的情况,这时候就需要在APP端做一些事了。APP需要导入pem或crt为扩展名的公钥。一般这就是一些字符串,以BEGIN CERTIFICATE开头END CERTIFICATE结尾
比如这样:

-----BEGIN CERTIFICATE-----
MIIDnDCCAoQ.....avwIvw==
-----END CERTIFICATE-----

可以通过使用openssl做出来,具体做法就不赘述了,小伙伴们可以找度娘。
然后就是APP需要使用这个公钥给自己的https通讯加密,官方文档上已经有代码了,这里直接贴一下。

// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
    ca = cf.generateCertificate(caInput);
    System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
    caInput.close();
}

// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);

// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);

// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);

// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL("https://certs.cac.washington.edu/CAtest/");
HttpsURLConnection urlConnection =
    (HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);

注意几个坑
1.最后一句代码不能编译,其实你只需要给自己的HttpsURLConnection设置上SSLSocketFactory就行了。
2.HttpsURLConnection是HttpURLConnection的子类,打开https的url时才会返回HttpsURLConnection这点要注意。
3.证书一般会绑定url地址,可以通过证书信息看到
证书信息
如果和url不匹配会报错,可以自己改写一下HostnameVerifier的规则。当然这会降低安全性。

urlConnection.setHostnameVerifier(new HostnameVerifier() {
    @Override
     public boolean verify(String hostname, SSLSession session) {
     //这里也可以直接返回true更简单
        HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
        return hv.verify("www.xxxx.com", session);
    }
});

还有几个难题:
1.证书过期的问题。一旦过期,客户端的证书怎么更新是个大问题。本人这次使用了长有效期的证书规避这个问题了。
2.多个服务器多个证书的情况,客户端的处理就非常麻烦了。这次本人解决方法是使用nginx做负载均衡,统一使用一个证书。但是这个需要改变后台结构,需要慎重。

总结一下,使用自签名的HTTPS服务器有不少问题和坑,小伙伴们如果碰到这种情况,最好先研究一下客户端的处理比较好

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值