客戶端校驗(yàn)服務(wù)器的證書野哭,一般有兩種情況:
- 直接校驗(yàn)服務(wù)器下發(fā)的證書挑庶,并與本地的證書作對(duì)比
- 證書鎖定法拿霉,直接利用本地的證書生成TrustManager
PS:這里的證書都是指服務(wù)器的證書(包括CA證書 與 自簽名證書)
- Android安全開(kāi)發(fā)之安全使用HTTPS==(推薦閱讀)==
HttpURLConnection
fun httpUrlConn() {
val url = URL("https://www.baidu.com")
val conn = url.openConnection()
if (conn is HttpsURLConnection) {
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(SslUtils.UnsafeTrustManager()), null)
conn.sslSocketFactory = sslContext.socketFactory
conn.hostnameVerifier = SslUtils.UnsafeHostnameVerifier()
val ins = conn.inputStream
// 通過(guò)流寫入數(shù)據(jù)
}
}
OkHttp
fun okHttpUse() {
OkHttpClient.Builder().sslSocketFactory(SslUtils.getSslContext().socketFactory, SslUtils.UnsafeTrustManager())
.hostnameVerifier(SslUtils.UnsafeHostnameVerifier())
}
實(shí)現(xiàn)代碼及注釋詳解:
package com.stone.templateapp.demo.https
import com.stone.log.Logs
import com.stone.templateapp.App
import okio.Buffer
import org.apache.http.conn.ssl.SSLSocketFactory
import java.io.BufferedInputStream
import java.io.InputStream
import java.security.KeyStore
import java.security.PublicKey
import java.security.cert.Certificate
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.net.ssl.*
/**
* Created By: sqq
* Created Time: 2019-05-15 19:48.
*
* HTTPS 客戶端校驗(yàn)服務(wù)器證書的邏輯筋搏。
*
* 目前還未整理 校驗(yàn)客戶端證書的邏輯 KeyManager
*
*/
object SslUtils {
fun getSslContext(): SSLContext {
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(UnsafeTrustManager()), null)
return sslContext
}
/**
* SSL證書的校驗(yàn)
*
* @param inputStream 預(yù)埋入APP中的服務(wù)器證書的流
*/
fun getTrustManager(inputStream: InputStream): Array<out TrustManager>? {
//以 X.509 格式獲取證書
val cf = CertificateFactory.getInstance("X.509")
val cert = cf.generateCertificate(inputStream)
Logs.d("getTrustManager: 證書公鑰:${cert.publicKey}")
//生成一個(gè)包含服務(wù)端證書的KeyStore
val keyStoreType = KeyStore.getDefaultType()
Logs.d("getTrustManager: keyStoreType: $keyStoreType")
val keyStore = KeyStore.getInstance(keyStoreType)
keyStore.load(null)
keyStore.setCertificateEntry("cert", cert)
//用包含服務(wù)端證書的Keystore生成一個(gè)TrustManager
try {
inputStream.close()
} catch (e: Exception) {
}
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(keyStore)
return trustManagerFactory.trustManagers
}
/**
* 通過(guò)公鑰的字符串生成證書
*
* 備注:當(dāng)不在APP預(yù)埋證書文件時(shí)同云,可以拿到 公鑰 信息糖权,硬編碼進(jìn)代碼中。
*
* 證書公鑰的輸出:
*
* keytool -printcert -rfc -file <證書 file path>
*
* 以 ---------BEIGIN CERTIFICATE----------- 開(kāi)始
* 以 ---------END CERTIFICATE-------------- 結(jié)束
*/
fun getCertificate(publicKey: String): Certificate? {
val certStream = Buffer().writeUtf8(publicKey).inputStream()
//X509Certificate
return CertificateFactory.getInstance("X.509").generateCertificate(certStream)
}
/**
*
*/
fun getTrustManagerPwd(inputStream: InputStream, password: String): Array<out TrustManager>? {
val trustStore = KeyStore.getInstance("BKS")
trustStore.load(inputStream, password.toCharArray())
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(trustStore)
return trustManagerFactory.trustManagers
}
/**
* 弱校驗(yàn)證書(沒(méi)有真正校驗(yàn)證書)炸站,不安全的方式星澳。
*/
class UnsafeTrustManager : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
//用以校驗(yàn)客戶端證書
}
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
//用以校驗(yàn)服務(wù)端證書
SSLSocketFactory.STRICT_HOSTNAME_VERIFIER
}
override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
}
//
/**
* 未校驗(yàn)服務(wù)器證書的域名是否相符
*
* @see SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER Deprecated
*/
class UnsafeHostnameVerifier : HostnameVerifier {
override fun verify(hostname: String?, session: SSLSession?): Boolean {
//未校驗(yàn)服務(wù)器證書的域名是否相符
return true
}
}
/**
* 對(duì)服務(wù)器證書域名進(jìn)行強(qiáng)校驗(yàn)
*/
class StrictHostnameVerifier : HostnameVerifier {
override fun verify(hostname: String?, session: SSLSession?): Boolean {
val hv = HttpsURLConnection.getDefaultHostnameVerifier()
return hv.verify("*.stone.com", session)
}
}
/**
*
* 真正實(shí)現(xiàn) TrustManager#checkServerTrusted 的正確寫法,真正對(duì)服務(wù)器的證書做了強(qiáng)校驗(yàn)
*
* 首先校驗(yàn)服務(wù)器證書的有效性旱易,然后與本地預(yù)埋的證書作對(duì)比禁偎。
*
* 自定義的 TrustManager
*/
class StrictTrustManager : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
//校驗(yàn)客戶端證書
}
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
//校驗(yàn)服務(wù)器證書
if (chain.isNullOrEmpty()) throw IllegalArgumentException("Check Server x509Certificate is null or Empty")
chain.forEach {
//檢查服務(wù)端簽名是否有問(wèn)題
it.checkValidity()
try {
//和APP預(yù)埋的證書做對(duì)比
it.verify(getPublicKey(" cert"))
} catch (e: Exception) {
e.printStackTrace()
}
}
}
override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
}
/**
* 根據(jù)文件名獲取在assets目前下的證書的公鑰(PublicKey)
*/
fun getPublicKey(filename: String): PublicKey? {
val certInput = BufferedInputStream(App.getApp().assets.open(filename))
val certificate = CertificateFactory.getInstance("X.509").generateCertificate(certInput)
return certificate.publicKey
}
}