赞
踩
HTTP协议
Http协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web)服务器传输超文本到本地浏览器的传送协议。
HTTPS协议
Https协议是Hypertext Transfer Protocol Secure(安全超文本传输协议)的缩写,是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议。
http和https的区别:
http | https |
---|---|
HTTP | HTTP |
TCP | SSL / TLS |
IP | TCP |
IP |
SSL/TLS协议:
SSL及其继任者TLS是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层与应用层之间对网络连接进行加密。
SSL (Secure Sockets Layer 安全套接字协议) 是一种在客户端跟服务器端建立一个加密连接的安全标准. 一般用来加密网络服务器跟浏览器, 或者是邮件服务器跟邮件客户端(如: Outlook)之间传输的数据。
TLS(Transport Layer Security 传输层安全)是更为安全的升级版的SSL。
SSL /TLS能保障敏感信息(如:银行卡号, 社保卡号, 登陆凭证等)的传输安全. 一般情况下, 数据在浏览器跟服务器之间的传输使用的是明文格式, 这种方式存在资料被窃取的风险. 如果黑客能拦截浏览器跟服务器之间的通讯数据, 就能看到通讯的内容. 如果某个网站受 SSL 证书保护,其相应的 URL 中会显示 HTTPS,单击浏览器地址栏的挂锁图标,即可查看证书详细信息,包括颁发机构和网站所有者的公司名称。
什么是自签名证书(self-signed certicates)?
自签名证书就是没有通过受信任的证书颁发机构, 自己给自己颁发的证书.
SSL 证书大致分三类:
由安卓认可的证书颁发机构CA(Certificate Authority)(如: VeriSign、DigiCert), 或这些机构的下属机构颁发的证书.
没有得到安卓认可的证书颁发机构颁发的证书.
自己颁发的证书, 分临时性的(在开发阶段使用)或在发布的产品中永久性使用的两种.
而只有Android系统认可的机构办法的证书,在使用过程中才不会出现安全提示。
为什么会有人使用自签名的证书呢?
(重要的事重复三遍)免费,免费,免费,不用花费一分钱,在开发阶段写的代码, 测试跟发布的时候也可以用。
1.获取证书流有两种方式:
- 将服务端提供的证书文件放到assets文件夹里面,然后获取。(因为是在assets文件下,所以会写入到apk里面)
- 打开证书文件,将证书里面的内容以字符串的形式通过流写入。(不会写入到apk中)
2.创建秘钥,添加证书进去
3.创建信任管理器,并把秘钥初始化到信任管理器里
4.获取SLL上下文,并把信任管理器初始化到SSL里
5.获取socket工厂,设置到okhttpclient中
6.检验主机名
if (UrlConsts.serverUrl.contains("https://")){ InputStream cerInputStream = null; try { //方式一:在assets中获取证书 cerInputStream = context.getAssets().open("ssl.cer"); //方式二:把字符串通过流的形式获取证书 // cerInputStream = new ByteArrayInputStream(cerText.getBytes("UTF-8")); //获取socket工厂 SSLSocketFactory sslSocketFactory = SSLUtil.getSSLSocketFactory(cerInputStream); //okhttp设置socket工厂 okHttpClientBuilder.sslSocketFactory(sslSocketFactory); } catch (IOException e) { e.printStackTrace(); } } //检验主机名 okHttpClientBuilder.hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } });
public class SSLUtil { /** * 返回SSLSocketFactory * * @param certificates 证书的输入流 * @return SSLSocketFactory */ public static SSLSocketFactory getSSLSocketFactory(InputStream... certificates) { return getSSLSocketFactory(null,certificates); } /** * 双向认证 * @param keyManagers KeyManager[] * @param certificates 证书的输入流 * @return SSLSocketFactory */ public static SSLSocketFactory getSSLSocketFactory(KeyManager[] keyManagers, 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) { e.printStackTrace(); } } //创建信任管理工厂 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); //初始化信任管理器 trustManagerFactory.init(keyStore); //获取SSL上下文对象 SSLContext sslContext = SSLContext.getInstance("TLS"); //初始化信任管理器 sslContext.init(keyManagers, trustManagerFactory.getTrustManagers(), new SecureRandom()); //获取socket工厂 SSLSocketFactory socketFactory = sslContext.getSocketFactory(); return socketFactory; } catch (Exception e) { e.printStackTrace(); } return null; } }
测试运行,完美解决。
2.信任所有证书(不建议使用)
服务端提供的证书文件,如果服务端更改了需要客户端进行相应的更改,否则无法请求服务端数据。如果想不换证书可以继续访问,可以通过X509TrustManager信任所有的证书,以达到客户端不换证书文件就能获取到服务端数据,但不建议这样使用!!!
/** *信任所有证书(不建议使用) * @return */ public static SSLSocketFactory getAllSSLSocketFactory(){ //创建X509信任管理器 TrustManager[] trustManagers = new TrustManager[]{ new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }; try { //获取SSL上下文对象 SSLContext sslContext = SSLContext.getInstance("TLS"); //初始化无信任管理器 sslContext.init(null, trustManagers, new SecureRandom()); //获取socket工厂 SSLSocketFactory socketFactory = sslContext.getSocketFactory(); return socketFactory; } catch (NoSuchAlgorithmException | KeyManagementException e) { e.printStackTrace(); } return null; }
Glide 直接加载没有权威机构颁发的证书https的URL会直接抛出错误SSLHandshakeException(CertPathValidatorException证书路径验证器异常)
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
看到CertPathValidatorException这个异常,不用多说也知道什么原因导致的,所以我们需要对Glide内部的Okhttp3进行https自签名。
// Glide图片加载库
api 'com.github.bumptech.glide:glide:4.11.0'
// Glide需要处理注解时的依赖库,用于处理注解并生成java类
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
// Glide集成OkHttp时需要使用的库
api 'com.github.bumptech.glide:okhttp3-integration:4.11.0'
//注解处理器帮助我们生成meta-data,无需自行配置 @GlideModule public class OkHttpGlideModule extends AppGlideModule { private SSLSocketFactory mSSLSocketFactory; private X509TrustManager mX509TrustManager; @Override public void registerComponents(Context context, Glide glide, Registry registry) { super.registerComponents(context, glide, registry); replace(registry); } private void replace(Registry registry) { //信任管理器 mX509TrustManager = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }; //构建OkHttpClient.Builder OkHttpClient.Builder builder = new OkHttpClient.Builder(); try { //获取SSL上下文对象 SSLContext sslContext = SSLContext.getInstance("SSL"); //初始化信任管理器 sslContext.init(null, new TrustManager[]{mX509TrustManager}, new SecureRandom()); //获取Socket工厂 mSSLSocketFactory = sslContext.getSocketFactory(); //OkHttp设置Socket工厂 builder.sslSocketFactory(mSSLSocketFactory, mX509TrustManager); } catch (NoSuchAlgorithmException | KeyManagementException e) { e.printStackTrace(); } //构建OkHttpClient OkHttpClient okHttpClient = builder.build(); //替换Glide内部的OkHttpClient registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient)); } }
GlideApp.with(context).load(url).apply(options).into(imageView);
万事俱备,只需测试,运行跑一下,呃呃呃,尴尬了翻车了…可以看到SSLPeerUnverifiedException异常Hostname 172.17.230.133 not verified,逗我玩了,都已经做认证了凭什么不给我通过,经过百度和对这段话的分析,发现subjectAltName是个空值,大概率猜测会不会是这个值导致的。于是搜索发现:
数字证书使用 Subject 子项标明证书的持有者,并且使用 subjectAltName 扩展项对持有者的身份进行更多的标记和界定,在 subjectAltName 中可以包含有关持有者身份的多条信息。如果证书持有者是一个CA,则 Subject 项不能为空,必须包含一个 DN 名,该 DN 名必须与该 CA 颁发的所有证书中 Issuer 项的 DN 名相同。
简单说就是 X.509 数字证书中的扩展字段,可以包括更多的主机信息:IP地址、DNS、Email等。原因就是ssl证书的SAN 这块儿没有配置,必须要通过证书中的SAN扩展字段中拿到IP地址做二次校验,才能够建立连接。
只需要在上面代码中添加hostName进行再次验证
//设置主机名进行再次验证
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
运行测试,完美解决
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。