2015-03-11 9 views
1

Я пытаюсь подключить брандмауэр, используя https для ограниченного отдыха api. Проблема заключается в том, что сертификат удаленного сервера не зарегистрирован как доверенный, тогда простое соединение Get() отменяется с помощью SSLHandshakeException, и я изо всех сил пытаюсь найти какую-либо информацию о том, как это сделать. Это как-то работает с моей локальной машины без необходимости что-то менять.Spray-client sendReceive throws SSLHandshakeException

Я нашел учебники о том, как поместить сертификат в jvm truststore, однако, поскольку я использую dokku/docker, AFAIK экземпляр jvm имеет конкретный контейнер (или?). Несмотря на то, что я могу в будущем перераспределить приложение на разных машинах, я бы хотел, чтобы он определялся в приложении, а не устанавливал jvm каждый раз.

Это первый раз, когда я сталкиваюсь с программным обеспечением SSL, поэтому я могу ошибаться в предположениях о том, как это работает. Вы можете помочь?

ответ

1

Я не эксперт в scala, и я никогда не использовал брызговик, но я постараюсь помочь вам на основе моего опыта Java.

У вас есть два варианта, инициализировать SSLContext с TrustManagerFactory из хранилища ключей с сертификатом сервера (SECURE)

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, но его не сложно перевести.

Надеюсь, это поможет.


EDIT (предложенный Матей 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 поступает из-за пределов этого фрагмента кода.

И, наконец, вы можете использовать sendReceive(connection) для доступа к зашифрованному хосту SSL.


Примечание: Оригинал редактирования была ссылка:

Согласно discussion online этот вопрос будет фиксированной, хотя.

Однако обсуждение закончилось с 2013 года, а сейчас 2016 год. Проблема в том, что подключение к работе будет работать, похоже, все еще есть. Не уверен, что обсуждение актуально.

+0

Спасибо большое, это выглядит вы отвечаете на вопрос, я попробую это вечером и дам вам знать. Он очень похож на то, что я видел на стороне сервера, и пытался применить его на стороне клиента (я думал, что есть большая разница), но без везения. Теперь я знаю, что пошла правильно. –

+0

Взгляните на это сообщение, возможно, поможет и вам. Https://stackoverflow.com/questions/20706026/bing-azure-search-api/20789379#20789379 – vzamanillo

+1

Я только что заработал. Дело в том, что клиент-распылитель не является распылителем, и это слишком высокоуровневое соединение на основе запроса/ответа. Мне пришлось установить связь и поместить ее внутрь. –

1

Вот мои 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 
} 
+0

Большое спасибо за этот ответ! – pcejrowski