MINA, Netty, Twisted Learning Together (11): SSL/TLS

Keywords: Java SSL Netty OpenSSL socket

What is SSL/TLS

Network communication without using SSL/TLS is usually plaintext transmission. Network transmission content is easy to be eavesdropped or even tampered with in the transmission process, which is very insecure. The SSL/TLS protocol is designed to solve these security problems. The SSL/TLS protocol is located on top of TCP/IP protocol. Under each application layer protocol, the content of network transmission is encrypted by encryption algorithm, and only the server and client can encrypt and decrypt. Even if the middleman catches the data packet, he can not decrypt the content of transmission, thus avoiding security problems. For example, the widely used HTTPS protocol is to add a layer of SSL/TLS protocol between TCP protocol and HTTP protocol.

Related terms

Before learning the SSL/TLS protocol, we should first understand some related concepts:
Symmetric encryption: Both encryption and decryption use the same key. The commonly used algorithms are DES, 3DES and AES, which are simpler and faster than asymmetric encryption.  
Asymmetric encryption: Unlike symmetric encryption algorithms, asymmetric encryption algorithms have two keys: public key (which can be public) and private key (private). For example, if the client uses public key encryption, other people's public key can not be decrypted, and can only be decrypted through the server's private key. RSA algorithm is a typical asymmetric encryption algorithm.  
Digital Certificate: Digital certificate is a series of data that contains public key and is issued by authoritative organizations. Many digital certificates need to be paid for, but also free of charge. In addition, digital certificates can be generated by themselves. In this paper, digital certificates will be generated by self-signature.

SSL/TLS process

Before the server and client using the SSL/TLS protocol begin to communicate, a handshake phase will be performed:

  1. Client requests: In this step, the client generates a random number to the server;
  2. Server response: In this step, the server will return a server digital certificate (the certificate contains the public key for encryption) to the client, and the server will generate a random number to the client.
  3. Client response: In this step, the client first verifies the validity of the digital certificate, and then regenerates it into a random number. This random number will be encrypted by asymmetric encryption algorithm (such as RSA algorithm) using the public key in step 2, and then transmitted to the server. The ciphertext can only be decrypted by the server's private key.
  4. The server finally responds: The handshake is over.

After the handshake, both the client and the server have three random numbers at the upper handshake stage. Client and server generate a key randomly through these three. Then all the communication content is encrypted and transmitted by symmetric encryption algorithm. The server and client begin to communicate securely.

If you see here is still a face of confusion, you can refer to An Overview of the Operation Mechanism of SSL/TLS Protocol To get a deeper understanding of the SSL/TLS process, this article will not cover it too much.

Generate private keys and certificates

Use openssl to generate private keys and certificates:

openssl req -x509 -newkey rsa:2048 -nodes -days 365 -keyout private.pem -out cert.crt

After running the above command, a private key file (private.pem) and a certificate file (cert.crt) are generated in the current directory.

The generated private key and certificates Twisted and Netty can be used directly. However, MINA requires that the format of the private key file be converted from pem format to Der format, which is actually to convert the private key of the text file into the private key of the binary file. openssl converts private.pem to private.der private key file:

openssl pkcs8 -topk8 -inform PEM -in private.pem -outform DER -nocrypt -out private.der

SSL/TLS Server

Next on http://xxgblog.com/2014/08/21/mina-netty-twisted-2/ On the basis of this paper, add the layer of SSL/TLS.

MINA

MINA can implement SSL/TLS through SslFilter. The code to initialize SslFilter is tedious:

public class MinaServer {

    public static void main(String[] args) throws Exception {


        String certPath = "/Users/wucao/Desktop/ssl/cert.crt";  // certificate
        String privateKeyPath = "/Users/wucao/Desktop/ssl/private.der";  // private key

        // certificate
        // https://docs.oracle.com/javase/7/docs/api/java/security/cert/X509Certificate.html
        InputStream inStream = null;
        Certificate certificate = null;
        try {
            inStream = new FileInputStream(certPath);
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            certificate = cf.generateCertificate(inStream);
        } finally {
            if (inStream != null) {
                inStream.close();
            }
        }

        // private key
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Files.readAllBytes(new File(privateKeyPath).toPath()));
        PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);

        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(null, null);
        Certificate[] certificates = {certificate};
        ks.setKeyEntry("key", privateKey, "".toCharArray(), certificates);

        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, "".toCharArray());

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(), null, null);

        IoAcceptor acceptor = new NioSocketAcceptor();
        DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
        chain.addLast("ssl", new SslFilter(sslContext));  // SslFilter Need to be at the front
        chain.addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"), "\r\n", "\r\n")));
        acceptor.setHandler(new TcpServerHandle());
        acceptor.bind(new InetSocketAddress(8080));
    }
}


class TcpServerHandle extends IoHandlerAdapter {

    @Override
    public void exceptionCaught(IoSession session, Throwable cause)
            throws Exception {
        cause.printStackTrace();
    }

    @Override
    public void messageReceived(IoSession session, Object message)
            throws Exception {
        String line = (String) message;
        System.out.println("messageReceived:" + line);
    }

    @Override
    public void sessionCreated(IoSession session) throws Exception {
        System.out.println("sessionCreated");
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        System.out.println("sessionClosed");
    }
}

Netty

Netty implements SSL/TLS by adding a SslHandler. Compared with MINA, the code is more concise:

public class NettyServer {

    public static void main(String[] args) throws InterruptedException, SSLException {

        File certificate = new File("/Users/wucao/Desktop/ssl/cert.crt");  // certificate
        File privateKey = new File("/Users/wucao/Desktop/ssl/private.pem");  // private key
        final SslContext sslContext = SslContextBuilder.forServer(certificate, privateKey).build();

        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch)
                                throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();

                            // SslHandler Put it first
                            SslHandler sslHandler = sslContext.newHandler(ch.alloc());
                            pipeline.addLast(sslHandler);

                            pipeline.addLast(new LineBasedFrameDecoder(80));
                            pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));

                            pipeline.addLast(new TcpServerHandler());
                        }
                    });
            ChannelFuture f = b.bind(8080).sync();
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

}

class TcpServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        String line = (String) msg;
        System.out.println("channelRead:" + line);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("channelActive");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        System.out.println("channelInactive");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

Twisted

Twisted implementations of SSL/TLS are also very simple. Replace reactor.listenTCP with reactor.listenSSL

# -*- coding:utf-8 –*-

from twisted.protocols.basic import LineOnlyReceiver
from twisted.internet.protocol import Factory
from twisted.internet import reactor, ssl

sslContext = ssl.DefaultOpenSSLContextFactory(
    '/Users/wucao/Desktop/ssl/private.pem',  # private key
    '/Users/wucao/Desktop/ssl/cert.crt',  # public key
)

class TcpServerHandle(LineOnlyReceiver):

    def connectionMade(self):
        print 'connectionMade'

    def connectionLost(self, reason):
        print 'connectionLost'

    def lineReceived(self, data):
        print 'lineReceived:' + data

factory = Factory()
factory.protocol = TcpServerHandle
reactor.listenSSL(8080, factory, sslContext)
reactor.run()

SSL/TLS Client

Java is also used to write an SSL/TLS client to test the above three server programs. It should be noted that in the introduction of the above SSL/TLS process, in the second step of the SSL/TLS handshake phase, the server will pass the certificate to the client, and in the third step, the client will verify the validity of the certificate. So the following code will first let the client trust the certificate generated by openssl, in order to correctly complete the SSL/TLS handshake.

public class SSLClient {

    public static void main(String args[]) throws Exception {

        // Client Trust Change Certificate, which will be used to verify the validity of certificates passed by the server
        String certPath = "/Users/wucao/Desktop/ssl/cert.crt";
        InputStream inStream = null;
        Certificate certificate = null;
        try {
            inStream = new FileInputStream(certPath);
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            certificate = cf.generateCertificate(inStream);
        } finally {
            if (inStream != null) {
                inStream.close();
            }
        }

        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(null, null);
        ks.setCertificateEntry("cert", certificate);

        TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
        tmf.init(ks);

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

        SSLSocketFactory socketFactory = sslContext.getSocketFactory();

        Socket socket = null;
        OutputStream out = null;

        try {

            socket = socketFactory.createSocket("localhost", 8080);
            out = socket.getOutputStream();

            // Request Server
            String lines = "Abed, I see a silver light\r\n Suspected ground frost\r\n Look at the bright moon\r\n Looking down at home\r\n";
            byte[] outputBytes = lines.getBytes("UTF-8");
            out.write(outputBytes);
            out.flush();

        } finally {
            // Close connection
            out.close();
            socket.close();
        }

    }
}

MINA, Netty, Twisted Learning Series

MINA, Netty, Twisted Learning Together (1): Implementing a Simple TCP Server

MINA, Netty, Twisted Learning Together (2): TCP Message Boundary Problem and Segmentation of Messages by Line

MINA, Netty, Twisted learn together (3): TCP message fixed size prefix (Header)

MINA, Netty, Twisted Learning Together (4): Customize your own protocol

MINA, Netty, Twisted Learning Together (V): Integrating protobuf

MINA, Netty and Twisted learn together (6):

MINA, Netty, Twisted Learning Together (7): Publish/Subscribe

MINA, Netty, Twisted Learning Together (8): HTTP Server

MINA, Netty, Twisted Learning Together (9): Asynchronous IO and Callback Functions

MINA, Netty, Twisted Learning Together (10): Thread Model

MINA, Netty, Twisted Learning Together (11): SSL/TLS

source code

https://github.com/wucao/mina-netty-twisted

Posted by nosheep on Sat, 05 Jan 2019 11:33:09 -0800