Scala Spray客户端sendReceive抛出SSLHandshakeException
我正在尝试使用https将spray客户端连接到受限RESTAPI。问题是远程服务器的证书没有被注册为受信任的,然后简单的Get()连接被SSLHandshakeException拒绝,我很难找到任何关于如何使其工作的信息。这在我的本地机器上运行,无需更改某些内容 我找到了关于如何将证书放入jvm信任库的教程,但是因为我使用的是dokku/docker,所以jvm实例是特定于容器的(或者?)。尽管将来我可能会在不同的机器上重新部署应用程序,但我希望在应用程序中定义它,而不是每次都设置jvmScala Spray客户端sendReceive抛出SSLHandshakeException,scala,ssl,spray-client,Scala,Ssl,Spray Client,我正在尝试使用https将spray客户端连接到受限RESTAPI。问题是远程服务器的证书没有被注册为受信任的,然后简单的Get()连接被SSLHandshakeException拒绝,我很难找到任何关于如何使其工作的信息。这在我的本地机器上运行,无需更改某些内容 我找到了关于如何将证书放入jvm信任库的教程,但是因为我使用的是dokku/docker,所以jvm实例是特定于容器的(或者?)。尽管将来我可能会在不同的机器上重新部署应用程序,但我希望在应用程序中定义它,而不是每次都设置jvm 这是
这是我第一次以编程方式面对SSL,因此我可能会对它的工作原理做出错误的假设。你能帮忙吗?我不是scala方面的专家,也从未使用过spray客户端,但我会根据我的Java经验尝试帮助你 您有两个选项,使用TrustManagerFactory从密钥库使用服务器证书(安全)初始化SSLContext 或者创建一个接受任何证书(不安全)的虚拟TrustManagerFactory 通过以下方式初始化SSLContext() 我不知道Scala语法,但将其翻译给您应该不难 希望这有帮助
编辑(由Matej Briškár建议):上述方法是正确的,但对于spray客户端来说,这并不容易。要使
sendReceive
使用SSL,您需要首先建立连接,然后将此连接传递到sendReceive
首先,如上所述创建隐式信任管理器。例如:
implicit def sslContext: SSLContext = {
val context = SSLContext.getInstance("TLS")
context.init(null, Array[TrustManager](new DummyTrustManager), new SecureRandom())
context
}
请注意,此连接将在一段时间后超时,因此您可能希望更改此默认行为
然后,您需要建立使用此隐式连接的连接,如:
val connection = {
Await.result((IO(Http) ? HostConnectorSetup(host, port = 443, sslEncryption = true)).map { case HostConnectorInfo(hostConnector, _) => hostConnector }, timeout.duration)
}
注意:host
表示您试图访问的URL。另外,timeout
来自此代码段之外
最后,您可以使用sendReceive(connection)
访问SSL加密的主机
注意:原始编辑有一个参考: 据报道,这个问题将得到解决
然而,讨论从2013年开始,现在是2016年。需要建立连接以使SSL工作的问题似乎仍然存在。不确定讨论是否相关。这是我的2美分。如果您只是想以不安全的方式进行讨论,我只创建sendReceive方法来发送(HttpRequest,HostConnectorSetup),而不是HttpRequest
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.{SSLContext, TrustManager, X509TrustManager}
import akka.actor.ActorRefFactory
import akka.io.IO
import akka.pattern.ask
import akka.util.Timeout
import spray.can.Http
import spray.can.Http.HostConnectorSetup
import spray.client.pipelining._
import spray.http.{HttpResponse, HttpResponsePart}
import spray.io.ClientSSLEngineProvider
import spray.util._
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
object Test {
// prepare your sslContext and engine Provider
implicit lazy val engineProvider = ClientSSLEngineProvider(engine => engine)
implicit lazy val sslContext: SSLContext = {
val context = SSLContext.getInstance("TLS")
context.init(null, Array[TrustManager](new DummyTrustManager), new SecureRandom)
context
}
private class DummyTrustManager extends X509TrustManager {
def isClientTrusted(cert: Array[X509Certificate]): Boolean = true
def isServerTrusted(cert: Array[X509Certificate]): Boolean = true
override def getAcceptedIssuers: Array[X509Certificate] = Array.empty
override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = {}
override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = {}
}
// rewrite sendReceiveMethod fron spray.client.pipelining
def mySendReceive(implicit refFactory: ActorRefFactory, executionContext: ExecutionContext,
futureTimeout: Timeout = 60.seconds): SendReceive = {
val transport = IO(Http)(actorSystem)
// HttpManager actually also accepts Msg (HttpRequest, HostConnectorSetup)
request =>
val uri = request.uri
val setup = HostConnectorSetup(uri.authority.host.toString, uri.effectivePort, uri.scheme == "https")
transport ? (request, setup) map {
case x: HttpResponse => x
case x: HttpResponsePart => sys.error("sendReceive doesn't support chunked responses, try sendTo instead")
case x: Http.ConnectionClosed => sys.error("Connection closed before reception of response: " + x)
case x => sys.error("Unexpected response from HTTP transport: " + x)
}
}
// use mySendReceive instead spray.client.pipelining.sendReceive
}
非常感谢,看起来你已经回答了这个问题,我会在晚上试试,然后告诉你。它看起来与我在服务器端看到的非常相似,并试图申请我的客户端(我认为有更大的区别),但没有任何运气。我知道现在我走的路是对的。看看这篇文章,也许对你也有帮助。我刚刚把它做好了。问题是spray客户端不是spray can,它是基于请求/响应的高级别方法通信。我必须建立连接并把它放在里面。我已经编辑了你的答案,这样我可以把它标记为正确的。希望你不介意。这很奇怪:-)谢谢你的回答!
implicit def sslContext: SSLContext = {
val context = SSLContext.getInstance("TLS")
context.init(null, Array[TrustManager](new DummyTrustManager), new SecureRandom())
context
}
val connection = {
Await.result((IO(Http) ? HostConnectorSetup(host, port = 443, sslEncryption = true)).map { case HostConnectorInfo(hostConnector, _) => hostConnector }, timeout.duration)
}
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.{SSLContext, TrustManager, X509TrustManager}
import akka.actor.ActorRefFactory
import akka.io.IO
import akka.pattern.ask
import akka.util.Timeout
import spray.can.Http
import spray.can.Http.HostConnectorSetup
import spray.client.pipelining._
import spray.http.{HttpResponse, HttpResponsePart}
import spray.io.ClientSSLEngineProvider
import spray.util._
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
object Test {
// prepare your sslContext and engine Provider
implicit lazy val engineProvider = ClientSSLEngineProvider(engine => engine)
implicit lazy val sslContext: SSLContext = {
val context = SSLContext.getInstance("TLS")
context.init(null, Array[TrustManager](new DummyTrustManager), new SecureRandom)
context
}
private class DummyTrustManager extends X509TrustManager {
def isClientTrusted(cert: Array[X509Certificate]): Boolean = true
def isServerTrusted(cert: Array[X509Certificate]): Boolean = true
override def getAcceptedIssuers: Array[X509Certificate] = Array.empty
override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = {}
override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = {}
}
// rewrite sendReceiveMethod fron spray.client.pipelining
def mySendReceive(implicit refFactory: ActorRefFactory, executionContext: ExecutionContext,
futureTimeout: Timeout = 60.seconds): SendReceive = {
val transport = IO(Http)(actorSystem)
// HttpManager actually also accepts Msg (HttpRequest, HostConnectorSetup)
request =>
val uri = request.uri
val setup = HostConnectorSetup(uri.authority.host.toString, uri.effectivePort, uri.scheme == "https")
transport ? (request, setup) map {
case x: HttpResponse => x
case x: HttpResponsePart => sys.error("sendReceive doesn't support chunked responses, try sendTo instead")
case x: Http.ConnectionClosed => sys.error("Connection closed before reception of response: " + x)
case x => sys.error("Unexpected response from HTTP transport: " + x)
}
}
// use mySendReceive instead spray.client.pipelining.sendReceive
}