当前位置: 首页 > 工具软件 > SSL Decoder > 使用案例 >

netty与ssl

费辰阳
2023-12-01

一、下载

Netty是最新的5.0 Alpha2版本,下载地址是:http://dl.bintray.com/netty/downloads/netty-5.0.0.Alpha2.tar.bz2

二、生成自签证书

打开cmd输入

keytool -genkey -alias securechat -keysize 2048 -validity 
365 -keyalg RSA -dname "CN=localhost" -keypass sNetty 
-storepass sNetty -keystore sChat.jks
即可生成一个名为sChat.jks的文件
 
 

keytool为JDK提供的生成证书工具

  • -keysize 2048 密钥长度2048位(这个长度的密钥目前可认为无法被暴力破解)
  • -validity 365 证书有效期365天
  • -keyalg RSA 使用RSA非对称加密算法
  • -dname "CN=gornix.com" 设置Common Name为gornix.com,这是我的域名
  • -keypass 654321 密钥的访问密码为654321
  • -storepass 123456 密钥库的访问密码为123456(其实这两个密码也可以设置一样,通常都设置一样,方便记)
  • -keystore gornix.jks 指定生成的密钥库文件为gornix.jks

完了之后就拿到了gornix.jks这个密钥库文件了,把它放到自己的home目录下,比如:/home/guogangj/gornix.jks

三、生成SSLContent

keyStore ks = KeyStore.getInstance("JKS");
InputStream ksInputStream = new FileInputStream("/home/guogangj/gornix.jks");
ks.load(ksInputStream, "123456".toCharArray());
keyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, "654321".toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), null, null);
这个过程在整个程序周期只需要做一次,最好try-catch一下,以便检查异常,好了之后保存好sslContext,后面用到
四、设置netty的ssl
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(false); //服务器端模式
sslEngine.setNeedClientAuth(false); //不需要验证客户端
socketChannel.pipeline().addLast("ssl", new SslHandler(sslEngine)); //搞定
sslContext就是前面生成的那个sslContext,通常可以用参数的形式传入到ChannelInitializer中。还有,记得把SslHandler放在其它Handler的前面。


五:
最后放一个创建netty服务器包含ssl以及连接握手的例子;
首先是使用ssl文件生辰sslContentext:
public final class ModbusSslContextFactory {
    private static OMCLog log = OMCLogFactory.getOMCLog(ModbusSslContextFactory.class);
    private static final SSLContext SERVER_CONTEXT;
    private static final String key_file = "server_keystore.jks";
    private static final String CERT_ALGORITHM_TYPE = "SunX509";
    private static final String KEYSTORE_TYPE = "JKS";
    private static final String KEYSTORE_PASS = "NetEco123";

    static{

        SSLContext sslContext = null;
        try{
            sslContext = SSLContext.getInstance("SSLv3");
        }
        catch(NoSuchAlgorithmException e1){
            log.error("have Exception:",e1);
        }

        try{
            if(null != getKeyManagers() && null != getTrustManagers()){
                sslContext.init(getKeyManagers(), getTrustManagers(), null);
            }
        }
        catch(KeyManagementException e){
            log.error("have Exception:",e);
        }
        catch(IOException e){
            log.error("have Exception:",e);
        }
        catch(GeneralSecurityException e){
            log.error("have Exception:",e);
        }
        sslContext.createSSLEngine().getSupportedCipherSuites();

        SERVER_CONTEXT = sslContext;

    }

    public static SSLContext getServerContext() {
        return SERVER_CONTEXT;
    }

    private ModbusSslContextFactory() {
    }

    private static FileInputStream getKeyFileIs() throws Exception {
        try{
            URL keyFileUrl = ModbusSslContextFactory.class.getClassLoader()
                .getResource("spring/" + key_file);
            FileInputStream is = new FileInputStream(keyFileUrl.getFile());
            return is;
        }
        catch(Exception e){
            log.error("have Exception:",e);
            throw e;
        }
    }

    private static KeyManager[] getKeyManagers() throws IOException,
        GeneralSecurityException {
        FileInputStream is = null;
        KeyStore ks = null;
        KeyManagerFactory kmFact = null;

        KeyManager[] kms = null;
        try{
            // 获得KeyManagerFactory对象.
            kmFact = KeyManagerFactory.getInstance(CERT_ALGORITHM_TYPE);

            is = getKeyFileIs();
            ks = KeyStore.getInstance(KEYSTORE_TYPE);
            ks.load(is, KEYSTORE_PASS.toCharArray());

            // 使用获得的KeyStore初始化KeyManagerFactory对象
            kmFact.init(ks, KEYSTORE_PASS.toCharArray());

            // 获得KeyManagers对象
            kms = kmFact.getKeyManagers();
        }
        catch(Exception e){
            log.error("have Exception:",e);
        }
        finally{
            if(is != null){
                is.close();
            }
        }
        return kms;
    }

    private static TrustManager[] getTrustManagers() throws IOException,
        GeneralSecurityException {
        FileInputStream is = null;
        TrustManagerFactory kmFact = null;
        KeyStore ks = null;
        TrustManager[] kms = null;

        // 获得KeyManagerFactory对象.
        try{
            kmFact = TrustManagerFactory.getInstance(CERT_ALGORITHM_TYPE);

            // 配置KeyManagerFactory对象使用的KeyStoree.我们通过一个文件加载
            // is = new FileInputStream(cert_path);
            is = getKeyFileIs();
            ks = KeyStore.getInstance(KEYSTORE_TYPE);
            ks.load(is, KEYSTORE_PASS.toCharArray());
            // 使用获得的KeyStore初始化KeyManagerFactory对象
            kmFact.init(ks);

            // 获得KeyManagers对象
            kms = kmFact.getTrustManagers();
        }
        catch(Exception e){

            log.error("have Exception:",e);
        }
        finally{
            if(is != null){
                is.close();
            }
        }
        return kms;
    }
}
 
接着是服务端设置并绑定端口
public void run() throws Exception {

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .handler(new LoggingHandler(LogLevel.DEBUG))//设置父类handler
             .childHandler(new ModbusServerInitializer())//
             .option(ChannelOption.SO_REUSEADDR, true)
             .option(ChannelOption.TCP_NODELAY, true)
             .option(ChannelOption.SO_KEEPALIVE, true)
             .option(ChannelOption.SO_LINGER, 0)
             .option(ChannelOption.SO_BACKLOG, 1500)//设置两队列大小
             .option(ChannelOption.SO_RCVBUF, 1024 * 256)
             .option(ChannelOption.SO_SNDBUF, 1024 * 256)
             .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)//池化内存
             .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
            //绑定端口
            b.bind(port).sync();
            log.info("init modbus server complete");
        } catch(Exception e){
            log.error("init modbus server exception:",e);
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
 其中的ModbusServerInitializer类如下:
public class ModbusServerInitializer extends ChannelInitializer<SocketChannel>{

    private static final OMCLog log = OMCLogFactory
    .getOMCLog(ModbusServerInitializer.class);
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        
        ChannelPipeline pipeline = ch.pipeline();
        
        
        //add a ssl filter
        if(MedConstant.MODBUS_SSL_ENABLE){
        	SSLEngine engine = ModbusSslContextFactory.getServerContext().createSSLEngine();
            
            engine.setUseClientMode(false);
            
            engine.setNeedClientAuth(true);
            
        	pipeline.addLast("ssl", new SslHandler(engine));
        }
        
        pipeline.addLast("idleHandler",new ModbusIdleHandler(MedConstant.MODBUS_SERVER_READIDLE, 
        													 MedConstant.MODBUS_SERVER_WRITEIDLE, 
        													 MedConstant.MODBUS_SERVER_ALLIDLE));
        
        //pipeline.addLast("decoder", new ModbusMessageDecoder(/*ByteOrder.BIG_ENDIAN,1024*10,5,1,0,0,true*/));
        
        pipeline.addLast("decoder", new LengthDecoder(ByteOrder.BIG_ENDIAN,1024*10,4,2,0,0,true));

        
        pipeline.addLast("encoder", new ModbusMessageEncoder());
        
        pipeline.addLast("handler", new ModbusServerHandler());
        
        //pipeline.addLast("idleHandler", new ModbusIdleHandler(3 * 60, 90, 5 * 60));

    }
    
    private static class ModbusIdleHandler extends IdleStateHandler{
        
        public ModbusIdleHandler(int readerIdleTimeSeconds,
            int writerIdleTimeSeconds, int allIdleTimeSeconds) {
            super(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds);
        }
        
        @Override
        protected void channelIdle(ChannelHandlerContext ctx,IdleStateEvent evt) throws Exception {
            
            //log.info("the channl is idle:"+ctx.channel().remoteAddress());
            if(IdleStateEvent.class.isAssignableFrom(evt.getClass())){  
                IdleStateEvent event = (IdleStateEvent) evt;  
                if(event.state() == IdleState.READER_IDLE){
                    log.error("the channel is read idle:"+ctx.channel().remoteAddress()+":will be closed");
                    ctx.close();
                }  
                else if(event.state() == IdleState.WRITER_IDLE){
                    log.error("the channel is write idle:"+ctx.channel().remoteAddress()+":will be heart beat");
                    sendHeartBeatFrame(ctx); 
                }
                else if(event.state() == IdleState.ALL_IDLE){
                    log.error("the channel is all idle");
                }   
            }  
        }
        /**
         * 在此线程上做同步操作会阻塞single线程池接收报文,所以用异步
         * @param ctx
         */
        private void sendHeartBeatFrame(ChannelHandlerContext ctx){
            String ip = IecUtils.getIpByChannel(ctx);
            ReadAndWriteRegisters rw = new ReadAndWriteRegisters();
            if(ip != null && !"".equals(ip)){
                rw.readRegisterAsyn(ip, 0, ModbusConstant.HEART_BEAT_ADDRESS, 1);
                log.debug("the smart idle heartbeat info is:"+ip);
            }
        }
        
    }

}
 
其中包含了设置sslhandler
最后是握手环节,握手是在ModbusServerHandler中,也就是接收消息发送连接创建的业务类:
@Override
    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
    	if(MedConstant.MODBUS_SSL_ENABLE){
        ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
                new GenericFutureListener<Future<Channel>>() {
            //when handshake complete,start search devices
            @Override
            public void operationComplete(Future<Channel> future) throws Exception {
                log.info("the handshake is success: "+future.isSuccess()+";"+future.cause());
                if(future.isSuccess()){
                    
                  //  握手成功后要做的事
                }
            }
        });
    	}else{
    		//没有使用ssl就不握手直接是连接建立后。。。
    	}
    }
 
 类似资料: