Search code examples
scalasslakkatls1.2mutual-authentication

Mutual Authentication in Scala with Akka


I would create a TLS Session in Scala using Akka with mutual authentication between a client and a server. I have created two CA certificate that have to trust the respective certificates incoming from the other part. Could you give me an exemple of how implement this? Thank you.


Solution

  • I created a github project which demonstrates mutual authentication with different kind of clients, including Akka. Please have a look here: https://github.com/Hakky54/mutual-tls-ssl

    It contains a full example of loading the ssl material into the client and server

    A summary what you need to do is:

    • For the client

      • Create a key and a certificate and load it into a keystore
      • Export the certificate
      • Create a separate keystore for trusted certificates and import server certificate
      • Load the two keystores into your http client
    • For the server

      • Create a key and a certificate and load it into a keystore
      • Export the certificate
      • Create a separate keystore for trusted certificates and import client certificate
      • Load the two keystores into your server

    It is not really clear to me what kind of server you are using, but if you are using spring-boot the example configuration would be:

    server:
      port: 8443
      ssl:
        enabled: true
        key-store: classpath:identity.jks
        key-password: secret
        key-store-password: secret
        trust-store: classpath:truststore.jks
        trust-store-password: secret
        client-auth: need
    

    Akka requires a pre-configured instance of SSLContext to be able to configure HTTPS. An example of creating a client with https options would be the code snippet below.

    import akka.actor.ActorSystem;
    import akka.http.javadsl.ConnectionContext;
    import akka.http.javadsl.Http;
    import akka.http.javadsl.HttpsConnectionContext;
    import com.typesafe.config.ConfigFactory;
    
    import javax.net.ssl.SSLContext;
    import java.util.Optional;
    
    class App {
    
        public static void main(String[] args) {
            ActorSystem actorSystem = ActorSystem.create(
                    App.class.getSimpleName(),
                    ConfigFactory.defaultApplication(App.class.getClassLoader())
            );
    
            SSLContext sslContext = ...; //Initialized SSLContext
    
            Http http = Http.get(actorSystem);
            HttpsConnectionContext httpsContext = ConnectionContext.https(
                    sslContext,
                    Optional.empty(),
                    Optional.empty(),
                    Optional.empty(),
                    Optional.of(sslContext.getDefaultSSLParameters()));
            http.setDefaultClientHttpsContext(httpsContext);
        }
    }
    

    There are couple of libraries which provides easy to use utility/factory/builder classes to help you to create a SSLContext.

    There could be a bunch other libraries which provide similar functionality, but I am only aware of these three. By the way the sslcontext-kickstart is a library which is maintained by me.

    Below is an overview of four ways to load the keystores and create an SSLContext. Vanilla Java and by using the three libraries.

    import io.netty.handler.ssl.SslContextBuilder;
    import nl.altindag.sslcontext.SSLFactory;
    import org.apache.http.ssl.SSLContextBuilder;
    import org.eclipse.jetty.util.ssl.SslContextFactory;
    
    import javax.net.ssl.*;
    import java.io.File;
    import java.io.InputStream;
    import java.security.KeyStore;
    import java.security.SecureRandom;
    import java.util.Objects;
    
    class SslExample {
    
        public static void main(String[] args) throws Exception {
    
            //Traditional flow of creating sslContext
            String keyStorePath = "keystore.p12";
            String trustStorePath = "truststore.p12";
    
            char[] keyStorePassword = "secret".toCharArray();
            char[] trustStorePassword = "secret".toCharArray();
    
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            KeyStore trustStore = KeyStore.getInstance("PKCS12");
    
            try(InputStream keyStoreInputStream = SslExample.class.getClassLoader().getResourceAsStream(keyStorePath);
                InputStream trustStoreInputStream = SslExample.class.getClassLoader().getResourceAsStream(trustStorePath)) {
    
                Objects.requireNonNull(keyStoreInputStream);
                Objects.requireNonNull(trustStoreInputStream);
    
                keyStore.load(keyStoreInputStream, keyStorePassword);
                trustStore.load(trustStoreInputStream, trustStorePassword);
            }
    
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, keyStorePassword);
            KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
    
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(trustStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
    
            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
            sslContext.init(keyManagers, trustManagers, new SecureRandom());
    
            //creating sslContext with Apache SSLContextBuilder
            SSLContext sslContext1 = SSLContextBuilder.create()
                    .loadKeyMaterial(new File("keystore.p12"), "secret".toCharArray(), "secret".toCharArray())
                    .loadTrustMaterial(new File("truststore.p12"), "secret".toCharArray())
                    .build();
    
            //creating sslContext with Jetty SslContextFactory
            SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
            sslContextFactory.setKeyStorePath("keystore.p12");
            sslContextFactory.setKeyStorePassword("secret");
            sslContextFactory.setTrustStorePath("truststore.p12");
            sslContextFactory.setTrustStorePassword("secret");
            sslContextFactory.start();
    
            SSLContext sslContext2 = sslContextFactory.getSslContext();
    
            //creating sslContext with sslcontext-kickstart
            SSLFactory sslFactory = SSLFactory.builder()
                    .withIdentity("keystore.p12", "secret".toCharArray())
                    .withTrustStore("truststore.p12", "secret".toCharArray())
                    .build();
    
            SSLContext sslContext3 = sslFactory.getSslContext();
        }
    
    }
    

    It is in java, but IntelliJ Idea provides a handy translate function to scala when pasting the code snippet.