Ssl-config Lightbend с не HTTP-сервером TCP - PullRequest
0 голосов
/ 28 августа 2018

Я нашел несколько ресурсов, которые предоставляют подробную информацию о настройке параметров ssl-config в файле application.conf, и я определил, как получить доступ к этим конфигурациям с помощью AkkaSSLConfig.get (). Я видел, что контекст https может быть создан с использованием объекта AkkaSSLConfig в качестве параметра для ConnectionContext.https ().

Можно ли использовать это для не-http серверов? Возвращен ли контекст каким-то конкретным образом для http? Я пытаюсь воспользоваться преимуществами ssl-config, но мне не ясно, дает ли он какие-либо преимущества для не-http серверов, и я не вижу удобного способа построения контекста из определения ssl-config, в в этом случае, кажется, я могу также определить контекст вручную.

Наконец, трудно найти какие-либо примеры построения контекста для не-http серверов. Кажется, процесс может быть таким же, как и для http-серверов, но я обнаружил, что примеры часто включают использование классов / методов, в имени которых указано «http». Если кто-нибудь знает хороший пример, я был бы очень признателен.

Ответы [ 2 ]

0 голосов
/ 31 августа 2018

Я полагаю, что ответ на мой вопрос заключается в том, что при тщательном конфигурировании параметров TLS в ssl-config при создании соединения TLS, отличного от HTTP, нет особого смысла.

Ни один пример, который я обнаружил, не показывает, как определить параметры хранилища ключей и хранилища доверенных сертификатов в конфигурации и затем использовать эти конфигурации для создания объекта SSLContext (все примеры настраивают параметры хранилища ключей / хранилища доверенных сертификатов вручную, в коде). В конечном итоге я обнаружил, что использовать ssl-config для хранения конфигураций бесполезно. Единственное место, которое я нашел полезным, - это получить список шифров по умолчанию и протоколов по умолчанию (и, следовательно, я все еще использую его в своем коде).

Для справки ниже приведено описание того, что я сделал для настройки контекста и начальной структуры сеанса и создания сервера TCP. Это очень похоже на другие примеры, найденные в документации, а также некоторые ответы здесь на SO. Некоторые различия в этом ответе: 1) Это требует сертификатов клиента, 2) Это для сервера (в отличие от клиента), 3) Этот код показывает, как использовать фабричные методы для создания TLS BidiFlow (обратите внимание на вызов Tcp().bindTls ) 4) Это позволяет вам передавать поток, который будет обрабатывать входящие сообщения.

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
  }
}
0 голосов
/ 28 августа 2018
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()
  }
}
...