Я полагаю, что ответ на мой вопрос заключается в том, что при тщательном конфигурировании параметров 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
}
}