简体   繁体   English

具有非HTTP TCP服务器的Lightbend ssl-config

[英]Lightbend ssl-config with a non-http TCP Server

I've found several resources that provide details on configuring ssl-config options within the application.conf file and I've identified how to access these configurations using AkkaSSLConfig.get(). 我发现了一些资源,这些资源提供了有关在application.conf文件中配置ssl-config选项的详细信息,并且确定了如何使用AkkaSSLConfig.get()访问这些配置。 I've seen that an https context can be created using a AkkaSSLConfig object as a parameter to ConnectionContext.https(). 我已经看到可以使用AkkaSSLConfig对象作为ConnectionContext.https()的参数来创建https上下文。

Is it possible to use this for non-http servers? 是否可以将其用于非HTTP服务器? Is the context returned somehow specific to http? 返回的上下文是否以某种方式特定于http? I'm trying to take advantage of ssl-config but it isn't clear to me that it provides any advantages for non-http servers and I don't see any convenient way of building a context from the ssl-config definition, in which case it seems I may as well define the context manually. 我正在尝试利用ssl-config的优势,但我不清楚它是否可以为非http服务器提供任何优势,而且我看不到任何从ssl-config定义中构建上下文的便捷方法,在这种情况下,我似乎也可以手动定义上下文。

Lastly, any examples of building the context for non-http servers are difficult to find. 最后,很难找到为非http服务器构建上下文的任何示例。 It seems the process may be the same as for http servers, but I'm finding that examples often include the use of classes/methods that have 'http' in the name. 看来该过程可能与http服务器相同,但是我发现示例经常包括使用名称中带有“ http”的类/方法。 If anyone knows of a good example I'd be very appreciative. 如果有人知道一个好榜样,我将不胜感激。

import java.io.{File, FileInputStream}
import java.security.{KeyStore, SecureRandom}
import akka.actor.ActorSystem
import akka.http.scaladsl.Http.ServerBinding
import akka.http.scaladsl.model.{HttpResponse, StatusCodes}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.server.Directives.pathSingleSlash
import akka.http.scaladsl.{ConnectionContext, Http}
import akka.stream.{ActorMaterializer, TLSClientAuth}
import com.typesafe.sslconfig.akka.AkkaSSLConfig
import com.typesafe.sslconfig.ssl.{KeyManagerConfig, KeyManagerFactoryWrapper, KeyStoreConfig, SSLConfigFactory, SSLConfigSettings}
import javax.net.ssl.{SSLContext, TrustManagerFactory}

import scala.concurrent.{ExecutionContext, Future}

object Test extends App{

  implicit val actorSystem: ActorSystem = ActorSystem("test")
  implicit val materializer: ActorMaterializer = ActorMaterializer()
  implicit val executionContext: ExecutionContext = actorSystem.dispatcher

  val ksConfig: KeyStoreConfig = KeyStoreConfig.apply(data = None,
    filePath = Some("/Users/mshaik/testApp/src/main/resources/keystore/localhost.p12")
  ).withPassword(Some("test"))

  val kmConfig: KeyManagerConfig = KeyManagerConfig().withKeyStoreConfigs(List(ksConfig))

  val sslConfigSettings: SSLConfigSettings = SSLConfigFactory.defaultConfig.withKeyManagerConfig(kmConfig)

  val akkaSSLConfig: AkkaSSLConfig = AkkaSSLConfig.get(actorSystem).withSettings(sslConfigSettings)

  val ks: KeyStore = KeyStore.getInstance("PKCS12")
  ks.load(new FileInputStream(new File(ksConfig.filePath.get)), ksConfig.password.get.toCharArray)

  val kmf: KeyManagerFactoryWrapper = akkaSSLConfig.buildKeyManagerFactory(sslConfigSettings)
  kmf.init(ks, ksConfig.password.get.toCharArray)

  val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509")
  tmf.init(ks)

  val sslContext: SSLContext = SSLContext.getInstance("TLS")
  sslContext.init(kmf.getKeyManagers, tmf.getTrustManagers, new SecureRandom)

  val ctx: ConnectionContext = ConnectionContext.https(sslContext,
    sslConfig = Some(akkaSSLConfig),
    clientAuth = Some(TLSClientAuth.Want)
  )

  var bindingFuture: Future[ServerBinding] = _

  Http().setDefaultServerHttpContext(ctx)

  val route: Route = pathSingleSlash {
    get {
      complete(HttpResponse(StatusCodes.OK, entity = "Welcome to base path!"))
    }
  }

  try{
    bindingFuture = Http().bindAndHandle(route,  "localhost", 8085, connectionContext = ctx)
    println( s"Server online at https://localhost:8085/")
  } catch {
    case ex: Exception =>
      println(this.getClass, ex.getMessage, ex)
      materializer.shutdown()
      actorSystem.terminate()
  }
}

I believe the answer to my question is that there isn't much use in thoroughly configuring TLS options within ssl-config when creating a non-HTTP TLS connection. 我相信我的问题的答案是,在创建非HTTP TLS连接时,在ssl-config中彻底配置TLS选项并没有太多用处。

Not a single example I found shows how to define keystore and truststore parameters within the config and then use those configurations to create the SSLContext object (all examples configure the keystore/truststore parameters manually, within the code). 我发现的一个示例没有显示如何在配置中定义密钥库和信任库参数,然后使用这些配置来创建SSLContext对象(所有示例都在代码内手动配置密钥库/信任库参数)。 Ultimately I found it wasn't useful to use ssl-config for storing configurations. 最终,我发现使用ssl-config来存储配置没有用。 The only place I found it useful is to obtain the list of default ciphers and default protocols (and hence I still use it in my code). 我发现它唯一有用的地方是获取默认密码和默认协议的列表(因此我仍然在代码中使用它)。

For reference, below is what I ended up doing to configure the context and initial session structure and create the TCP server. 供参考,下面是我最终配置上下文和初始会话结构并创建TCP服务器的操作。 This is very similar to other examples found within documentation as well as some responses here on SO. 这与文档中的其他示例以及此处的一些响应非常相似。 Some differences in this response: 1) This requires client certificates, 2) This is for a server (as opposed to a client), 3) This code shows how to use factory methods to create the TLS BidiFlow (note the Tcp().bindTls call) 4) This allows you to pass in the Flow that will handle the incoming communications. 此响应中的一些区别:1)这需要客户端证书,2)这是针对服务器(而不是客户端)的,3)此代码显示了如何使用工厂方法创建TLS BidiFlow(请注意Tcp().bindTls调用)4)这使您可以传递将处理传入通信的Flow。

object TcpServerBindTls extends StrictLogging {
  def apply(hostInterface: String, tcpPort: Int, handler: Flow[ByteString, ByteString, NotUsed])(implicit system: ActorSystem, materializer: ActorMaterializer) = {
    val sslContext = buildSSLContext
    val firstSession = prepareFirstSession(sslContext)
    val connections: Source[Tcp.IncomingConnection, Future[Tcp.ServerBinding]] = Tcp().bindTls(hostInterface, tcpPort, sslContext, firstSession)

    connections runForeach { connection =>
      logger.info(s"New connection: ${connection}")
      connection.handleWith(handler)
    }
  }

  def prepareFirstSession(sslContext: SSLContext)(implicit system: ActorSystem) = {
    val sslConfig = AkkaSSLConfig.get(system);
    val config = sslConfig.config;
    val defaultParams = sslContext.getDefaultSSLParameters();
    val defaultProtocols = defaultParams.getProtocols();
    val defaultCiphers = defaultParams.getCipherSuites();
    val clientAuth = TLSClientAuth.need

    defaultParams.setProtocols(defaultProtocols)
    defaultParams.setCipherSuites(defaultCiphers)

    val firstSession = new TLSProtocol.NegotiateNewSession(None, None, None, None)
       .withCipherSuites(defaultCiphers: _*)
       .withProtocols(defaultProtocols: _*)
       .withParameters(defaultParams)

    firstSession
  }

  def buildSSLContext: SSLContext = {
    val bufferedSource = io.Source.fromFile("/path/to/password/file")
    val keyStorePassword = bufferedSource.getLines.mkString
    bufferedSource.close

    val keyStore = KeyStore.getInstance("PKCS12");
    val keyStoreLocation = "/path/to/keystore/file/server.p12"
    val keyStoreFIS = new FileInputStream(keyStoreLocation)
    keyStore.load(keyStoreFIS, keyStorePassword.toCharArray())

    val trustStore = KeyStore.getInstance("PKCS12");
    val trustStoreLocation = settings.tls.keyStoreLocation;
    val trustStoreFIS = new FileInputStream(keyStoreLocation)
    trustStore.load(trustStoreFIS, keyStorePassword.toCharArray())

    val kmf = KeyManagerFactory.getInstance("SunX509")
    kmf.init(keyStore, keyStorePassword.toCharArray())

    val tmf = TrustManagerFactory.getInstance("SunX509")
    tmf.init(trustStore)

    val sslContext = SSLContext.getInstance("TLS")
    sslContext.init(kmf.getKeyManagers, tmf.getTrustManagers, new SecureRandom())
    sslContext
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM