6

I am having some trouble configuring Akka HTTP to use specific SSL certificates when making HTTP requests from Scala. I can make non-SSL-based requests without hiccups, but cannot seem to find proper examples on how to configure Akka for use with a set of certificates.

Sadly, the documentation on Akka HTTP's website gives detailed information on server-side SSL configuration, but no concrete example is given on client-side configuration.

Any examples of such usage would be much appreciated.

Jeffrey Chung
  • 19,319
  • 8
  • 34
  • 54

1 Answers1

1

I have done this in a small project of mine and I think could be useful to somenie even the question is old. You will have to create your keystore.pkcs12 file. My build.sbt I am using:

val akkaVersion = "2.6.10"
 val akkaHttpVersion = "10.2.2"
  "com.typesafe.akka" %% "akka-actor" % akkaVersion,
  "com.typesafe.akka" %% "akka-http" % akkaHttpVersion,
  "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpVersion,
  "com.typesafe.akka" %% "akka-http-testkit" % akkaHttpVersion,
  "com.typesafe.akka" %% "akka-http2-support" % akkaHttpVersion

I have the HttpsServerContext object that I can use in any Akka-http api in my project. I use by calling enableHttps(HttpsServerContext.httpsConnectionContext).

object HttpsServerContext {
  // Step 1 - key store
  val ks: KeyStore = KeyStore.getInstance("PKCS12")
  val keystoreFile: InputStream = getClass.getClassLoader.getResourceAsStream("certs/keystore.pkcs12")
  // alternative: new FileInputStream(new File("src/main/resources/certs/keystore.pkcs12"))
  val password = "akka-https".toCharArray // TODO: fetch the password from a secure place
  ks.load(keystoreFile, password)

  // Step 2 - initialize a key manager
  val keyManagerFactory = KeyManagerFactory.getInstance("SunX509") // PKI public key infrastructure
  keyManagerFactory.init(ks, password)

  // Step 3 - initialize a trust manager
  val trustmanagerFactory = TrustManagerFactory.getInstance("SunX509")
  trustmanagerFactory.init(ks)

  // Step 4 - initialize a SSL context
  val sslContext: SSLContext = SSLContext.getInstance("TLS")
  sslContext.init(keyManagerFactory.getKeyManagers, trustmanagerFactory.getTrustManagers, new SecureRandom())

  // Step 5 - return the HTTPS connection context
  val httpsConnectionContext: HttpsConnectionContext = ConnectionContext.httpsServer(sslContext)
}

Then I have an async request handler

implicit val system = ActorSystem("HttpsRestApi")

import system.dispatcher
    val asyncRequestHandler: HttpRequest => Future[HttpResponse] = {
      case HttpRequest(HttpMethods.GET, Uri.Path("/home"), headers, entity, protocol) =>
        Future(HttpResponse(
          StatusCodes.OK, // HTTP 200
          entity = HttpEntity(
            ContentTypes.`text/html(UTF-8)`,
            """
              |<html>
              | <body>
              |  Async Hello Akka HTTPS
              | </body>
              |</html>
              |""".stripMargin)
        ))
      case HttpRequest(HttpMethods.GET, Uri.Path("/redirect"), headers, entity, protocol) =>
        Future(HttpResponse(
          StatusCodes.Found,
          headers = List(Location("http://www.google.com"))
        ))
      case request: HttpRequest =>
        request.discardEntityBytes()
        Future(HttpResponse(
          StatusCodes.NotFound, // HTTP 404
          entity = HttpEntity(
            ContentTypes.`text/html(UTF-8)`,
            """
              |<html>
              | <body>
              |  OOPS! async page not found =(<br>try https://localhost:8553/home
              | </body>
              |</html>
              |""".stripMargin)
        ))
    }
    val httpsBindingAsync = Http()
      .newServerAt("localhost", 8443)
      .enableHttps(HttpsServerContext.httpsConnectionContext)
      .bind(asyncRequestHandler)
Felipe
  • 7,013
  • 8
  • 44
  • 102