Akka HTTP supports TLS encryption on the server-side as well as on theclient-side.
The central vehicle for configuring encryption is theHttpsConnectionContext, which can be created using the static methodConnectionContext.httpswhich is defined like this:
// ConnectionContext def https(
sslContext: SSLContext,
sslConfig: Option[AkkaSSLConfig] = None, enabledCipherSuites: Option[immutable.Seq[String]] = None, enabledProtocols: Option[immutable.Seq[String]] = None, clientAuth: Option[TLSClientAuth] = None, sslParameters: Option[SSLParameters] = None) =
new HttpsConnectionContext(sslContext, sslConfig, enabledCipherSuites,
˓→enabledProtocols, clientAuth, sslParameters)
On the server-side thebind, andbindAndHandleXXX methods of theakka.http.scaladsl.Httpextension de- fine an optional httpsContextparameter, which can receive the HTTPS configuration in the form of an HttpsContextinstance. If defined encryption is enabled on all accepted connections. Otherwise it is dis- abled (which is the default).
For detailed documentation for client-side HTTPS support refer toClient-Side HTTPS Support.
8.8.1 SSL-Config
Akka HTTP heavily relies on, and delegates most configuration of any SSL/TLS related options toLightbend SSL-Config, which is a library specialized in providing an secure-by-default SSLContext and related options.
Please refer to theLightbend SSL-Configdocumentation for detailed documentation of all available settings.
SSL Config settings used by Akka HTTP (as well as Streaming TCP) are located under theakka.ssl-confignames- pace.
In order to use SSL-Config in Akka so it logs to the right ActorSystem-wise logger etc., theAkkaSSLConfig extension is provided. Obtaining it is as simple as:
implicit val system = ActorSystem() val sslConfig = AkkaSSLConfig()
While typical usage, for example for configuring http client settings would be applied globally by configuring ssl-config inapplication.conf, it’s possible to obtain the extension andcopyit while modifying any con- figuration that you might need to change and then use that specificAkkaSSLConfiginstance while establishing connections be it client or server-side.
8.8.2 Obtaining SSL/TLS Certificates
In order to run an HTTPS server a certificate has to be provided, which usually is either obtained from a signing authority or created by yourself for local or staging environment purposes.
Signing authorities often provide instructions on how to create a Java keystore (typically with reference to Tomcat configuration). If you want to generate your own certificates, the official Oracle documentation on how to generate keystores using the JDK keytool utility can be foundhere.
SSL-Config provides a more targeted guide on generating certificates, so we recommend you start with the guide titledGenerating X.509 Certificates.
8.8. Server-Side HTTPS Support 740
8.8.3 Using HTTPS
Once you have obtained the server certificate, using it is as simple as preparing an HttpsConnectionContext and either setting it as the default one to be used by all servers started by the givenHttpextension or passing it in explicitly when binding the server:
import java.io.InputStream
import java.security.{ SecureRandom, KeyStore }
import javax.net.ssl.{ SSLContext, TrustManagerFactory, KeyManagerFactory } import akka.actor.ActorSystem
import akka.http.scaladsl.server.{ RouteResult, Route, Directives }
import akka.http.scaladsl.{ ConnectionContext, HttpsConnectionContext, Http } import akka.stream.ActorMaterializer
import com.typesafe.sslconfig.akka.AkkaSSLConfig
implicit val system = ActorSystem() implicit val mat = ActorMaterializer() implicit val dispatcher = system.dispatcher // Manual HTTPS configuration
val password: Array[Char] = ??? // do not store passwords in code, read them from
˓→somewhere safe!
val ks: KeyStore = KeyStore.getInstance("PKCS12")
val keystore: InputStream = getClass.getClassLoader.getResourceAsStream("server.p12
˓→")
require(keystore != null, "Keystore required!") ks.load(keystore, password)
val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509") keyManagerFactory.init(ks, password)
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509") tmf.init(ks)
val sslContext: SSLContext = SSLContext.getInstance("TLS")
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new
˓→SecureRandom)
val https: HttpsConnectionContext = ConnectionContext.https(sslContext) Once you configured the HTTPS context, you can set it as default:
// sets default context to HTTPS - all Http() bound servers for this ActorSystem
˓→will use HTTPS from now on
Http().setDefaultServerHttpContext(https)
Http().bindAndHandle(routes, "127.0.0.1", 9090, connectionContext = https) It is also possible to pass in the context to specificbind...(or client) calls, like displayed below:
Http().bind("127.0.0.1", connectionContext = https) // or using the high level routing DSL:
val routes: Route = get { complete("Hello world!") }
Http().bindAndHandle(routes, "127.0.0.1", 8080, connectionContext = https)
8.8. Server-Side HTTPS Support 741
8.8.4 Running both HTTP and HTTPS
If you want to run HTTP and HTTPS servers in a single application, you can callbind... methods twice, one for HTTPS, and the other for HTTP.
When configuring HTTPS, you can do it up like explained in the aboveUsing HTTPSsection, implicit val system = ActorSystem()
implicit val mat = ActorMaterializer() implicit val dispatcher = system.dispatcher // Manual HTTPS configuration
val password: Array[Char] = ??? // do not store passwords in code, read them from
˓→somewhere safe!
val ks: KeyStore = KeyStore.getInstance("PKCS12")
val keystore: InputStream = getClass.getClassLoader.getResourceAsStream("server.p12
˓→")
require(keystore != null, "Keystore required!") ks.load(keystore, password)
val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509") keyManagerFactory.init(ks, password)
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509") tmf.init(ks)
val sslContext: SSLContext = SSLContext.getInstance("TLS")
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new
˓→SecureRandom)
val https: HttpsConnectionContext = ConnectionContext.https(sslContext) or viaSSL-Config(not explained here though).
Then, callbind...methods twice like below. The passedhttpscontext is from the above code snippet.
// you can run both HTTP and HTTPS in the same application as follows:
val commonRoutes: Route = get { complete("Hello world!") }
Http().bindAndHandle(commonRoutes, "127.0.0.1", 443, connectionContext = https) Http().bindAndHandle(commonRoutes, "127.0.0.1", 80)
8.8.5 Further reading
The topic of properly configuring HTTPS for your web server is an always changing one, thus we recommend staying up to date with various security breach news and of course keep your JVM at the latest version possible, as the default settings are often updated by Oracle in reaction to various security updates and known issues.
We also recommend having a look at thePlay documentation about securing your app, as well as the techniques described in the Play documentation about setting up areverse proxy to terminate TLS in front of your application instead of terminating TLS inside the JVM, and therefore Akka HTTP, itself.
Other excellent articles on the subject:
• Oracle Java SE 8: Creating a Keystore using JSSE
• Java PKI Programmer’s Guide
• Fixing X.509 Certificates
8.8. Server-Side HTTPS Support 742