android 与 https
1.官方文档
android https | https://developer.android.google.cn/training/articles/security-ssl |
android 配置ca | https://developer.android.google.cn/training/articles/security-config 自签名证书配置、network_security_config.xml语法等 |
okhttp 配置ca | https://square.github.io/okhttp/https/#customizing-trusted-certificates-kt-java |
2.android 上使用自签名证书
2.1 生成证书
参考: https://www.cnblogs.com/sjjg/p/15327857.html
2.2 在代码中设置ca示例
- HttpsURLConnection
1 // Load CAs from an InputStream 2 // (could be from a resource or ByteArrayInputStream or ...) 3 val cf: CertificateFactory = CertificateFactory.getInstance("X.509") 4 // From https://www.washington.edu/itconnect/security/ca/load-der.crt 5 val caInput: InputStream = BufferedInputStream(FileInputStream("load-der.crt")) 6 val ca: X509Certificate = caInput.use { 7 cf.generateCertificate(it) as X509Certificate 8 } 9 System.out.println("ca=" + ca.subjectDN) 10 11 // Create a KeyStore containing our trusted CAs 12 val keyStoreType = KeyStore.getDefaultType() 13 val keyStore = KeyStore.getInstance(keyStoreType).apply { 14 load(null, null) 15 setCertificateEntry("ca", ca) 16 } 17 18 // Create a TrustManager that trusts the CAs inputStream our KeyStore 19 val tmfAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm() 20 val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm).apply { 21 init(keyStore) 22 } 23 24 // Create an SSLContext that uses our TrustManager 25 val context: SSLContext = SSLContext.getInstance("TLS").apply { 26 init(null, tmf.trustManagers, null) 27 } 28 29 // Tell the URLConnection to use a SocketFactory from our SSLContext 30 val url = URL("https://certs.cac.washington.edu/CAtest/") 31 val urlConnection = url.openConnection() as HttpsURLConnection 32 urlConnection.sslSocketFactory = context.socketFactory 33 val inputStream: InputStream = urlConnection.inputStream 34 copyInputStreamToOutputStream(inputStream, System.out)
- okhttp
1 private val client: OkHttpClient 2 3 init { 4 val trustManager = trustManagerForCertificates(trustedCertificatesInputStream()) 5 val sslContext = SSLContext.getInstance("TLS") 6 sslContext.init(null, arrayOf<TrustManager>(trustManager), null) 7 val sslSocketFactory = sslContext.socketFactory 8 9 client = OkHttpClient.Builder() 10 .sslSocketFactory(sslSocketFactory, trustManager) 11 .build() 12 } 13 14 fun run() { 15 val request = Request.Builder() 16 .url("https://publicobject.com/helloworld.txt") 17 .build() 18 19 client.newCall(request).execute().use { response -> 20 if (!response.isSuccessful) throw IOException("Unexpected code $response") 21 22 for ((name, value) in response.headers) { 23 println("$name: $value") 24 } 25 26 println(response.body!!.string()) 27 } 28 } 29 30 /** 31 * Returns an input stream containing one or more certificate PEM files. This implementation just 32 * embeds the PEM files in Java strings; most applications will instead read this from a resource 33 * file that gets bundled with the application. 34 */ 35 private fun trustedCertificatesInputStream(): InputStream { 36 ... // Full source omitted. See sample. 37 } 38 39 private fun trustManagerForCertificates(inputStream: InputStream): X509TrustManager { 40 ... // Full source omitted. See sample. 41 }
3.android 应用内配置ca
target <= 23 的应用默认情况下会信任用户添加的 CA ,应用可以在res/xml/network_security_config.xml 内,使用
base-config
(应用范围的自定义)domain-config
(针对每个域名的自定义)
它的配置如下:
1 <base-config cleartextTrafficPermitted="true"> 2 <trust-anchors> 3 <certificates src="system" /> 4 <certificates src="user" /> 5 </trust-anchors> 6 </base-config>
当( 23 < target <= 27) 时,它的默认配置如下:
1 <base-config cleartextTrafficPermitted="true"> 2 <trust-anchors> 3 <certificates src="system" /> 4 </trust-anchors> 5 </base-config>
当 target > 27时,cleartextTrafficPermitted="false" 表示不支持明文传输。
3.1 配置自定义 CA
1 <?xml version="1.0" encoding="utf-8"?> 2 <network-security-config> 3 <domain-config> 4 <domain includeSubdomains="true">example.com</domain> 5 <trust-anchors> 6 <certificates src="@raw/my_ca"/> 7 </trust-anchors> 8 </domain-config> 9 </network-security-config>
证书 以 PEM 或 DER 格式。如果使用 PEM 格式,文件必须仅包含 PEM 数据,且没有额外的文本。
3.2 限制可信CA集
1 <?xml version="1.0" encoding="utf-8"?> 2 <network-security-config> 3 <domain-config> 4 <domain includeSubdomains="true">secure.example.com</domain> 5 <domain includeSubdomains="true">cdn.example.com</domain> 6 <trust-anchors> 7 <certificates src="@raw/trusted_roots1"/> 8 <certificates src="@raw/trusted_roots2"/> 9 ... 10 </trust-anchors> 11 </domain-config> 12 </network-security-config>
3.3 信任其他 CA
1 <?xml version="1.0" encoding="utf-8"?> 2 <network-security-config> 3 <base-config> 4 <trust-anchors> 5 <certificates src="@raw/extracas"/> 6 <certificates src="system"/> 7 </trust-anchors> 8 </base-config> 9 </network-security-config>
3.4 固定证书
一般情况下,应用信任所有预装 CA。如果有预装 CA 颁发了欺诈性证书,则应用将面临被中间人攻击的风险。有些应用选择通过限制信任的 CA 集或通过固定证书来限制其接受的证书集。
如需固定证书,您可以通过按公钥的哈希值(X.509 证书的 SubjectPublicKeyInfo
)提供证书集。然后,只有至少包含一个已固定公钥的证书链才有效。
详见 : https://developer.android.google.cn/training/articles/security-config#CertificatePinning
1 <?xml version="1.0" encoding="utf-8"?> 2 <network-security-config> 3 <domain-config> 4 <domain includeSubdomains="true">example.com</domain> 5 <pin-set expiration="2018-01-01"> 6 <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> 7 <!-- backup pin --> 8 <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin> 9 </pin-set> 10 </domain-config> 11 </network-security-config>
4.安全测试工具
Nogotofai
https://github.com/google/nogotofail