3

特定の CA 証明書を使用して、セキュリティで保護されたサーバーに http 要求を投稿したいと考えています。

私はSpray 1.3.1を使用しています。コードは次のようになります。

val is = this.getClass().getResourceAsStream("/cacert.crt")

val cf: CertificateFactory = CertificateFactory.getInstance("X.509")

val caCert: X509Certificate = cf.generateCertificate(is).asInstanceOf[X509Certificate];

val tmf: TrustManagerFactory  = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
val ks: KeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null); 
ks.setCertificateEntry("caCert", caCert);

tmf.init(ks);

implicit val sslContext: SSLContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);

implicit val timeout: Timeout = Timeout(15.seconds)
import spray.httpx.RequestBuilding._

val respFuture = (IO(Http) ? Post( uri=Uri(url), content="my content")).mapTo[HttpResponse]

問題は、定義された暗黙の SSLContext が取得されず、実行時に「要求されたターゲットへの有効な証明書パスが見つかりません」というメッセージが表示されることです。

スプレー クライアントで使用する SSLContext を定義するにはどうすればよいですか?

4

3 に答える 3

2

以下を使用して、スプレーで SSLContext を定義します。私の場合、リモート サーバーの証明書を検証しない非常に寛容なコンテキストを使用しています。この投稿の最初の解決策に基づいて- 私にとってはうまくいきます。

import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.{SSLContext, X509TrustManager, TrustManager}

import akka.actor.ActorRef
import akka.io.IO
import akka.util.Timeout
import spray.can.Http

import scala.concurrent.Future

trait HttpClient {
  /** For the HostConnectorSetup ask operation. */
  implicit val ImplicitPoolSetupTimeout: Timeout = 30 seconds

  val hostName: String
  val hostPort: Int

  implicit val sslContext = {
    /** Create a trust manager that does not validate certificate chains. */
    val permissiveTrustManager: TrustManager = new X509TrustManager() {
      override def checkClientTrusted(chain: Array[X509Certificate], authType: String): Unit = {
      }
      override def checkServerTrusted(chain: Array[X509Certificate], authType: String): Unit = {
      }
      override def getAcceptedIssuers(): Array[X509Certificate] = {
        null
      }
    }

    val initTrustManagers = Array(permissiveTrustManager)
    val ctx = SSLContext.getInstance("TLS")
    ctx.init(null, initTrustManagers, new SecureRandom())
    ctx
  }

  def initClientPool(): Future[ActorRef] = {
    val hostPoolFuture = for {
      Http.HostConnectorInfo(connector, _) <- IO(Http) ? Http.HostConnectorSetup(hostName, port = hostPort,
        sslEncryption = true)
    } yield connector
  }
}
于 2015-05-19T22:53:30.287 に答える
0

私が仕事に就いた最短はこれです:

IO(Http) ! HostConnectorSetup(host = Conf.base.getHost, port = 443, sslEncryption = true)

つまり、@ reed-sandberg の回答にあるものですが、質問パターンは必要ないようです。接続パラメーターを渡しませんがsendReceive、代わりに:

// `host` is the host part of the service
//
def addHost = { req: HttpRequest => req.withEffectiveUri(true, Host(host, 443)) }

val pipeline: HttpRequest => Future[Seq[PartitionInfo]] = (
    addHost
    ~> sendReceive
    ~> unmarshal[...]
)

これは機能しているように見えますが、当然、このアプローチに欠点があるかどうかを知りたいと思います.

スプレークライアントの SSL サポートに関するすべての批判に同意します。こんな大変なことって、むずかしいですよね。さまざまなソース (SO、スプレーのドキュメント、メーリング リスト) からのデータをマージするのに、おそらく 2 日間を費やしました。

于 2016-05-13T09:19:34.860 に答える
0

sendReceiveカスタムをSSLContext(としてimplicit)渡すことができるこの代替品を思いつきました

def mySendReceive( request: HttpRequest )( implicit uri: spray.http.Uri, ec: ExecutionContext, futureTimeout: Timeout = 60.seconds, sslContext: SSLContext = SSLContext.getDefault): Future[ HttpResponse ] = {

    implicit val clientSSLEngineProvider = ClientSSLEngineProvider { _ =>
        val engine = sslContext.createSSLEngine( )
        engine.setUseClientMode( true )
        engine
    }

    for {
        Http.HostConnectorInfo( connector, _ ) <- IO( Http ) ? Http.HostConnectorSetup( uri.authority.host.address, port = uri.authority.port, sslEncryption = true )
        response <- connector ? request
    } yield response match {
        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 )
    }
}

次に、「通常」として使用します(ほぼ以下を参照):

val pipeline: HttpRequest => Future[ HttpResponse ] = mySendReceive
pipeline( Get( uri ) ) map processResponse

私が本当に好きではないことがいくつかあります:

  • それはハックです。spray-clientカスタムのサポートをネイティブで許可することを期待していSSLContextます。TrustManagersこれらは、通常、カスタムを強制するために、開発およびテスト中に非常に役立ちます

  • implicit uri: spray.http.Uriコネクタのホストとポートのハード コーディングを回避するためのパラメータがあります。したがってuri、宣言する必要がありますimplicit

このコードの改善、またはより良い、へのパッチspray-clientは大歓迎です (SSLEngine の作成の外部化は明らかです)。

于 2015-05-20T08:38:00.110 に答える