netty 集成 wss 安全链接

netty集成ssl完整参考指南(含完整源码)

 虽然我们在内部rpc通信中使用的是基于认证和报文头加密的方式实现安全性,但是有些时候仍然需要使用SSL加密,可能是因为对接的三方系统需要,也可能是由于open的考虑。中午特地测了下netty下集成ssl的功能,关于ssl的握手过程以及java安全框架中的相关组件说明,请参考如下链接:

http://www.cnblogs.com/zhjh256/p/6262620.html

http://www.cnblogs.com/zhjh256/p/6104537.html

网上搜了下,并没有看到完整的netty ssl示例例子,netty in action中也只是匆匆带过。特详细的测试和整理如下。

首先生成服务端证书:

D:securityserver>keytool -genkey -alias securechat -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass sNetty -storepass sNetty -keystore sChat.jks

D:securityserver>keytool -export -alias securechat -keystore sChat.jks -storepass sNetty -file sChat.cer
存储在文件 <sChat.cer> 中的证书

D:securityserver>cd /d ../client

D:securityclient>keytool -genkey -alias smcc -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass cNetty -storepass cNetty -keystore cChat.jks

D:securityclient>keytool -import -trustcacerts -alias securechat -file ../serversChat.cer -storepass cNetty -keystore cChat.jks
所有者: CN=localhost
发布者: CN=localhost
序列号: 78384348
有效期开始日期: Wed Mar 01 12:48:48 CST 2017, 截止日期: Thu Mar 01 12:48:48 CST 2018
证书指纹:
MD5: 94:83:6C:6D:4B:0D:0B:E6:BF:39:B7:2C:17:29:E8:3C
SHA1: 9A:29:27:41:BE:71:38:C8:13:99:3A:8F:C6:37:C2:95:31:14:B4:98
SHA256: E9:31:40:C7:FC:EA:EF:24:54:EF:4C:59:50:44:CB:1F:9A:35:B7:26:07:2D:3B:1F:BC:30:8E:C0:63:45:4F:21
签名算法名称: SHA256withRSA
版本: 3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 9B 96 0D 50 4A 5E AF 3D 56 25 9C A5 69 C1 3E CC ...PJ^.=V%..i.>.
0010: 32 85 0D A8 2...
]
]

是否信任此证书? [否]: 是
证书已添加到密钥库中

netty服务端源码:

复制代码
package com.ld.net.spider.server;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.net.InetSocketAddress;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SpiderServerBusiHandler extends SimpleChannelInboundHandler<Object> {
    static final Logger logger = LoggerFactory.getLogger(SpiderServerBusiHandler.class);
    
    @Override
    protected void channelRead0(final ChannelHandlerContext ctx, final Object msg)
            throws Exception {
        System.out.println(msg.toString());
    }
    
    @Override 
    public void exceptionCaught(ChannelHandlerContext ctx,  
            Throwable cause) throws Exception {  
        logger.error("channel " + ((InetSocketAddress)ctx.channel().remoteAddress()).toString() + " exception:",cause);
        ctx.close();
    }
}
复制代码
复制代码
package com.ld.net.spider.channel;

import java.nio.charset.Charset;

import javax.net.ssl.SSLEngine;

import com.ld.net.spider.server.SpiderServerBusiHandler;

import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;

public class SslChannelInitializer extends ChannelInitializer<Channel> {
    private final SslContext context;

    public SslChannelInitializer(SslContext context) {
        this.context = context;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        SSLEngine engine = context.newEngine(ch.alloc());
        engine.setUseClientMode(false);
        ch.pipeline().addFirst("ssl", new SslHandler(engine));
        ChannelPipeline pipeline = ch.pipeline(); 
        pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));  
        pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));  //最大16M                
        pipeline.addLast("decoder", new StringDecoder(Charset.forName("UTF-8")));  
        pipeline.addLast("encoder", new StringEncoder(Charset.forName("UTF-8")));  
        pipeline.addLast("spiderServerBusiHandler", new SpiderServerBusiHandler());
    }
}
复制代码
复制代码
package com.ld.net.spider.channel;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;

import java.io.FileInputStream;
import java.security.KeyStore;

import javax.net.ssl.KeyManagerFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SocketServerHelper {
    static final Logger logger = LoggerFactory.getLogger(SocketServerHelper.class);
    private static int WORKER_GROUP_SIZE = Runtime.getRuntime().availableProcessors() * 2; 

    private static EventLoopGroup bossGroup; 
    private static EventLoopGroup workerGroup;  
    
    private static Class<? extends ServerChannel> channelClass;
    
    public static void startSpiderServer() throws Exception {
        ServerBootstrap b = new ServerBootstrap();
        b.childOption(ChannelOption.TCP_NODELAY, true)
        .childOption(ChannelOption.SO_KEEPALIVE, true)
        .childOption(ChannelOption.SO_REUSEADDR, true)    
        .childOption(ChannelOption.ALLOCATOR, new PooledByteBufAllocator(false))
        .childOption(ChannelOption.SO_RCVBUF, 1048576)
        .childOption(ChannelOption.SO_SNDBUF, 1048576);
        
        bossGroup = new NioEventLoopGroup(1);
        workerGroup = new NioEventLoopGroup(WORKER_GROUP_SIZE);
        channelClass = NioServerSocketChannel.class;
        logger.info("workerGroup size:" + WORKER_GROUP_SIZE);
        logger.info("preparing to start spider server...");
        b.group(bossGroup, workerGroup);  
        b.channel(channelClass);
        KeyManagerFactory keyManagerFactory = null;
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(new FileInputStream("D:\security\server\sChat.jks"), "sNetty".toCharArray());
        keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keyStore,"sNetty".toCharArray());
        SslContext sslContext = SslContextBuilder.forServer(keyManagerFactory).build();
        b.childHandler(new SslChannelInitializer(sslContext)); 
        b.bind(9912).sync();  
        logger.info("spider server start sucess, listening on port " + 9912 + ".");  
    }
    
    public static void main(String[] args) throws Exception {
        SocketServerHelper.startSpiderServer();
    }
      
    public static void shutdown() {  
        logger.debug("preparing to shutdown spider server...");
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();  
        logger.debug("spider server is shutdown.");
    }
}
复制代码
复制代码
package com.ld.net.spider.channel;

import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;

public class SocketHelper {
    static final Logger logger = LoggerFactory.getLogger(SocketHelper.class);
    
    public static ChannelFuture writeMessage(Channel channel,String msg) {  
        if(channel!=null){  
            try {
                return channel.writeAndFlush(msg).sync();
            } catch (Exception e) {
                String otherInfo = "";
                
                if(channel.remoteAddress() != null) {
                    otherInfo = "remote address [" + ((InetSocketAddress)channel.remoteAddress()).toString() + "]";
                } else {
                    otherInfo = "channel is null.";
                }
                
                if(e instanceof ClosedChannelException) {
                    logger.error("channel to " + otherInfo + " is closed",e);
                } else {
                    logger.error("timeout occured during channel send msg, " + otherInfo,e);
                }
            }
        }else{
            logger.error("send msg failed, channel is disconnected or not connect. channel is null, please see caller log.");
        }
        return null;
    }
    
    public static ChannelFuture writeMessage(Channel channel,ByteBuf msg) {  
        if(channel!=null){  
            try {
                return channel.writeAndFlush(msg).sync();
            } catch (Exception e) {
                logger.error("timeout occured during channel send msg. remote address is:" + ((InetSocketAddress)channel.remoteAddress()).toString(),e);
            }
        }else{
            logger.error("send msg failed, channel is disconnected or not connect, channel is null, please see caller log.");
        }
        return null;
    }
}
复制代码
原文地址:https://www.cnblogs.com/vana/p/9524428.html