scala - Scala 与 Akka 中的相互身份验证

标签 scala ssl akka tls1.2 mutual-authentication

我将使用 Akka 在 Scala 中创建一个 TLS session ,并在客户端和服务器之间进行相互身份验证。我创建了两个 CA 证书,它们必须信任来自另一部分的相应证书。 您能给我一个如何实现这一点的例子吗? 谢谢。

最佳答案

我创建了一个 github 项目,它演示了与不同类型的客户端(包括 Akka)的相互身份验证。请看这里:https://github.com/Hakky54/mutual-tls-ssl

它包含将 ssl Material 加载到客户端和服务器的完整示例

您需要做的摘要是:

  • 对于客户

    • 创建 key 和证书并将其加载到 keystore 中
    • 导出证书
    • 为可信证书创建单独的 keystore 并导入服务器证书
    • 将两个 keystore 加载到您的 http 客户端
  • 对于服务器

    • 创建 key 和证书并将其加载到 keystore 中
    • 导出证书
    • 为可信证书创建单独的 keystore 并导入客户端证书
    • 将两个 keystore 加载到您的服务器

我不太清楚你使用的是哪种服务器,但如果你使用的是 spring-boot,示例配置将是:

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 需要预先配置的 SSLContext 实例才能配置 HTTPS。下面的代码片段是使用 https 选项创建客户端的示例。

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);
    }
}

有几个库提供了易于使用的实用程序/工厂/构建器类来帮助您创建 SSLContext。

可能还有很多其他库提供类似的功能,但我只知道这三个。顺便说一句,sslcontext-kickstart 是我维护的一个库。

下面概述了加载 keystore 和创建​​ SSLContext 的四种方法。 Vanilla Java 并使用三个库。

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();
    }

}

它是用java编写的,但是IntelliJ Idea在粘贴代码片段时提供了一个方便的到scala的翻译功能。

关于scala - Scala 与 Akka 中的相互身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61868096/

相关文章:

scala - 案例类构造函数的预处理参数而不重复参数列表

ssl - 如何让 gitlab 使用由内部 CA 生成的私有(private)信任证书

tcp - Akka IO TCP 接收数据

scala - 类型别名优化在 Scala 中跳过隐式转换?

java - SBT 在 jar 中包含一个二进制文件

scala - Slick:选项列过滤

Android - HTTP 获取自签名

php - stream_socket_client 错误

scala - Akka Persistence——强制重放

java - 在ask回调中转发Akka actor内部