2017-04-01 19 views
2

TLSとのTCP接続を介してプロプライエタリプロトコルを話すScalaクライアントを作成したいと思います。 akka documentationからakkaを使用してscalaのTLSでTCP接続を開く方法

var conn_options = { 
     host: endpoint, 
     port: port 
}; 
tlsSocket = tls.connect(conn_options, function() { 
     if (tlsSocket.authorized) { 
     logger.info('Successfully established a connection'); 

     // Now that the connection has been established, let's perform the handshake 
     // Identification frame: 
     // 1 | I | id_size | id 
     var idFrameTypeAndVersion = "1I"; 
     var clientIdString = "foorbar"; 
     var idDataBuffer = new Buffer(idFrameTypeAndVersion.length + 1 + clientIdString.length); 

     idDataBuffer.write(idFrameTypeAndVersion, 0 , 
     idFrameTypeAndVersion.length); 

     idDataBuffer.writeUIntBE(clientIdString.length, 
     idFrameTypeAndVersion.length, 1); 
     idDataBuffer.write(clientIdString, idFrameTypeAndVersion.length + 1, clientIdString.length); 

     // Send the identification frame to Logmet 
     tlsSocket.write(idDataBuffer); 

     } 
     ... 
} 

私は無地のTCP上でアッカとの良好な例を見つけましたが、私はどのように強化するために何の手がかりをしたん:

基本的に、私はScalaでのNode.jsから次のコードを書き換えたいですこの例では、TLSソケット接続を使用しています。 with ssl/tlsの例を示す古いバージョンのドキュメントがありますが、それは新しいバージョンでは見逃されています。

私は、AkkaのTLSオブジェクトに関するドキュメントを見つけましたが、その周りに良い例は見つかりませんでした。

事前に感謝します。

+1

TLSサポートの使用例は、単体テストで確認できます。 https://github.com/akka/akka/blob/master/akka-stream-tests/src/test/scala/akka/stream/io/TlsSpec.scala。それが役に立てば幸い! –

答えて

4

次のコードを使用していて、共有したいと思っています。

基本的に、私はakkaコミュニティから得たTcpTlsEcho.javaを見て始めました。

私はakka-streamsの文書に従った。示し、アッカ・ストリームの使用法を説明するもう一つの非常に良い例は、以下のblog post

接続設定で見つけることができ、流れは次のようになります。

/** 
    +---------------------------+    +---------------------------+ 
    | Flow      |    | tlsConnectionFlow   | 
    |       |    |       | 
    | +------+  +------+ |    | +------+  +------+ | 
    | | SRC | ~Out~> |  | ~~> O2 -- I1 ~~> |  | ~O1~> |  | | 
    | |  |  | LOGG | |    | | TLS |  | CONN | | 
    | | SINK | <~In~ |  | <~~ I2 -- O2 <~~ |  | <~I2~ |  | | 
    | +------+  +------+ |    | +------+  +------+ | 
    +---------------------------+    +---------------------------+ 
**/ 
// the tcp connection to the server 
val connection = Tcp().outgoingConnection(address, port) 

// ignore the received data for now. There are different actions to implement the Sink. 
val sink = Sink.ignore 

// create a source as an actor reference 
val source = Source.actorRef(1000, OverflowStrategy.fail) 

// join the TLS BidiFlow (see below) with the connection 
val tlsConnectionFlow = tlsStage(TLSRole.client).join(connection) 

// run the source with the TLS conection flow that is joined with a logging step that prints the bytes that are sent and or received from the connection. 
val sourceActor = tlsConnectionFlow.join(logging).to(sink).runWith(source) 

// send a message to the sourceActor that will be send to the Source of the stream 
sourceActor ! ByteString("<message>") 

TLS接続の流れがBidiFlowです。私の最初の簡単な例では、すべての証明書を無視し、信頼とキーストアの管理を避けています。例がどのように行われたかは、上記の.javaの例を参照してください。

def tlsStage(role: TLSRole)(implicit system: ActorSystem) = { 
    val sslConfig = AkkaSSLConfig.get(system) 
    val config = sslConfig.config 

    // create a ssl-context that ignores self-signed certificates 
    implicit val sslContext: SSLContext = { 
     object WideOpenX509TrustManager extends X509TrustManager { 
      override def checkClientTrusted(chain: Array[X509Certificate], authType: String) =() 
      override def checkServerTrusted(chain: Array[X509Certificate], authType: String) =() 
      override def getAcceptedIssuers = Array[X509Certificate]() 
     } 

     val context = SSLContext.getInstance("TLS") 
     context.init(Array[KeyManager](), Array(WideOpenX509TrustManager), null) 
     context 
    } 
    // protocols 
    val defaultParams = sslContext.getDefaultSSLParameters() 
    val defaultProtocols = defaultParams.getProtocols() 
    val protocols = sslConfig.configureProtocols(defaultProtocols, config) 
    defaultParams.setProtocols(protocols) 

    // ciphers 
    val defaultCiphers = defaultParams.getCipherSuites() 
    val cipherSuites = sslConfig.configureCipherSuites(defaultCiphers, config) 
    defaultParams.setCipherSuites(cipherSuites) 

    val firstSession = new TLSProtocol.NegotiateNewSession(None, None, None, None) 
     .withCipherSuites(cipherSuites: _*) 
     .withProtocols(protocols: _*) 
     .withParameters(defaultParams) 

    val clientAuth = getClientAuth(config.sslParametersConfig.clientAuth) 
    clientAuth map { firstSession.withClientAuth(_) } 

    val tls = TLS.apply(sslContext, firstSession, role) 

    val pf: PartialFunction[TLSProtocol.SslTlsInbound, ByteString] = { 
     case TLSProtocol.SessionBytes(_, sb) => ByteString.fromByteBuffer(sb.asByteBuffer) 
    } 

    val tlsSupport = BidiFlow.fromFlows(
     Flow[ByteString].map(TLSProtocol.SendBytes), 
     Flow[TLSProtocol.SslTlsInbound].collect(pf)); 

    tlsSupport.atop(tls); 
    } 

    def getClientAuth(auth: ClientAuth) = { 
    if (auth.equals(ClientAuth.want)) { 
     Some(TLSClientAuth.want) 
    } else if (auth.equals(ClientAuth.need)) { 
     Some(TLSClientAuth.need) 
    } else if (auth.equals(ClientAuth.none)) { 
     Some(TLSClientAuth.none) 
    } else { 
     None 
    } 
    } 

そして、完了のために、BidiFlowとして実装されたロギングステージがあります。

def logging: BidiFlow[ByteString, ByteString, ByteString, ByteString, NotUsed] = { 
    // function that takes a string, prints it with some fixed prefix in front and returns the string again 
    def logger(prefix: String) = (chunk: ByteString) => { 
     println(prefix + chunk.utf8String) 
     chunk 
    } 

    val inputLogger = logger("> ") 
    val outputLogger = logger("< ") 

    // create BidiFlow with a separate logger function for each of both streams 
    BidiFlow.fromFunctions(outputLogger, inputLogger) 
} 

私はさらに回答を改善し、更新しようとします。希望が役立ちます。

+0

ようこそスタックオーバーフロー!あなたの答えを共有してくれてありがとう。私の場合はあまりにも具体的ですが、他人を助けるかもしれません。 –

1

Jeremias Wernerの答えが本当に好きでした。しかし、実際のTLSサーバーに当たる というコードを使って、作成する時間が少ないので、以下のコード(彼の答えの影響を強く受けています)を「ワンカットアンドペースト」ソリューションとして提供したいと思います。

import javax.net.ssl.SSLContext 

import akka.NotUsed 
import akka.actor.ActorSystem 
import akka.stream.TLSProtocol.NegotiateNewSession 
import akka.stream.scaladsl.{BidiFlow, Flow, Sink, Source, TLS, Tcp} 
import akka.stream.{ActorMaterializer, OverflowStrategy, TLSProtocol, TLSRole} 
import akka.util.ByteString 

object TlsClient { 

    // Flow needed for TLS as well as mapping the TLS engine's flow to ByteStrings 
    def tlsClientLayer = { 

    // Default SSL context supporting most protocols and ciphers. Embellish this as you need 
    // by constructing your own SSLContext and NegotiateNewSession instances. 
    val tls = TLS(SSLContext.getDefault, NegotiateNewSession.withDefaults, TLSRole.client) 

    // Maps the TLS stream to a ByteString 
    val tlsSupport = BidiFlow.fromFlows(
     Flow[ByteString].map(TLSProtocol.SendBytes), 
     Flow[TLSProtocol.SslTlsInbound].collect { 
     case TLSProtocol.SessionBytes(_, sb) => ByteString.fromByteBuffer(sb.asByteBuffer) 
     }) 

    tlsSupport.atop(tls) 
    } 

    // Very simple logger 
    def logging: BidiFlow[ByteString, ByteString, ByteString, ByteString, NotUsed] = { 

    // function that takes a string, prints it with some fixed prefix in front and returns the string again 
    def logger(prefix: String) = (chunk: ByteString) => { 
     println(prefix + chunk.utf8String) 
     chunk 
    } 

    val inputLogger = logger("> ") 
    val outputLogger = logger("< ") 

    // create BidiFlow with a separate logger function for each of both streams 
    BidiFlow.fromFunctions(outputLogger, inputLogger) 
    } 

    def main(args: Array[String]): Unit = { 
    implicit val system: ActorSystem = ActorSystem("sip-client") 
    implicit val materializer: ActorMaterializer = ActorMaterializer() 

    val source = Source.actorRef(1000, OverflowStrategy.fail) 
    val connection = Tcp().outgoingConnection("www.google.com", 443) 
    val tlsFlow = tlsClientLayer.join(connection) 
    val srcActor = tlsFlow.join(logging).to(Sink.ignore).runWith(source) 

    // I show HTTP here but send/receive your protocol over this actor 
    // Should respond with a 302 (Found) and a small explanatory HTML message 
    srcActor ! ByteString("GET/HTTP/1.1\r\nHost: www.google.com\r\n\r\n") 
    } 
} 
関連する問題