MINA、Netty、Twisted一起学(十一):SSL/TLS

摘要:
什么是SSL/TLS?没有SSL/TLS的网络通信通常以明文传输。网络传输内容在传输过程中很容易被窃听甚至篡改,非常不安全。SSL/TLS协议旨在解决这些安全问题。例如,广泛使用的HTTPS协议在TCP协议和HTTP协议之间添加了一层SSL/TLS协议。如果您在这里仍然感到困惑,可以参考SSL/TLS协议操作机制概述,以进一步了解SSL/TLS过程。本文将不再介绍更多内容。Openssl转换为私有。pem转为私人。der私钥文件:opensslpkcs8-topk8-informPEM-private。pem outformDER nocrypt outprivate。derSSL/TLS服务器下一步http://xxgblog.com/2014/08/21/mina-netty-twisted-2/在文章的基础上添加SSL/TLS层。

什么是SSL/TLS

不使用SSL/TLS的网络通信,一般都是明文传输,网络传输内容在传输过程中很容易被窃听甚至篡改,非常不安全。SSL/TLS协议就是为了解决这些安全问题而设计的。SSL/TLS协议位于TCP/IP协议之上,各个应用层协议之下,使网络传输的内容通过加密算法加密,并且只有服务器和客户端可以加密解密,中间人即使抓到数据包也无法解密获取传输的内容,从而避免安全问题。例如广泛使用的HTTPS协议即是在TCP协议和HTTP协议之间加了一层SSL/TLS协议。

相关术语

在学习SSL/TLS协议之前,首先要了解一些相关概念: 
- 对称加密:加密和解密都采用同一个密钥,常用的算法有DES、3DES、AES,相对于非对称加密算法更简单速度更快。 
- 非对称加密:和对称加密算法不同,非对称加密算法会有两个密钥:公钥(可以公开的)和私钥(私有的),例如客户端如果使用公钥加密,那么即时其他人有公钥也无法解密,只能通过服务器私有的私钥解密。RSA算法即是典型的非对称加密算法。 
- 数字证书:数字证书是一个包含公钥并且通过权威机构发行的一串数据,数字证书很多需要付费购买,也有免费的,另外也可以自己生成数字证书,本文中将会采用自签名的方式生成数字证书。

SSL/TLS流程

使用SSL/TLS协议的服务器和客户端开始通信之前,会先进行一个握手阶段:

  1. 客户端发出请求:这一步客户端会生成一个随机数传给服务器;
  2. 服务器回应:这一步服务器会返回给客户端一个服务器数字证书(证书中包含用于加密的公钥),另外服务器也会生成一个随机数给客户端;
  3. 客户端回应:这一步客户端首先会校验数字证书的合法性,然后会再生成一个随机数,这个随机数会使用第2步中的公钥采用非对称加密算法(例如RSA算法)进行加密后传给服务器,密文只能通过服务器的私钥来解密。
  4. 服务器最后回应:握手结束。

握手结束后,客户端和服务器都有上面握手阶段的三个随机数。客户端和服务器都通过这三个随机生成一个密钥,接下来所有的通信内容都使用这个密钥通过对称加密算法加密传输,服务器和客户端才开始进行安全的通信。

如果看到这里还是一脸懵逼,可以参考SSL/TLS协议运行机制的概述更深入地了解SSL/TLS流程,本文不再过多介绍。

生成私钥和证书

使用openssl来生成私钥和证书:

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

运行以上命令后,会在当前目录下生成一个私钥文件(private.pem)和一个证书文件(cert.crt)。

生成的私钥和证书Twisted、Netty可以直接使用,然而MINA对私钥文件的格式的要求,要将pem格式转换成der格式,实际上就是将文本文件私钥转成二进制文件私钥。openssl将private.pem转成private.der私钥文件:

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

SSL/TLS服务器

接下来在http://xxgblog.com/2014/08/21/mina-netty-twisted-2/一文的基础上,加上SSL/TLS层。

MINA

MINA可以通过SslFilter来实现SSL/TLS,初始化SslFilter的代码比较繁琐:

public class MinaServer {

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


        String certPath = "/Users/wucao/Desktop/ssl/cert.crt";  // 证书
        String privateKeyPath = "/Users/wucao/Desktop/ssl/private.der";  // 私钥

        // 证书
        // 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();
            }
        }

        // 私钥
        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需要放在最前面
        chain.addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"), "
", "
")));
        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通过添加一个SslHandler来实现SSL/TLS,相对MINA来说代码就比较简洁:

public class NettyServer {

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

        File certificate = new File("/Users/wucao/Desktop/ssl/cert.crt");  // 证书
        File privateKey = new File("/Users/wucao/Desktop/ssl/private.pem");  // 私钥
        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要放在最前面
                            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实现SSL/TLS也是非常简单的,将reactor.listenTCP替换为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',  # 私钥
    '/Users/wucao/Desktop/ssl/cert.crt',  # 公钥
)

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客户端

这里还是使用Java来写一个SSL/TLS客户端,用来测试以上三个服务器程序。需要注意的是,在上面SSL/TLS流程的介绍中,SSL/TLS握手阶段的第2步服务器会将证书传给客户端,第3步客户端会校验证书的合法性,所以下面的代码首先会让客户端信任openssl生成的证书,才能正确的完成SSL/TLS握手。

public class SSLClient {

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

        // 客户端信任改证书,将用于校验服务器传过来的证书的合法性
        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();

            // 请求服务器
            String lines = "床前明月光
疑是地上霜
举头望明月
低头思故乡
";
            byte[] outputBytes = lines.getBytes("UTF-8");
            out.write(outputBytes);
            out.flush();

        } finally {
            // 关闭连接
            out.close();
            socket.close();
        }

    }
}

 

MINA、Netty、Twisted一起学系列

MINA、Netty、Twisted一起学(一):实现简单的TCP服务器

MINA、Netty、Twisted一起学(二):TCP消息边界问题及按行分割消息

MINA、Netty、Twisted一起学(三):TCP消息固定大小的前缀(Header)

MINA、Netty、Twisted一起学(四):定制自己的协议

MINA、Netty、Twisted一起学(五):整合protobuf

MINA、Netty、Twisted一起学(六):session

MINA、Netty、Twisted一起学(七):发布/订阅(Publish/Subscribe)

MINA、Netty、Twisted一起学(八):HTTP服务器

MINA、Netty、Twisted一起学(九):异步IO和回调函数

MINA、Netty、Twisted一起学(十):线程模型

MINA、Netty、Twisted一起学(十一):SSL/TLS

MINA、Netty、Twisted一起学(十二):HTTPS

源码

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

免责声明:文章转载自《MINA、Netty、Twisted一起学(十一):SSL/TLS》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇WPF 分页控件SpringBoot整合Mybatis,TypeAliases配置失败的问题下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

使用daphne部署django channles websocket 项目

前言:最近写了一个项目,有部分功能使用django channles websocket写的,使用的链接是wss:// 或者 ws:// ,到真实在uwsgi+nginx部署时,发现wss:// 或者 ws://不可用了,后来查了比较多时间,尝试过修改nginx配置文件,尝试过修改uwsgi配置文件,尝试过使用gunicorn部署,都没有解决此问题。最终发...

用Netty开发中间件:高并发性能优化

用Netty开发中间件:高并发性能优化 最近在写一个后台中间件的原型,主要是做消息的分发和透传。因为要用Java实现,所以网络通信框架的第一选择当然就是Netty了,使用的是Netty 4版本。Netty果然效率很高,不用做太多努力就能达到一个比较高的tps。但使用过程中也碰到了一些问题,个人觉得都是比较经典而在网上又不太容易查找到相关资料的问题,所以在...

如何为网站启用HTTPS加密传输协议

前言 当今时代对上网的安全性要求比以前更高,chrome和firefox也都大力支持网站使用HTTPS,苹果也从2017年开始在iOS 10系统中强制app使用HTTPS来传输数据,微信小程序也是要求必须使用HTTPS请求,由此可见HTTPS势在必行。 部落在这里主要给大家介绍一下什么是HTTPS,以及如何使用Let’s Encrypt免费证书为网站启用H...

使用 Python 和 Oracle 数据库实现高并发性

随着趋势发展的核心转向更多而不是更快发展,最大限度地提高并发性的重要性日益凸显。并发性使得编程模式发生了新的转变,可以编写异步代码,从而将多个任务分散到一组线程或进程中并行工作。如果您不是编程新手并且很熟悉 C 或 C++,您可能已经对线程和进程有所了解,并且知道它们之间的区别。在进行并发编程时,线程提供了进程的轻量级替代物,在大多数情况下多线程较多进程更...

邮件基本概念及发送方式

 一:邮件发送的基本介绍   在工作中我相信大家会经常和邮件打交道,用邮件来进行信息的交流和汇报工作情况;但是在我们程序员眼里,邮件的用处还是挺广泛的,比如我们在注册账号完成时平台会发送一封邮件给我们,让我们点击邮件里的链接来激活当前注册的账号;其实邮件还可以实现验证码的发送、用户操作提醒、活动通知等功能 1:邮件基本介绍 邮件服务器: 邮件服务器...

Swoole从入门到入土(17)——WebSocket服务器[成员函数与配置选项]

这一节,我们重点了解websocket server的成员函数以及相关的配置选项。 1、成员函数 push:向 WebSocket 客户端连接推送数据,长度最大不得超过 2M。 SwooleWebSocketServer->push(int $fd, string $data, int $opcode = WEBSOCKET_OPCODE_TEXT,...