Incompatible digest error when calling endpoint with client certificate

59 Views Asked by At

Our app requires to authenticate some endpoints with client certificate. We create CSR, call endpoint and receive back client certificate. We have trouble calling secure endpoint that requires this client certificate.

App have BouncyCastle dependency

org.bouncycastle:bcpkix-jdk15to18:1.77.

This is how we proceeed. First, we create keystore

private val keystore: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
        load(
            null,
            null
        )
    }

Then we generate keypair

val keyPairGenerator = KeyPairGenerator.getInstance(
                "EC",
                "AndroidKeyStore"
            )

            val keyGenParameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
                KEY_TAG,
                KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
            ).apply {
                setDigests(KeyProperties.DIGEST_SHA256)
                setKeySize(KEY_256)
                setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) //tried with and without
            }.build()

            keyPairGenerator.initialize(keyGenParameterSpec)
val keyPair = keyPairGenerator.generateKeyPair()

Then we generate certificate signing request

val jcaPKCS10CertificationRequestBuilder = JcaPKCS10CertificationRequestBuilder(
                X500Principal("CN=$COMMON_NAME, O=$ORGANIZATION, C=$COUNTRY"),
                keyPair.public
            )
            val contentSigner = JcaContentSignerBuilder(KEY_ALGORITHM).build(keyPair.private)
            val csr = jcaPKCS10CertificationRequestBuilder.build(contentSigner)

            val stringWriter = StringWriter()
            val pemWriter = PEMWriter(stringWriter)
            pemWriter.writeObject(csr)
            pemWriter.close()
            stringWriter.close()
val csr = stringWriter.toString()

We send this csr to backend and in response we receive signed PEM client certificate.

We save this certifciate:

 val inputStream = input.byteInputStream()
            val certificateFactory = CertificateFactory.getInstance("X.509")
            val certificate = certificateFactory.generateCertificate(inputStream)
            inputStream.close()


val certChain = arrayOf(generatedCertificate as? X509Certificate)
keystore.setKeyEntry(
                KEY_TAG,
                privateKey,
                null,
                certChain
            )

We then load socketFactory

 val keyManagerFactory = KeyManagerFactory.getInstance("X509")
            keyManagerFactory.init(keystore, null)
            val sslContext = SSLContext.getInstance("TLS")
            sslContext.init(keyManagerFactory.keyManagers, null, null)
            Result.success(sslContext.socketFactory)

and trust manager

val trustManagerFactory = TrustManagerFactory.getInstance(
                TrustManagerFactory.getDefaultAlgorithm()
            )
            trustManagerFactory.init(null as KeyStore?)
            val trustManager = trustManagerFactory.trustManagers[0] as X509TrustManager

create Retrofit and OkHttp client

private val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(
            OkHttpClient.Builder().apply {
                protocols(listOf(Protocol.HTTP_1_1)) 
            }.sslSocketFactory(
                socketFactory,
                trustManager
            ).build()
        )
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()

and call endpoint. But we get an error:

--> GET *
--> END GET
Preferred provider doesn't support key:
java.security.InvalidKeyException: Keystore operation failed
    at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1373)
    at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1413)
    at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
    at android.security.keystore.AndroidKeyStoreSignatureSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreSignatureSpiBase.java:219)
    at android.security.keystore.AndroidKeyStoreSignatureSpiBase.engineInitSign(AndroidKeyStoreSignatureSpiBase.java:99)
    at android.security.keystore.AndroidKeyStoreSignatureSpiBase.engineInitSign(AndroidKeyStoreSignatureSpiBase.java:77)
    at java.security.Signature$Delegate.init(Signature.java:1357)
    at java.security.Signature$Delegate.chooseProvider(Signature.java:1310)
    at java.security.Signature$Delegate.engineInitSign(Signature.java:1385)
    at java.security.Signature.initSign(Signature.java:679)
    at com.android.org.conscrypt.CryptoUpcalls.signDigestWithPrivateKey(CryptoUpcalls.java:80)
    at com.android.org.conscrypt.CryptoUpcalls.ecSignDigestWithPrivateKey(CryptoUpcalls.java:68)
    at com.android.org.conscrypt.NativeCrypto.SSL_read(Native Method)
    at com.android.org.conscrypt.NativeSsl.read(NativeSsl.java:411)
    at com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream.read(ConscryptFileDescriptorSocket.java:549)
    at okio.InputStreamSource.read(JvmOkio.kt:93)
    at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:128)
    at okio.RealBufferedSource.indexOf(RealBufferedSource.kt:430)
    at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.kt:323)
    at okhttp3.internal.http1.HeadersReader.readLine(HeadersReader.kt:29)
    at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(Http1ExchangeCodec.kt:180)
    at okhttp3.internal.connection.Exchange.readResponseHeaders(Exchange.kt:110)
    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.kt:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:34)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.kt:221)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
    at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:919)
Caused by: android.security.KeyStoreException: Incompatible digest
    at android.security.KeyStore.getKeyStoreException(KeyStore.java:1303)
    ... 38 more
Could not find provider for algorithm: NONEwithECDSA
Could not sign message in EcdsaMethodDoSign!
<-- HTTP FAILED: javax.net.ssl.SSLException: Read error: ssl=0x79fcd80608: I/O error during system call, Success
---> javax.net.ssl.SSLException: Read error: ssl=0x79fcd80608: I/O error during system call, Success
 
0

There are 0 best solutions below