秒懂Https之如何在Android中使用自签名证书
【版权申明】非商业目的注明出处可*转载
博文地址:https://blog.csdn.net/ShuSheng0007/article/details/107838108
出自:shusheng007
系列文章:
秒懂Https之CA证书与自签名证书漫谈
概述
在秒懂Https之CA证书与自签名证书漫谈中我们谈到了如何生成自签名证书的问题。这篇文章我们看一下,如何在Android中使用生成的自签名证书。
前提
本文基于网络请求库Retrofit2(OkHttp)
使用方式
未来方案
为什么说是未来方案呢?因为只有Android7.0及以上才可以使用,而时至今日一款成熟的App至少要支持到Android5.0,甚至Android4.4。但我们还是要简单的介绍一下,毕竟再过几年人家就是主角了。
从Android7.0(API Level 24)开始Android引入一套网络安全配置的机制,使用这种机制可以非常容易使用自签名证书。使用步骤如下:
- 将自签名证书放到App的
src/main/res/raw
文件下,例如我的证书叫sng_certificate.
。 - 在App的
src/main/res
下建立一个xml
文件夹,在里面创建一个叫network_security_config.xml
的文件,名称可以任意叫,其内容如下
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">你服务器的域名</domain>
<trust-anchors>
<certificates src="@raw/sng_certificate"/>
</trust-anchors>
</domain-config>
</network-security-config>
- 将此文件配置到
AndroidManifest.xml
文件中
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application
android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>
</manifest>
至此即大功告成。
注意: 此种方式要求自签名证书的格式为 PEM 或 DER 。详情可以参考官网 网络安全配置
现实的方案
这个才是本文的重点,毕竟我们的App是要上线的,所以要争取更多的用户。
现在Retrofit基本上已经成为Android网络请求的标配了,而其是基于OkHttp的,所以此处处理证书的问题实际上还是仰赖于OkHttp.
具体通过调用:OkHttpClient.Builder
里的
fun sslSocketFactory(sslSocketFactory: SSLSocketFactory, trustManager: X509TrustManager )
方法。问题进而演变为如何获得这个方法的两个入参的问题,接下来我们就来解决这个问题。
-
将自签名证书放到App的
src/main/res/raw
文件下,例如我的证书叫sng_certificate.crt
。 -
获得第一个参数
SSLSocketFactory
private SSLContext getSslContext(InputStream certificateIs) throws GeneralSecurityException { //从证书文件中读入证书 CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(certificateIs); if (certificates.isEmpty()) { throw new IllegalArgumentException("certificate can not be empty"); } //将读取的证书放入KeyStore, 密码可以为任意值 char[] password = "ss007".toCharArray(); KeyStore keyStore = getEmptyKeyStore(password); int index = 0; for (Certificate certificate : certificates) { String certificateAlias = Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificate); } // 使用 keyStore去构建一个X509信任管理器 KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, password); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); //构建信任的证书管理器构建SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagers, null); return sslContext; } //获取一个空keystore private KeyStore getEmptyKeyStore(char[] password) throws GeneralSecurityException { try { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());//此处使用.crt文件,所以使用getDefaultType即可 InputStream in = null; //'null' creates an empty key store. keyStore.load(in, password); return keyStore; } catch (IOException e) { throw new AssertionError(e); } }
通过以上方法可以获得
SSLContext
,进而获得SSLSocketFactory
,如下所示InputStream is = context.getResources().openRawResource(R.raw.sng_certificate); SSLSocketFactory sslSocketFactory= getSslContext(is).getSocketFactory();
-
获得第二个参数
X509TrustManager
private X509TrustManager getSystemDefaultTrustManager() throws GeneralSecurityException{ TrustManagerFactory trustManagerFactory=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init((KeyStore) null); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); } return (X509TrustManager) trustManagers[0]; }
-
配置OkHttpClient.Builder
//放弃验证证书中的域名信息 builder.hostnameVerifier((hostname, session) -> true); builder.sslSocketFactory(getSslContext(is).getSocketFactory(), getSystemDefaultTrustManager());
注意: 如果你生成证书时包含的域名或者ip地址和你服务器的域名或者ip不匹配时,默认是会报错的,需要使用hostnameVerifier
来放弃验证域名。
经过以上4步就完成了自签名证书在Android中使用的功能。
总结
下一篇我们介绍一下如何在SpringBoot2中使用自签名证书。如果此文帮助到了你,请不要吝啬你的赞美,随手点赞转发,去帮助更多的小伙伴…
生于忧患,死于安乐。——孟子
本文地址:https://blog.csdn.net/ShuSheng0007/article/details/107838108