异常分析
最近测试端反馈,商品详情里某些图片无法展现,后来跟踪查看了一下,发现了如下异常信息,大概类似:javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found
打眼一看就知道大概意思,这是加载https图片时 证书验证出错了,正常情况下我们平台的商品都是上传到阿里云的,没有出过这个异常信息,但是某些商品来源于 第三方平台,图片使用的地址是直接复制的第三方的链接,由于第三方证书是自签名的,所以https证书验证失败,就会出现这个异常信息,现把这个异常总结一下。
解决方案
app使用的网络框架是Retrofit2+OkHttp3,Glide版本是4.9.0,这个搭配默认android识别的开源免费的证书都可以验证通过的,不需要单独去做处理;但是如果要访问自签名证书的网络https URL,就需要去做应对策略了:
一、不考虑安全性:
这里使用kotlin注解处理器插件kapt,在app的Gradle添加配置
apply plugin: 'kotlin-kapt'
kapt 'com.github.bumptech.glide:compiler:4.9.0'
implementation "com.github.bumptech.glide:okhttp3-integration:4.9.0"
自定义GlideModule,使用一个信任所有https证书的OkHttpClient。这里需要说一下,UnsafeOkHttpClient 信任所有https证书,不检验任何hostname,我app里只赋给GIide使用,如果访问服务端资源,比如访问某接口,走的不是这一个client,但是如果不考虑安全性,也可以走同一个,根据实际应用抉择。
@GlideModule
class OkHttpAppGlideModule : AppGlideModule() {
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
val client = UnsafeOkHttpClient.getUnsafeOkHttpClient()
client?.let {
registry.replace(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(client))
}
}
override fun isManifestParsingEnabled(): Boolean {
return false
}
}
object UnsafeOkHttpClient {
fun getUnsafeOkHttpClient(): OkHttpClient? {
return try {
val trustAllCerts = arrayOf<TrustManager>(
object : X509TrustManager {
override fun checkClientTrusted(
chain: Array<out X509Certificate>?,
authType: String?
) {
}
override fun checkServerTrusted(
chain: Array<out X509Certificate>?,
authType: String?
) {
}
override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
}
)
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
val sslSocketFactory = sslContext.socketFactory
val builder = OkHttpClient.Builder()
builder.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
builder.hostnameVerifier(HostnameVerifier { _, _ -> true })
builder.connectTimeout(10, TimeUnit.SECONDS)
builder.readTimeout(20, TimeUnit.SECONDS)
builder.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
二、考虑安全性问题:
如果考虑安全性问题,就需要添加自签名的证书信息,大致代码如下:
private fun getKeyStore(fileName: String): KeyStore? {
var keyStore: KeyStore? = null
var caInput: InputStream? = null
try {
val assetManager: AssetManager = BaseApplication.instance().assets
val cf: CertificateFactory = CertificateFactory.getInstance("X.509")
caInput = assetManager.open(fileName)
val ca: Certificate = cf.generateCertificate(caInput)
val keyStoreType: String = KeyStore.getDefaultType()
keyStore = KeyStore.getInstance(keyStoreType)
keyStore.load(null, null)
keyStore.setCertificateEntry("ca", ca)
} catch (e: Exception) {
LogUtils.e("Error during getting keystore")
e.printStackTrace()
} finally {
caInput?.close()
}
return keyStore
}
private fun loadCertificateFile(fileName: String,mBuilder: OkHttpClient.Builder){
try {
var keyStore: KeyStore? = getKeyStore(fileName)
keyStore?.let {
val sslContext: SSLContext = SSLContext.getInstance("SSL")
val trustManagerFactory: TrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(it)
sslContext.init(null, trustManagerFactory.trustManagers, SecureRandom())
//具体Builder,Client 根据自己需求设置
mBuilder.sslSocketFactory(sslContext.socketFactory,trustManagerFactory.trustManagers[0] as X509TrustManager )
mHttpClient = mBuilder.build() // 此client赋给UnsafeOkHttpClient的getxxOkHttpClient方法,使Glide使用此client去加载图片即可
}
} catch (e: Exception) {
LogUtils.e("Error during creating SslContext for certificate from assets")
e.printStackTrace()
}
}
以上就是针对Glide加载服务端自签名证书异常的大致解决方案,也可以作为访问自签名Https证书异常的参考,总结结束。