2016-06-11 43 views
5

阿卡版(SSL)服务器:阿卡HTTPS使用SSL-conf的

阿卡特点

语言:斯卡拉

我使用阿卡2.4.7的HTTP服务器功能提供不同的端口数HTTPS服务连接。 在这个阶段,需求是这个角色系统的这个组件在一个JVM中托管几个HTTPS服务 - 它是连接和集成其他服务的后端。

问题

我想用Typesafe's ssl-config库配置每个HTTPS服务器。我该怎么做(我没有成功)

我曾尝试:

因为我已经在application.conf定义SSL-config配置块,每个服务。一个nexted结构的一个例子是:

my-service { 
    ssl-config = { 
    debug { 
     all = true 
    } 
    sslParameters { 
     clientAuth : "none" 
    } 
    ssl = { 
     keyManager = { 
     stores = [ 
      {path: tmp/certs/autumn/devhost.jks, password: "not-real-password", type: "JKS"} 
     ] 
     } 
    } 
    } 
} 

我从使用HOCON路径用于application.conf定义我的服务抓住配置的该部分,并与参考默认配置来创建合并它一个SSLConfigSettings。

def parseSslConfig(config: Config): SSLConfigSettings = { 
    val cfg = config.withFallback(ConfigFactory.defaultReference().getConfig("ssl-config")) 
    val parser = new SSLConfigParser(EnrichedConfig(cfg), getClass.getClassLoader) 
    parser.parse() 
    } 

现在具有SSLConfigSettings我现在可以创建其进而,在阿卡2.4.7,可以用于使用函数原型创建HttpsConnectionContext一个AkkaSSLConfig对象:

// #HTTPS的上下文创建 // ConnectionContext DEF HTTPS( 的SSLContext:的SSLContext, sslConfig:选项[AkkaSSLConfig] =无, enabledCipherSuites:选项[immutable.Seq [字符串]] =无, enabledProtocols:选项[不可变。SEQ [字符串]] =无, clientAuth:选项[TLSClientAuth] =无, sslParameters:选项[SSLParameters] =无)= 新HttpsConnectionContext(的SSLContext,sslConfig,enabledCipherSuites,enabledProtocols,clientAuth,sslParameters) //#HTTPS -context创造

所以干脆我可以启动HTTPS服务器与如下代码(注:请求handeler被定义在其他地方提供Future[HttpResponse]

val akkaSSLConfig: AkkaSSLConfig = AkkaSSLConfig().withSettings(sslConfigSettings) 
val serverConnectionContext = ConnectionContext.https(SSLContext.getDefault, Some(akkaSSLConfig)) 

    val httpServer = httpServerSystem.bind(interface = "127.0.0.1", 
    port = 8991, 
    connectionContext = serverConnectionContext) 

    val bindingFuture: Future[Http.ServerBinding] = httpServer.to(Sink.foreach { 
    connection => 
     system.log.info(s"Accepted HTTP connection " + 
     s"[Local: address=${connection.localAddress.getAddress.getHostAddress}, port=${connection.localAddress.getPort};" + 
     s" Remote: address=${connection.remoteAddress.getAddress.getHostAddress} port=${connection.remoteAddress.getPort}]" + connection.remoteAddress) 
     connection.handleWithAsyncHandler(httpRequest => requestHandler(httpRequest, connection.localAddress, connection.remoteAddress)) 
    }).run() 

服务器无一例外启动或错误和在定义的端口上绑定到127.0.0.1

2016-06-11 14:07:51,403 DEBUG [autumn-backend-akka.actor.default-dispatcher-7] TcpListener - Successfully bound to /127.0.0.1:8991 
2016-06-11 14:07:51,404 DEBUG [autumn-backend-akka.actor.default-dispatcher-7] TcpListener - started ([email protected]) 
2016-06-11 14:07:51,404 DEBUG [autumn-backend-akka.actor.default-dispatcher-7] TcpListener - now watched by Actor[akka://autumn-backend/system/IO-TCP/selectors/$a#-745039521] 
2016-06-11 14:07:51,407 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] TcpListener - now watched by Actor[akka://autumn-backend/user/StreamSupervisor-0/$$a#-672917867] 

我使用浏览器或curl访问服务器,结果不好。这是要求客户端证书,我知道是错误的,因为我在ssl-conf中明确配置了它们不需要,并且JDK8中的ssl-conf将此设置为默认情况下不需要。

curl -v https://localhost:8991 
* Rebuilt URL to: https://localhost:8991/ 
* Trying 127.0.0.1... 
* Connected to localhost (127.0.0.1) port 8991 (#0) 
* SSL peer handshake failed, the server most likely requires a client certificate to connect 
* Closing connection 0 
curl: (35) SSL peer handshake failed, the server most likely requires a client certificate to connect 

OpenSSL的与_s_client_选项进一步的调查表明,有没有发生SSL握手,也没有返回,尽管知道密钥库是好的,其他地方工作的证书。

openssl s_client -showcerts -connect localhost:8991 
CONNECTED(00000003) 
140735299473488:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:s23_clnt.c:769: 
--- 
no peer certificate available 

No client certificate CA names sent 
--- 
SSL handshake has read 7 bytes and written 317 bytes 
--- 
New, (NONE), Cipher is (NONE) 
Secure Renegotiation IS NOT supported 
Compression: NONE 
Expansion: NONE 
No ALPN negotiated 
--- 

的阿卡日志在调试模式下显示没有异常和一个TCP连接已完成,一个TLS演员开始,然后立即停止。

2016-06-11 14:09:26,378 DEBUG [autumn-backend-akka.actor.default-dispatcher-6] TcpListener - New connection accepted 
2016-06-11 14:09:26,378 DEBUG [autumn-backend-akka.actor.default-dispatcher-9] SelectionHandler - now supervising Actor[akka://autumn-backend/system/IO-TCP/selectors/$a/9#1252313265] 
2016-06-11 14:09:26,378 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] TcpIncomingConnection - started ([email protected]) 
2016-06-11 14:09:26,378 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] TcpIncomingConnection - now watched by Actor[akka://autumn-backend/system/IO-TCP/selectors/$a#-745039521] 
2016-06-11 14:09:26,381 INFO [autumn-backend-akka.actor.default-dispatcher-7] ActorSystemImpl - Accepted HTTP connection [Local: address=127.0.0.1, port=8991; Remote: address=127.0.0.1 port=58726]/127.0.0.1:58726 
2016-06-11 14:09:26,384 DEBUG [autumn-backend-akka.actor.default-dispatcher-9] StreamSupervisor - now supervising Actor[akka://autumn-backend/user/StreamSupervisor-0/flow-9-0-unknown-operation#149184815] 
2016-06-11 14:09:26,385 DEBUG [autumn-backend-akka.actor.default-dispatcher-7] TcpIncomingConnection - now watched by Actor[akka://autumn-backend/user/StreamSupervisor-0/$$j#-1999211380] 
2016-06-11 14:09:26,385 DEBUG [autumn-backend-akka.actor.default-dispatcher-9] ActorGraphInterpreter - started ([email protected]) 
2016-06-11 14:09:26,385 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] StreamSupervisor - now supervising Actor[akka://autumn-backend/user/StreamSupervisor-0/flow-9-1-unknown-operation#1511230856] 
sslConfig.config.loose.disableSNI = false 
2016-06-11 14:09:26,387 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] TLSActor - started ([email protected]) 
2016-06-11 14:09:26,389 DEBUG [autumn-backend-akka.actor.default-dispatcher-5] TLSActor - stopped 

调试运行时显示,密钥库被拾起:

akkaSSLConfig = {[email protected]} 
system = {[email protected]} "akka://autumn-backend" 
config = {[email protected]} "SSLConfig(None,SSLDebugConfig(false,false,false,None,false,false,false,false,None,false,false,false,false,false),false,Vector(RSA keySize < 2048, DSA keySize < 2048, EC keySize < 224),Vector(MD2, MD4, MD5),None,Some(Vector(TLSv1.2, TLSv1.1, TLSv1)),class com.typesafe.sslconfig.ssl.DefaultHostnameVerifier,KeyManagerConfig(SunX509,List(KeyStoreConfig(None,Some(config/certs/autumn/devhost.jks),Some(A8C7B78Ymb),JKS))),SSLLooseConfig(false,None,None,false,false,false,false),TLSv1.2,None,None,SSLParametersConfig(Default,Vector()),TrustManagerConfig(PKIX,List()))" 
    default = false 
    protocol = "TLSv1.2" 
    checkRevocation = {[email protected]} "None" 
    revocationLists = {[email protected]} "None" 
    enabledCipherSuites = {[email protected]} "None" 
    enabledProtocols = {[email protected]} "Some(Vector(TLSv1.2, TLSv1.1, TLSv1))" 
    disabledSignatureAlgorithms = {[email protected]} "Vector" size = 3 
    disabledKeyAlgorithms = {[email protected]} "Vector" size = 3 
    sslParametersConfig = {[email protected]} "SSLParametersConfig(Default,Vector())" 
    keyManagerConfig = {[email protected]} "KeyManagerConfig(SunX509,List(KeyStoreConfig(None,Some(config/certs/autumn/devhost.jks),Some(A8C7B78Ymb),JKS)))" 
    algorithm = "SunX509" 
    keyStoreConfigs = {[email protected]} "::" size = 1 
    0 = {[email protected]} "KeyStoreConfig(None,Some(config/certs/autumn/devhost.jks),Some(not-real-password),JKS)" 

什么工作,如果我手动创建一个HttpsConnectionContext和不使用SSL-conf的或AkkaSSLConfig是 - 但是这不是目标。如何使用AkkaSSLconf对象和Typesafe ssl-config库配置和创建HTTPS配置连接?

更新1:

如果我特别要求这样的TLS上下文的TLS例如:

val sslCtx = SSLContext.getInstance("TLS") 

我得到一个异常的的SSLContext将不会被初始化。但对于init SSLContext,我需要创建密钥库truststore,这一切都很好,但感觉就像我忽略了已经定义了所有这些东西的ssl-conf库的所有优点。

更新2:

我发现,您可以创建以下方法的HTTPS连接上下文:

Http().createServerHttpsContext(akkaSSLConfig) 

您可以使用该AkkaSSLConfig这是好东西,我的HTTPS服务器上下文后。问题是在测试HTTPS服务器无法正常工作,它只是挂起1分钟例外:

2016-06-12 11:14:53,222 DEBUG [autumn-backend-akka.actor.default-dispatcher-12] RepointableActorRef - Aborting tcp connection because of upstream failure: No elements passed in the last 1 minute. 
akka.stream.impl.Timers$IdleTimeoutBidi$$anon$7.onTimer(Timers.scala:160) 
akka.stream.stage.TimerGraphStageLogic.akka$stream$stage$TimerGraphStageLogic$$onInternalTimer(GraphStage.scala:1125) 
akka.stream.stage.TimerGraphStageLogic$$anonfun$akka$stream$stage$TimerGraphStageLogic$$getTimerAsyncCallback$1.apply(GraphStage.scala:1114) 
akka.stream.stage.TimerGraphStageLogic$$anonfun$akka$stream$stage$TimerGraphStageLogic$$getTimerAsyncCallback$1.apply(GraphStage.scala:1114) 
akka.stream.impl.fusing.GraphInterpreter.runAsyncInput(GraphInterpreter.scala:572) 
akka.stream.impl.fusing.GraphInterpreterShell.receive(ActorGraphInterpreter.scala:420) 
akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:604) 
akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:619) 
akka.actor.Actor$class.aroundReceive(Actor.scala:484) 

我看了看源createServerHttpsContext在阿卡回购在GitHub上here发现:

// currently the same configuration as client by default, however we should tune this for server-side apropriately (!) 
    def createServerHttpsContext(sslConfig: AkkaSSLConfig): HttpsConnectionContext = { 
    log.warning("Automatic server-side configuration is not supported yet, will attempt to use client-side settings. " + 
     "Instead it is recommended to construct the Servers HttpsConnectionContext manually (via SSLContext).") 
    createClientHttpsContext(sslConfig) 
    } 

为什么HTTPS服务器不能与createServerHttpsContext(..)一起使用?特别是手动设置,你基本上设置了一个TLS SSLContext,KeyManagerFactory (带密钥存储),这是一个SecureRandom的实例,不用再去了。

回答

4

正如另一条评论发布的那样,存在一个git集线器问题,它指出“自动”使用配置尚不受支持。但是,这个问题现在已经关闭了;没有完成只是moved。我为未来版本阅读了发行说明,但没有看到与此有关的任何内容。由于现在非常重视安全性,我很惊讶SSL/TSL的设置不适合开箱即用。

我正在使用v2.4.4(当前是2.4.16)和类似于提问者,我发现尽管akk-http文档告诉你使用配置,实际上从调试中可以看到配置读入,实际使用它的实现没有完成。我在我的日志中这样的信息:

akka.actor.ActorSystemImpl(OtisRestActorSystem)] Automatic server-side configuration is not supported yet, will attempt to use client-side settings. Instead it is recommended to construct the Servers HttpsConnectionContext manually (via SSLContext) 

我试图“手动构建服务器HttpsConnectionContext”使用其SSL配置,但我无法得到它的工作。

还有其他消息,当我最初进行故障排除时显示它在配置的密钥存储区中读取(它不使用类路径来查找它,所以一开始就找不到它)。所以我不确定哪些部件在工作,哪些部件不见了。所以我最终放弃了完全的akka​​-http ssl配置,并且自己设置了它,因为我的用例非常简单。我只想启用服务器端SSL/TSL。

在我的配置我有:

ssl { 
    keyStoreFileName = "myKeyFile.p12" 
    keyStorePassword = "myPassword" 
    } 

阅读我的设置,我有:

class Settings(config: Config) extends Extension { 
    object Ssl { 
    var KeyStoreFileName = config.getString("ssl.keyStoreFileName") 
    var KeyStorePassword = config.getString("ssl.keyStorePassword") 
    } 
} 

,并为 “应用程序” 我有:

object RestWebServiceApp extends App with RouteConcatenation { 
    import akka.event.{Logging, LoggingAdapter} 
    import akka.http.scaladsl.{ ConnectionContext, HttpsConnectionContext, Http } 
    import akka.http.scaladsl.server.Directives._ 
    import akka.http.scaladsl.model.MediaTypes._ 
    import akka.stream.{ActorMaterializer, ActorMaterializerSettings} 
    import java.io.InputStream 
    import java.security.{ SecureRandom, KeyStore } 
    import javax.net.ssl.{ SSLContext, TrustManagerFactory, KeyManagerFactory } 
    import JsonSupport._ 

    implicit val system = ActorSystem("OtisRestActorSystem") 
    implicit val materializer: ActorMaterializer = ActorMaterializer(ActorMaterializerSettings(system)) 
    implicit val ec = system.dispatcher 

    ... //setting up all the routes, etc. 

    val settings = Settings(system) 
    val fileName = settings.Ssl.KeyStoreFileName 
    val keyFile: InputStream = getClass.getClassLoader.getResourceAsStream(fileName) 
    require(keyFile != null, s"Failed to load key file: ${settings.Ssl.KeyStoreFileName}") 
    val extension = if(fileName.lastIndexOf('.')>0) fileName.substring(fileName.lastIndexOf('.')+1) else "" 
    val keyStore: KeyStore = extension.toLowerCase match { 
    case "jks" => KeyStore.getInstance("jks") //Java Key Store; Java default and only works with Java; tested 
    case "jcek" => KeyStore.getInstance("JCEKS") //Java Cryptography Extension KeyStore; Java 1.4+; not tested 
    case "pfx" | "p12" => KeyStore.getInstance("PKCS12") // PKCS #12, Common and supported by many languages/frameworks; tested 
    case _ => throw new IllegalArgumentException(s"Key has an unknown type extension $extension. Support types are jks, jcek, pfx, p12.") 
    } 
    val password: Array[Char] = (if(settings.Ssl.KeyStorePassword==null) "" else settings.Ssl.KeyStorePassword).toCharArray 
    keyStore.load(keyFile, password) 

    //TODO: looks like the "SunX509", "TLS", are defined in the keystore, should we pull them out rather than hard coding? 
    val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509") 
    keyManagerFactory.init(keyStore, password) 

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

    val sslContext: SSLContext = SSLContext.getInstance("TLS") 
    sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new SecureRandom) 
    val https: HttpsConnectionContext = ConnectionContext.https(sslContext) 
    Http().setDefaultServerHttpContext(https) 
    Http().bindAndHandle(routes, "localhost", 433, connectionContext = https) 
} 
0

当您的HTTPS服务器挂起1分钟,然后产生“由于上游故障而中止tcp连接:在最后1分钟内没有元素传递。”错误,它可能是你的随机数发生器没有足够的熵在合理的时间内产生足够的随机数。

在我的情况下,当初始化SSLContext时,我通过使用new SecureRandom而不是SecureRandom.getInstanceString修复了这个问题。这对我来说看起来足够随机/安全,但当然你应该自己决定。