Essential support of TLS for fs2.
This library adds sport of TLS for fs2 programs. Library provides two levels of TLS:
- TLSEngine A Low level abstraction of java's
SSLEngine
allowing asynchronous functional usage ofSSLEngine
- TLSSocket A wrapper of standard
fs2.io.tcp.Socket
that allows to receive/send data over TLS/SSL
Add this to your sbt build file :
libraryDependencies += "com.spinoco" %% "fs2-crypto" % "0.4.0"
version | scala | fs2 |
---|---|---|
0.5.0-M1 | 2.11, 2.12, 2.13 | 1.1.0-M1 |
0.4.0 | 2.11, 2.12 | 1.0.0 |
0.2.0 | 2.11, 2.12 | 0.10.0 |
0.1.2 | 2.11, 2.12 | 0.9.7 |
To create encrypted TLS socket, library allows very simple usage by just using current fs2.io.tcp.Socket
.
User just need to create tcp socket and then pass it to TLSSocket
that creates encrypted version of the socket.
Following is the example how this can be done in scala :
import javax.net.ssl._
import java.net.InetSocketAddress
import fs2.io.tcp.client
import spinoco.fs2.crypto._
import spinoco.fs2.crypto.io.tcp.TLSSocket
import java.nio.channels.AsynchronousChannelGroup
import java.nio.channels.spi.AsynchronousChannelProvider
import java.util.concurrent.{Executors, ThreadFactory}
import cats.effect.IO._
import scala.concurrent.ExecutionContext
import scala.{Stream => _ }
import fs2.Stream
implicit val executionContext: ExecutionContext = ExecutionContext.Implicits.global
val sslCtx = SSLContext.getInstance("TLS")
sslCtx.init(null, null, null)
implicit val tcpACG: AsynchronousChannelGroup = AsynchronousChannelProvider
.provider()
.openAsynchronousChannelGroup(Executors.newCachedThreadPool(), 8)
val ctx = SSLContext.getInstance("TLS")
ctx.init(null, null, null)
val engine = sslCtx.createSSLEngine()
engine.setUseClientMode(true)
val address = new InetSocketAddress("127.0.0.1", 6060)
client(address) flatMap { socket => TLSSocket(socket, engine) } flatMap { tlsSocket =>
// perform any operations with tlsSocket as you would with normal Socket.
???
}
Note that client initiates initial TLS handshake. As such, client needs to send always some data to server to trigger SSL handshake. It is engough to send empty chunk of bytes to trigger initial TLS handshake from client side.
Java's SSLEngine
is not most pleasant api to work with, and therefore there is TLSEngine
. TLSEngine
wraps SSLEngine
and
provides nonblocking asynchronous interface to SSLEngine
that can be used to create TLS (with java 9 even DTLS) encrypted
connections.
TLSEngine
is used to create TLSSocket
, which can be used as real example on how TLSEngine can construct the
higher level abstractions. TLSEgine
provides two simple methods:
- encrypt to encrypt data from application and send resulting data over transport layer
- decrypt to decrypt data received from network transport.
When encrypting data with TLSEngine, you use the encrypt
method that may return any of three results:
Encrypted(data)
indicating that data was successfully encrypted supplying the resulting frame to be sent over networkClosed
indicating that engine is closed and no more application data may be encryptedHandshake(data, next)
providing data to be sent to network party and next operation to perfom immediately after the data will be send to remote party, but before any otherencrypt
invocation.
When decrypting data with TLSEngine, you use the decrypt
method, that may return any of three possible results:
Decrypted(data)
indicating data that were decrypted. Note that data may be empty, for example in case the received data was not enough to form full TLS Record. If the data supplied to decrypt forms multiple TLS records, then this will return content of both TLS Recors, concatenated.Closed
idicating that engine is closed and no more data can be decrypted.Handshake(data, signalSentEventually)
providing data to be sent to remote party during handshake. EventuallysignalSentEventually
may be provided to perform operation immediately when thedata
will be sent to network, before any otherdecrypt
.