Reputation: 31
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
SSLSocketFactory
into 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()
?
Upvotes: 3
Views: 1080
Reputation: 40587
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();
Upvotes: 1