ktor client https request with self-signed certificate

3

I have a Ktor server application (rest api) running with a self signed certificate.

It works fine from a browser (after a warning and a confirmation) port 80 is redirected to 8443.

But if I try this from a Ktor Apache Client:

fun main(args: Array<String>) = runBlocking {

    val client = HttpClient(Apache) {
        install(JsonFeature) {
            serializer = GsonSerializer()
        }
    }

    val job = GlobalScope.launch {
        try {
            //self-signed certificate
            val resultWillFail = client.get<String>("https://10.0.0.11:8443/get-my-services")
            println("${resultWillFail}")
            val resultOk = client.get<String>("https://en.wikipedia.org/wiki/Main_Page") //ok
            println("${resultOk}")
        } catch (e: Exception) {
            println("Error: ${e.message}")
        }
    }

    job.join()
}

My request to https://10.0.0.11:8443/get-my-services will fail:

Error: General SSLEngine problem

I also tried the same using curl:

curl: (77) schannel: next InitializeSecurityContext failed: SEC_E_UNTRUSTED_ROOT (0x80090325) - The certificate chain was issued by an authority that is not trusted.

So my question would be: How to use a self signed certificate with ktor client (Apache) ?

Thanks, J

kotlin
ktor
asked on Stack Overflow Dec 12, 2018 by Jay Yanez • edited Dec 12, 2018 by lda573

2 Answers

10

Based on Erik answer this is how I made Ktor Apache Client to accept a self-signed certificate:

fun main(args: Array<String>) = runBlocking {
    val client = HttpClient(Apache) {
        install(JsonFeature) {
            serializer = GsonSerializer()
        }
        engine {
            customizeClient {
                setSSLContext(
                    SSLContextBuilder
                        .create()
                        .loadTrustMaterial(TrustSelfSignedStrategy())
                        .build()
                )
                setSSLHostnameVerifier(NoopHostnameVerifier())
            }
        }
    }

    val job = GlobalScope.launch {
        try {
            val sslGetResult = client.get<String>("https://10.0.0.11:8443/get-my-services")
            println("${sslGetResult}")
        } catch (e: Exception) {
            println("Error: ${e.message}")
        }
    }

    job.join()
}
answered on Stack Overflow Dec 12, 2018 by Jay Yanez
7

You need to configure Apache HttpClient to ignore self signed certificates, and pass that to KTor. Based on the information here, you can ignore self signed certificate validation with the following code:

// use the TrustSelfSignedStrategy to allow Self Signed Certificates
val sslContext = SSLContextBuilder
        .create()
        .loadTrustMaterial(TrustSelfSignedStrategy())
        .build()

val allowAllHosts = NoopHostnameVerifier()
val connectionFactory = SSLConnectionSocketFactory(sslContext, allowAllHosts)

val client = HttpClients
        .custom()
        .setSSLSocketFactory(connectionFactory)
        .build() 

The last thing you have to do is to use the client in your KTor code. I haven't tried that myself yet, but let me see know how you go.

answered on Stack Overflow Dec 12, 2018 by Erik Pragt

User contributions licensed under CC BY-SA 3.0