3
votes

I have a grpc-js server using self signed ssl certificates.

var credentials = grpc.ServerCredentials.createSsl(
    fs.readFileSync('./node/grpc/ssl/ca.crt'), 
    [{
        cert_chain: fs.readFileSync('./node/grpc/ssl/server.crt'),
        private_key: fs.readFileSync('./node/grpc/ssl/server.key')
    }], 
    true
);

I then tested this setup with a grpc-js client with the following credential setup and this works.

var credentials = grpc.credentials.createSsl(
    fs.readFileSync('./node/grpc/ssl/ca.crt'),
    fs.readFileSync('./node/grpc/ssl/client.key'),
    fs.readFileSync('./node/grpc/ssl/client.crt')
);

I want to replicate this in Android using OkHttpChannelBuilder but it is a bit more complicated. This is what I have so far.

private val mChannel : ManagedChannel
init {
    /**
     * Server certificate to make it trusted.
     */
    val serverCrtFile = applicationContext.resources.openRawResource(R.raw.server)
    val serverCertificate: X509Certificate =
        CertificateFactory.getInstance("X.509").generateCertificate(serverCrtFile) as X509Certificate

    val caKeyStore: KeyStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
        load(null, null)
        setCertificateEntry("server", serverCertificate)
    }

    val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply {
        init(caKeyStore)
    }

    val sslContext = SSLContext.getInstance("TLS").apply {
        init(null, trustManagerFactory.trustManagers, null)
    }

    mChannel = OkHttpChannelBuilder
        .forAddress(BuildConfig.GRPC_HOST_ADDRESS, BuildConfig.GRPC_HOST_PORT)
        .sslSocketFactory(sslContext.socketFactory)
        .keepAliveTime(10, TimeUnit.SECONDS)
        .useTransportSecurity()
        .keepAliveWithoutCalls(true)
        .build()
}

Everything worked before implementing ssl (so using plaintext() on the channel builder).

The error I get now is io.grpc.StatusRuntimeException: UNAVAILABLE: End of stream or IOException. Can someone please tell me if I am doing something wrong and how I can get a successful connection like between the js server and client.

1
So I can set the third parameter on the server's grpc.ServerCredentials.createSsl() function to false. This parameter specifies if the client certificate needs to be verified by the server. I would still like some help on how to setup the client certificate on Android, please.Daniel
Do you have a full stacktrace? Also, does the server require mutual TLS? Looks it does as the grpc-js client also takes in client cert/key. In that case, you would need two KeyStores, one for client cert/key and one for CA cert. Then create a KeyManagerFactory inited with the KeyStore for client cert/key. Finally, init the SSLContext with KeyManagers getting from the factory. github.com/grpc/grpc-java/issues/6374#issuecomment-550166965 contains the complete code snippet.voidzcy

1 Answers

0
votes

Looks like the SSL handshake failed on the server side so it will be helpful to get server side detailed logs to see what went wrong.

One possibility is using KeyStore.getInstance. Can you try using "PKCS12"?

 KeyStore.getInstance("PKCS12")