Android 7.0(Nougat)SSL握手信任锚缺失问题解决方案咨询
解决Android 7.0上Retrofit请求Railway后端的证书验证错误
问题根源
Android 7.0(API 24)及更早版本的系统信任存储未包含Railway平台SSL证书依赖的现代根CA证书,导致证书链验证失败,抛出java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.。
解决方案
方案1:添加根证书到应用信任存储(推荐,生产环境可用)
将Railway域名对应的根证书打包到应用中,让Retrofit使用自定义信任管理器完成验证:
- 获取根证书:用浏览器访问你的后端接口,查看证书详情,导出根证书(格式选PEM,重命名为
ca.crt),放到app/src/main/res/raw目录。 - 实现自定义信任管理器:
import android.content.Context import java.io.InputStream import java.security.KeyStore import java.security.cert.CertificateFactory import javax.net.ssl.SSLContext import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager fun getCustomTrustManager(context: Context): X509TrustManager { val certFactory = CertificateFactory.getInstance("X.509") val certificate = context.resources.openRawResource(R.raw.ca).use { stream -> certFactory.generateCertificate(stream) } val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply { load(null, null) setCertificateEntry("railway_root_ca", certificate) } val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply { init(keyStore) } return trustManagerFactory.trustManagers.first() as X509TrustManager } fun getCustomSSLSocketFactory(context: Context): javax.net.ssl.SSLSocketFactory { val trustManager = getCustomTrustManager(context) val sslContext = SSLContext.getInstance("TLS").apply { init(null, arrayOf(trustManager), java.security.SecureRandom()) } return sslContext.socketFactory }
- 配置Retrofit使用自定义客户端:
val okHttpClient = OkHttpClient.Builder() .sslSocketFactory(getCustomSSLSocketFactory(context), getCustomTrustManager(context)) .build() val retrofit = Retrofit.Builder() .baseUrl("你的后端BaseUrl") .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build()
方案2:兼容TLS协议配置(辅助方案)
Android 7.0默认对TLS 1.3支持有限,若后端强制使用TLS 1.3,可通过OkHttp配置兼容:
val okHttpClient = OkHttpClient.Builder() .connectionSpecs(listOf( ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS )) .build()
此方案需配合方案1使用,单独使用可能无法解决根证书缺失问题。
方案3:禁用证书验证(仅测试环境使用)
仅用于临时测试,生产环境绝对禁止(存在中间人攻击风险):
fun getUnsafeOkHttpClient(): OkHttpClient { return try { val trustAllCerts = arrayOf<X509TrustManager>(object : X509TrustManager { override fun checkClientTrusted(chain: Array<out java.security.cert.X509Certificate>?, authType: String?) = Unit override fun checkServerTrusted(chain: Array<out java.security.cert.X509Certificate>?, authType: String?) = Unit override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> = emptyArray() }) val sslContext = SSLContext.getInstance("TLS").apply { init(null, trustAllCerts, java.security.SecureRandom()) } OkHttpClient.Builder() .sslSocketFactory(sslContext.socketFactory, trustAllCerts[0]) .hostnameVerifier { _, _ -> true } .build() } catch (e: Exception) { throw RuntimeException(e) } }
内容的提问来源于stack exchange,提问作者AdrianIK
相关产品推荐
相关产品推荐

