Search code examples
androidkotlinokhttpx509pki

How to call the default X509KeyManager when creating a wrapper around a X509KeyManager


I am trying to implement a wrapper around a X509KeyManager to execute other code inside the callbacks and then call the the default KeyManager after but this isn't working.

Here's my simplified code:

class MyKeyManager(): X509ExtendedKeyManager() {
  private val defaultKeyManager: X509ExtendedKeyManager

  init {
    val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())

    keyManagerFactory.init(null, null)
    val keyManagers = keyManagerFactory.keyManagers
    check(keyManagers.size == 1 && keyManagers[0] is X509ExtendedKeyManager) {
      "Error Creating KeyManager"
    }
    defaultKeyManager = keyManagers[0] as X509ExtendedKeyManager
  }

  override fun getClientAliases(keyType: String?, issuers: Array<out Principal>?): Array<String> {
    return defaultKeyManager.getClientAliases(keyType, issuers)
  }

  override fun chooseClientAlias(keyType: Array<out String>?, issuers: Array<out Principal>?, socket: Socket?): String {
// execute code
    return defaultKeyManager.chooseClientAlias(keyType, issuers, socket)
  }

  override fun getServerAliases(keyType: String?, issuers: Array<out Principal>?): Array<String> {
    return defaultKeyManager.getServerAliases(keyType, issuers)
  }

  override fun chooseServerAlias(keyType: String?, issuers: Array<out Principal>?, socket: Socket?): String {
    return defaultKeyManager.chooseServerAlias(keyType, issuers, socket)
  }

  override fun getCertificateChain(alias: String?): Array<X509Certificate> {
// execute code
    return defaultKeyManager.getCertificateChain(alias)
  }

  override fun getPrivateKey(alias: String?): PrivateKey {
// execute code
    return defaultKeyManager.getPrivateKey(alias)
  }

  override fun chooseEngineClientAlias(keyType: Array<out String>?, issuers: Array<out Principal>?, engine: SSLEngine?): String {
    return defaultKeyManager.chooseEngineClientAlias(keyType, issuers, engine)
  }

  override fun chooseEngineServerAlias(keyType: String?, issuers: Array<out Principal>?, engine: SSLEngine?): String {
    return defaultKeyManager.chooseEngineServerAlias(keyType, issuers, engine)
  }
}

and when I run my app I get this exception:

E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
    Process: com.example.mytestapp1, PID: 20624
    java.lang.Error: javax.net.ssl.SSLHandshakeException: defaultKeyManager.choose…keyType, issuers, socket) must not be null
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1173)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:920)
     Caused by: javax.net.ssl.SSLHandshakeException: defaultKeyManager.choose…keyType, issuers, socket) must not be null
        at com.android.org.conscrypt.SSLUtils.toSSLHandshakeException(SSLUtils.java:363)
        at com.android.org.conscrypt.ConscryptEngineSocket.doHandshake(ConscryptEngineSocket.java:285)
        at com.android.org.conscrypt.ConscryptEngineSocket.access$1000(ConscryptEngineSocket.java:54)
        at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.renegotiate(ConscryptEngineSocket.java:929)
        at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.processDataFromSocket(ConscryptEngineSocket.java:884)
        at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.readUntilDataAvailable(ConscryptEngineSocket.java:824)
        at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.read(ConscryptEngineSocket.java:797)
...

I am using OkHttp to run this code and I create my Client like this:

      val keyManager = MyKeyManager()
      sslContext.init(arrayOf<X509KeyManager>(keyManager), TrustAllTrusManager, null)
      val builder = OkHttpClient.Builder().protocols(listOf(Protocol.HTTP_1_1))
        .sslSocketFactory(sslContext.socketFactory, TrustAllTrusManager[0])

Does anyone anyone know if this is the correct way of creating the default X509KeyManager or why this exception is thrown?


Solution

  • Turns out that the exception was thrown because the methods overwritten from x509ExtendedKeyManager needed to be marked as nullable. All of these methods in the java interface can return null and I wasn't making them nullable when implementing them in Kotlin