Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ssl/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala Spray客户端sendReceive抛出SSLHandshakeException_Scala_Ssl_Spray Client - Fatal编程技术网

Scala Spray客户端sendReceive抛出SSLHandshakeException

Scala Spray客户端sendReceive抛出SSLHandshakeException,scala,ssl,spray-client,Scala,Ssl,Spray Client,我正在尝试使用https将spray客户端连接到受限RESTAPI。问题是远程服务器的证书没有被注册为受信任的,然后简单的Get()连接被SSLHandshakeException拒绝,我很难找到任何关于如何使其工作的信息。这在我的本地机器上运行,无需更改某些内容 我找到了关于如何将证书放入jvm信任库的教程,但是因为我使用的是dokku/docker,所以jvm实例是特定于容器的(或者?)。尽管将来我可能会在不同的机器上重新部署应用程序,但我希望在应用程序中定义它,而不是每次都设置jvm 这是

我正在尝试使用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
}