如何结合Retrofit与Hilt动态提供CA证书/SSLContext?
无需重建Retrofit/OkHttpClient实现动态证书切换的方案
你的核心诉求是避免重建Retrofit实例,同时支持动态切换基站的证书(SSLContext),结合你已经实现的动态baseUrl拦截器,可以通过动态代理OkHttp的SSLSocketFactory来实现,核心思路是让OkHttp在每次请求时使用最新的SSLContext,而不是依赖不可变的OkHttpClient实例。
具体实现步骤
1. 定义动态SSLContext持有者
创建一个线程安全的单例类,用于存储当前基站的SSLContext和对应的TrustManager,方便后续动态更新:
object DynamicSSLContextHolder { @Volatile var currentSSLContext: SSLContext? = null @Volatile var currentTrustManager: X509TrustManager? = null fun updateSSLContext(sslContext: SSLContext, trustManager: X509TrustManager) { currentSSLContext = sslContext currentTrustManager = trustManager } }
2. 实现动态SSLSocketFactory
自定义SSLSocketFactory,每次创建Socket时读取DynamicSSLContextHolder中的最新SSLContext,替代OkHttp默认的静态SSLSocketFactory:
class DynamicSSLSocketFactory : SSLSocketFactory() { private val defaultFactory = SSLContext.getDefault().socketFactory private val currentFactory: SSLSocketFactory get() = DynamicSSLContextHolder.currentSSLContext?.socketFactory ?: defaultFactory override fun createSocket(): Socket = currentFactory.createSocket() override fun createSocket(s: Socket?, host: String?, port: Int, autoClose: Boolean): Socket = currentFactory.createSocket(s, host, port, autoClose) override fun createSocket(host: String?, port: Int): Socket = currentFactory.createSocket(host, port) override fun createSocket(host: String?, port: Int, localHost: InetAddress?, localPort: Int): Socket = currentFactory.createSocket(host, port, localHost, localPort) override fun createSocket(host: InetAddress?, port: Int): Socket = currentFactory.createSocket(host, port) override fun createSocket(address: InetAddress?, port: Int, localAddress: InetAddress?, localPort: Int): Socket = currentFactory.createSocket(address, port, localAddress, localPort) override fun getDefaultCipherSuites(): Array<String> = currentFactory.defaultCipherSuites override fun getSupportedCipherSuites(): Array<String> = currentFactory.supportedCipherSuites }
3. 实现动态TrustManager
同样需要一个动态的TrustManager,代理到当前基站的TrustManager,确保证书验证逻辑随基站切换更新:
class DynamicTrustManager : X509TrustManager { override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) { DynamicSSLContextHolder.currentTrustManager?.checkClientTrusted(chain, authType) } override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) { DynamicSSLContextHolder.currentTrustManager?.checkServerTrusted(chain, authType) } override fun getAcceptedIssuers(): Array<X509Certificate> { return DynamicSSLContextHolder.currentTrustManager?.acceptedIssuers ?: emptyArray() } }
4. Hilt中配置OkHttpClient和Retrofit
在Hilt模块中注入上述动态组件,确保OkHttpClient和Retrofit只初始化一次:
@Module @InstallIn(SingletonComponent::class) object NetworkModule { @Provides @Singleton fun provideDynamicSSLSocketFactory(): SSLSocketFactory = DynamicSSLSocketFactory() @Provides @Singleton fun provideDynamicTrustManager(): X509TrustManager = DynamicTrustManager() @Provides @Singleton fun provideBaseUrlInterceptor(): BaseUrlInterceptor = BaseUrlInterceptor() @Provides @Singleton fun provideOkHttpClient( sslSocketFactory: SSLSocketFactory, trustManager: X509TrustManager, baseUrlInterceptor: BaseUrlInterceptor ): OkHttpClient { return OkHttpClient.Builder() .sslSocketFactory(sslSocketFactory, trustManager) .addInterceptor(baseUrlInterceptor) // 其他通用配置:超时、日志拦截器等 .build() } @Provides @Singleton fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { return Retrofit.Builder() .baseUrl("https://default-base-url.com") // 默认值,实际由拦截器替换 .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build() } }
5. 切换基站时更新配置
当用户选择新基站后,生成对应基站的SSLContext和TrustManager,调用DynamicSSLContextHolder.updateSSLContext(),同时更新baseUrl拦截器的当前baseUrl:
// 示例:从基站证书文件生成SSLContext fun switchBaseStation(newBaseUrl: String, certificateInputStream: InputStream) { // 1. 更新baseUrl val baseUrlInterceptor = // 通过Hilt注入获取实例 baseUrlInterceptor.currentBaseUrl = newBaseUrl // 2. 生成并更新SSLContext val certificateFactory = CertificateFactory.getInstance("X.509") val certificate = certificateFactory.generateCertificate(certificateInputStream) val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()) keyStore.load(null, null) keyStore.setCertificateEntry("base-station-cert", certificate) val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) trustManagerFactory.init(keyStore) val trustManager = trustManagerFactory.trustManagers.first() as X509TrustManager val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, arrayOf(trustManager), SecureRandom()) DynamicSSLContextHolder.updateSSLContext(sslContext, trustManager) }
关键优势
- 无需重建OkHttpClient或Retrofit实例,复用原有单例对象,避免不必要的资源消耗
- 动态baseUrl和动态证书切换逻辑完全解耦,各自独立维护
- 线程安全:通过
@Volatile保证多线程环境下的配置可见性
内容的提问来源于stack exchange,提问作者Synth
相关产品推荐
相关产品推荐

