【问题标题】:Spray-client sendReceive throws SSLHandshakeException喷雾客户端 sendReceive 抛出 SSLHandshakeException
【发布时间】:2015-03-11 19:30:02
【问题描述】:

我正在尝试使用 https 将喷雾客户端连接到受限的 rest api。问题是远程服务器的证书未注册为受信任,然后简单的 Get() 连接被 SSLHandshakeException 拒绝,我很难找到有关如何使其工作的任何信息。这在我的本地机器上确实可以工作,而无需更改任何内容。

我找到了有关如何将证书放入 jvm 信任库的教程,但是由于我使用的是 dokku/docker,AFAIK jvm 实例是特定于容器的(或者?)。即使将来我可能会在不同的机器上重新部署应用程序,我希望在应用程序中定义它,而不是每次都设置 jvm。

这是我第一次以编程方式面对 SSL,所以我可能会对它的工作原理做出错误的假设。你能帮忙吗?

【问题讨论】:

    标签: scala ssl spray-client


    【解决方案1】:

    我不是 scala 方面的专家,也从未使用过 spray-client,但我会根据我的 Java 经验尝试为您提供帮助。

    您有两个选择,使用服务器证书 (SECURE) 从密钥库中的 TrustManagerFactory 初始化 SSLContext

    File keyStoreFile = new File("./myKeyStore");
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(new FileInputStream(keyStoreFile), "keyStorePassword".toCharArray());
    TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
    tmf.init(ks);
    SSLContext sc = SSLContext.getInstance("TLS");
    sc.init(null, tmf.getTrustManagers(), new java.security.SecureRandom());
    

    创建一个接受任何证书(不安全)的虚拟 TrustManagerFactory

    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    import javax.net.ssl.X509TrustManager;
    
    public class DummyTrustManager implements X509TrustManager {
    
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    
        /* (non-Javadoc)
        * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[], java.lang.String)
        */
        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
        }
        /* (non-Javadoc)
        * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], java.lang.String)
        */
        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
        }
    }
    

    通过这种方式初始化 SSLContext (it is very similar in spray-client)

    SSLContext sc = SSLContext.getInstance("TLS");
    sc.init(null, new TrustManager[] { new DummyTrustManager() }, new java.security.SecureRandom());
    

    我不知道 Scala 语法,但应该不难翻译给你。

    希望这会有所帮助。


    编辑(由 Matej Briškár 建议):以上是正确的方法,但是对于喷雾客户端来说,这并不容易。要使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 也来自此代码 sn-p 之外。

    最后您可以使用sendReceive(connection) 访问SSL 加密主机。


    注意:原编辑有参考:

    根据discussion online 的说法,该问题将得到解决。

    但是,讨论是从 2013 年开始的,现在是 2016 年。需要建立连接才能使 SSL 工作的问题似乎仍然存在。不确定讨论是否相关。

    【讨论】:

    • 非常感谢,看来你回答了这个问题,我晚上试试这个,让你知道。它看起来与我在服务器端看到的非常相似,并尝试申请我的客户端(我认为有更大的差异),但没有任何运气。我现在知道我走对了。
    • 看看这篇文章,也许对你也有帮助stackoverflow.com/questions/20706026/bing-azure-search-api/…
    • 我刚刚让它工作了。问题是喷雾客户端不是喷雾罐,它是基于请求/响应的高级方法通信。我必须建立连接并将其放入。
    • 我已编辑您的答案,以便将其标记为正确。希望你不要介意。
    • 这很奇怪 :-) 谢谢
    【解决方案2】:

    如果您只想以不安全的方式执行此操作,这是我的 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
    }
    

    【讨论】:

    • 非常感谢您的回答!
    猜你喜欢
    • 2013-05-11
    • 1970-01-01
    • 2012-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-25
    • 2011-02-23
    • 1970-01-01
    相关资源
    最近更新 更多