3

I have some Python code that makes a HTTP request:

import requests

response = requests.get(
  url,
  cert = tuple(clientCertPath, pkeyPath), // paths to crt.pem and pkey.pem
  verify = serverCertPath // path to server-ca.crt file
)

I'd like to rewrite this to Kotlin using ktor. This is what I've come up with so far:

val serverCert = serverCertPath.inputStream().use {
  CertificateFactory.getInstance("X.509").generateCertificate(it) as X509Certificate
}
val keyStore = KeyStore.getInstance(...).apply { 
  load(null, null)
  setCertificateEntry("serverCert", serverCert)
}
val trustManagerFactory = ... // init with keystore
val sslContext = SSLContext.getInstance("TLS") // and init with above config
val client = HttpClient(Java) {
  engine {
    config {
      sslContext(sslContext)
    }
  }
}

// So far so good. This server certificate config seems to work and cover the 'verify' parameter. Now for the other cert.

val clientCert = CertificateFactory.getInstance("X.509").let {
  clientCertPath.inputStream().use { stream -> it.generateCertificate(stream) as X509Certificate }
}

client.request(url) {
  this.method = HttpMethod.Get
  // how to supply client cert?
}

And now I'm stuck. How do I apply the client certificate to the request? Either client-level configuration or request-level configuration would be fine with me. Also, I still haven't used the pkeyPath. Where do I do that?

Jorn
  • 20,612
  • 18
  • 79
  • 126
  • I'm not sure about the close vote. This clearly is one problem (making a request with a client certificate using ktor) – Jorn Aug 24 '23 at 14:34
  • 1
    I have it working for quite some time, here is an example configuration which I use: https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KtorJavaHttpClientService.kt and an example for using pem files to create a sslfactory: https://github.com/Hakky54/sslcontext-kickstart#using-pem-files I need to tell you that this is my library which you can get from maven central but also as a gradle dependency – Hakan54 Aug 24 '23 at 17:30
  • Thanks for the suggestion, @Hakan54! The library looks like it'll make this much easier. If anyone else is looking for this, you'll need `io.github.hakky54:sslcontext-kickstart-for-pem` (current latest version 8.1.5) – Jorn Aug 25 '23 at 07:21
  • @Hakan54 Your library is working well! If you want to add an answer, I'll be happy to accept it. – Jorn Aug 25 '23 at 08:44

1 Answers1

1

There are couple of ways to load pem files and creating a sslcontext out of it. However it is a bit verbose with traditional java. If you just want to go with that without the need for additional libraries, you can give the following stackoverflow topic a try How to build a SSLSocketFactory from PEM certificate and key without converting to keystore?

Or else you can try my library which is a bit less verbose. What you need to do is load your pem files and create a key manager and trust manager which you can use to create a sslcontext. Ktor will be able to use this object. An example code snippet would be:

var keyManager = PemUtils.loadIdentityMaterial("certificate-chain.pem", "private-key.pem");
var trustManager = PemUtils.loadTrustMaterial("some-trusted-certificate.pem");

var sslFactory = SSLFactory.builder()
          .withIdentityMaterial(keyManager)
          .withTrustMaterial(trustManager)
          .build();

var sslContext = sslFactory.getSslContext();

You can add the library with the following snippet to your project:

implementation("io.github.hakky54:sslcontext-kickstart-for-pem:8.1.5")
Hakan54
  • 3,121
  • 1
  • 23
  • 37
  • Note that you'll need the separate artifact `io.github.hakky54:sslcontext-kickstart-for-pem` for the `PemUtils` class – Jorn Aug 26 '23 at 05:09