Erick
Erick

Reputation: 31

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

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

Answers (1)

Jesse Wilson
Jesse Wilson

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

Related Questions