使用自签名证书的Spray https服务器的配置问题?

时间:2014-09-15 17:55:31

标签: scala akka keystore spray

我在Mac 10.9.4上使用Spray 1.3,Akka 2.3和Scala 2.11来设置HTTP服务器。我跟随Ch。 2 Manning的Akka in Action中的示例(此处提供示例代码:https://github.com/RayRoestenburg/akka-in-action.git),当我使用http时,它会按预期编译,运行和运行,但我无法将其配置为与https一起使用。

要使用https运行,我已生成一个自签名证书,如下所示:

keytool -genkey -keyalg RSA -alias selfsigned -keystore myjks.jks -storepass abcdef -validity 360 -keysize 2048

按照此示例,https://github.com/spray/spray/tree/v1.2-M8/examples/spray-can/simple-http-server/src/main/scala/spray/examples

我添加了SSL配置类:

package com.goticks

import java.security.{SecureRandom, KeyStore}
import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory}
import spray.io._

// for SSL support (if enabled in application.conf)
trait MySSLConfig {

  // if there is no SSLContext in scope implicitly the HttpServer uses the default SSLContext,
  // since we want non-default settings in this example we make a custom SSLContext available here
  implicit def sslContext: SSLContext = { 
    val keyStoreResource = "myjks.jks"
    val password = "abcdef"

    val keyStore = KeyStore.getInstance("jks")
    keyStore.load(getClass.getResourceAsStream(keyStoreResource), password.toCharArray)
    val keyManagerFactory = KeyManagerFactory.getInstance("SunX509")
    keyManagerFactory.init(keyStore, password.toCharArray)
    val trustManagerFactory = TrustManagerFactory.getInstance("SunX509")
    trustManagerFactory.init(keyStore)
    val context = SSLContext.getInstance("TLS")
    context.init(keyManagerFactory.getKeyManagers, trustManagerFactory.getTrustManagers, new SecureRandom)
    context
  }

  // if there is no ServerSSLEngineProvider in scope implicitly the HttpServer uses the default one,
  // since we want to explicitly enable cipher suites and protocols we make a custom ServerSSLEngineProvider
  // available here
  implicit def sslEngineProvider: ServerSSLEngineProvider = { 
    ServerSSLEngineProvider { engine =>
      engine.setEnabledCipherSuites(Array("TLS_RSA_WITH_AES_256_CBC_SHA"))
      engine.setEnabledProtocols(Array("SSLv3", "TLSv1"))
      engine
    }   
  }
}

我已更新Main类以使用SSL配置:

package com.goticks

import akka.actor._
import akka.io.IO

import spray.can.Http
import spray.can.server._

import com.typesafe.config.ConfigFactory

object Main extends App with MySSLConfig {
  val config = ConfigFactory.load()
  val host = config.getString("http.host")
  val port = config.getInt("http.port")

  implicit val system = ActorSystem("goticks")

  val api = system.actorOf(Props(new RestInterface()), "httpInterface")
  IO(Http) ! Http.Bind(listener = api, interface = host, port = port)
}

我已经更新了application.conf:

spray {
  can {
    server {
      server-header = "GoTicks.com REST API"
      ssl-encryption = on
    }
  }
}

编译并运行服务器后,当我尝试执行https GET时出现以下错误:

[ERROR] [09/15/2014 10:40:48.056] [goticks-akka.actor.default-dispatcher-4] [akka://goticks/user/IO-HTTP/listener-0/7] Aborting encrypted connection to localhost/0:0:0:0:0:0:0:1%0:59617 due to [SSLHandshakeException:no cipher suites in common] -> [SSLHandshakeException:no cipher suites in common]

我不确定我的问题是使用生成的密钥还是使用我的配置。顺便说一句,我的最终目标是将此配置与TCP套接字一起使用(请参阅我的另一个问题:TCP socket with SSL on Scala with Akka),但我无法找到运行安全TCP的文档,所以我想我会从HTTPS开始。

感谢任何帮助。

2 个答案:

答案 0 :(得分:5)

我终于能够根据找到的Apache Camel建议使用here。看起来像过度使用Camel只是为了设置SSLContext,但这才是最终奏效的。

我的SSLConfig最终看起来像这样:

import javax.net.ssl.SSLContext
import spray.io._
import org.apache.camel.util.jsse._

trait MySSLConfig {
    implicit def sslContext: SSLContext = {
        //val keyStoreFile = "/Users/eschow/repo/services/jks/keystore.jks"
        val keyStoreFile = "/Users/eschow/code/scala/akka-in-action/chapter2/myjks.jks"

        val ksp = new KeyStoreParameters()
        ksp.setResource(keyStoreFile);
        ksp.setPassword("abcdef")

        val kmp = new KeyManagersParameters()
        kmp.setKeyStore(ksp)
        kmp.setKeyPassword("abcdef")

        val scp = new SSLContextParameters()
        scp.setKeyManagers(kmp)

        val context= scp.createSSLContext()

        context
      }

    implicit def sslEngineProvider: ServerSSLEngineProvider = {
        ServerSSLEngineProvider { engine =>
            engine.setEnabledCipherSuites(Array("TLS_RSA_WITH_AES_256_CBC_SHA"))
            engine.setEnabledProtocols(Array("SSLv3", "TLSv1"))
            engine
        }
    }
}

顺便说一下,Camel记录的错误更有帮助。做一些愚蠢的事情就像提供一个错误的keystone路径或错误的密码一样,会产生有意义的,人为可读的错误,而不是我之前看到的无声失败。

答案 1 :(得分:1)

如果要读取项目外的密钥库文件,可以使用

new FileInputStream("/Users/eschow/code/scala/akka-in-action/chapter2/myjks.jks")

否则你需要将文件放在项目的资源文件夹中,例如。 / your_project / src / main / resource,并阅读它

getClass.getResourceAsStream("/myjks.jks")