'How do you apply multiple client certificate dynamically to OkHttp Client without rebuilding the client
I have an Android application that should be able to connect to multiple servers with different client certificates respectively.
What is the proper way to dynamically add multiple client certificates to OkHttp client?
Currently, when I apply a new Certificate I am calling this custom method:
private fun applyCertificate(certificatePath: String, password: String) {
// load certificate into KeyStore
FileInputStream(path).use {
clientKeyStore.load(it, password.toCharArray())
}
// load KeyStore into KeyManagerFactory
val keyManagerFactory = KeyManagerFactory.getInstance("X509")
keyManagerFactory.init(clientKeyStore, null)
// Initialize the SSL Context
sslContext.init(keyManagerFactory.keyManagers, tmf.trustManagers, SecureRandom())
// call newBuilder and set new socketFactory
okHttpClient = okHttpClient.newBuilder()
.sslSocketFactory(sslContext.socketFactory, tmf.trustManagers[0] as X509TrustManager)
.build()
}
My concern is
- When I add multiple certificates dynamically, I will be setting a new
SSLSocketFactoryinto the the OkHttpClient multiple times, is this safe to do?
I am not sure if there are better ways of doing this without rebuilding the OkHttpClient.
I did notice that there's the library okhttp-tls, is this a better library for this kind of configuration?
Edit:
Just to clarify,
I am initializing a global instance of okHttpClient when I create our application. This global instance may be used by multiple application components simultaneously for the lifetime of the application.
fun initialSetup() {
okHttpClient = OkHttpClient.Builder().build()
...
}
then I get an a secured request that needs Certificate authentication:
val call = okHttpClient.newCall(
Request.Builder().url("https://pki.service.com/resource1")
.build()
)
val httpResponse = call.execute()
....
if (httpResponse.code == HTTP_CERTIFICATE_AUTHENTICATION) {
val certificate = getCertificatePathForServer()
httpClient.applyCertificate(certificate.path, certificate.password)
...
// retry call
}
and applyCertificate() looks like I explained above:
private fun applyCertificate(certificatePath: String, password: String) {
// load certificate into KeyStore
FileInputStream(path).use {
clientKeyStore.load(it, password.toCharArray())
}
// load KeyStore into KeyManagerFactory
val keyManagerFactory = KeyManagerFactory.getInstance("X509")
keyManagerFactory.init(clientKeyStore, null)
// Initialize the SSL Context
sslContext.init(keyManagerFactory.keyManagers, tmf.trustManagers, SecureRandom())
// call newBuilder and set new socketFactory
okHttpClient = okHttpClient.newBuilder()
.sslSocketFactory(sslContext.socketFactory, tmf.trustManagers[0] as X509TrustManager)
.build()
}
My corcern is that as I am rebulding the OkHttpClient everytime I add a new certificate, components that currently use the global OkHttpClient instance might see undesired side effects, for example pending requests not completing or worse, crashing. Is it safe to rebuild an OkHttpClient instance that may currently be in use with okHttpClient.newBuilder()?
Solution 1:[1]
I did notice that there's the library okhttp-tls, is this a better library for this kind of configuration?
Yes, that library is well suited to this.
HandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()
.addTrustedCertificate(...))
.build();
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())
.build();
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Jesse Wilson |
