Java,Netty,SSL认证,代码案例,单向认证和双向认证的代码分享

Java
258
0
0
2023-12-21

ssl 认证、单向认证和双向认证

SSL认证,客户端到服务器端的认证,主要用来提供对用户和服务器的认证,对传送的数据进行加密和隐藏,确保数据在传送中不被改变,即数据的完整性,现已成为该领域中全球化的标准。

单向认证,服务端部署 SSL证书 就行,任何用户都可以去访问(IP被限制除外等),只是服务端提供了身份认证。

双向认证,服务端部署SSL证书,也需要客户端提供身份认证,只有服务端允许的客户端才能访问,安全性相对于要高一些。

客户端&客户端代码总结:

 // false为服务器端模式,true为客户端模式
 SSL Engine.setUseClientMode(false);
//false为单向认证,true为双向认证
sslEngine.setNeedClientAuth(true); 

单向认证代码

 <dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdkto18</artifactId>
    <version>.70</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdkto18</artifactId>
    <version>.70</version>
</dependency>
<!--  netty -all -->
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>.1.60.Final</version>
</dependency> 

服务器端:

 package com.what.netty01.demo02.server;

import io.netty.bootstrap.Server Bootstrap ;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel. socket .nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class  Secure ChatServer {

    public  void  run(int port) throws Interrupted Exception  {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup,  workGroup )
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new SecureChatServerInitializer());

            ChannelFuture cf = b.bind(port).sync();

            cf.channel().closeFuture().sync();

        } finally {
            bossGroup. shutdown Gracefully();
            workGroup.shutdownGracefully();
        }

    }

    public  static  void main(String[] args) throws InterruptedException {
        new SecureChatServer().run();
    }

} 
 package com.what.netty01.demo02.server;

import  javax .net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import  java .io. File InputStream;
import java.io.IOException;
import java.io. InputStream ;
import java.security.KeyStore;

public class SecureChatServerSslContextFactory {

     private  static final String PROTOCOL = "TLS";

    //服务器安全套接字协议
    private static SSLContext SERVER_CONTEXT;

    /**
     * @param pkPath
     * @return
     */
    public static SSLContext getServerContext(String pkPath) {
        if (SERVER_CONTEXT != null) {
            return SERVER_CONTEXT;
        }
        InputStream in = null;
        try {
            //密钥管理器
            KeyManagerFactory kmf = null;
            if (pkPath != null) {
                //密钥库KeyStore
                KeyStore ks = KeyStore. getInstance ("JKS");
                //加载服务端证书
                in = new FileInputStream(pkPath);
                //加载服务端的KeyStore  ;sNetty是生成仓库时设置的密码,用于检查密钥库完整性的密码
                ks.load(in, "".toCharArray());
                kmf = KeyManagerFactory.getInstance("SunX");
                //初始化密钥管理器
                kmf.init(ks, "".toCharArray());
            }
            //获取安全套接字协议( TLS协议 )的对象
            SERVER_CONTEXT = SSLContext.getInstance(PROTOCOL);
            //初始化此上下文
            //参数一:认证的密钥      参数二:对等信任认证  参数三: 伪随机数 生成器 。 由于单向认证,服务端不用验证客户端,所以第二个参数为null
            SERVER_CONTEXT.init(kmf.getKeyManagers(), null, null);
        } catch (Exception e) {
            throw new Error("Failed to initialize the server-side SSLContext", e);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return SERVER_CONTEXT;
    }

} 
 package com.what.netty01.demo02.server;

import javax.net.ssl.SSLEngine;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslHandler;

public class SecureChatServerInitializer  extends  ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel sc) throws Exception {
        ChannelPipeline pipeline = sc.pipeline();
        String sChatPath = "d:/server.jks";
        SSLEngine engine = SecureChatServerSslContextFactory.getServerContext(sChatPath).createSSLEngine();
        engine.setUseClientMode(false);//设置为服务器模式
        //engine.setNeedClientAuth(false);//不需要客户端认证,默认为false,故不需要写这行。
        pipeline.addLast("ssl", new SslHandler(engine));
        // On top of the SSL handler, add the text line codec.
        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        // and then business logic.
        pipeline.addLast("handler", new SecureChatServerHandler());
    }

} 
 package com.what.netty01.demo02.server;

import java.net.InetAddress;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.GlobalEventExecutor;

public class SecureChatServerHandler extends SimpleChannelInboundHandler<String> {

    static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    protected void channelRead(ChannelHandlerContext ctx, String msg) throws Exception {
        // Send the received message to all channels but the current one.
        for (Channel c : channels) {
             if  (c != ctx.channel()) {
                c.writeAndFlush("[" + ctx.channel().remoteAddress() + "] " + msg + '\n');
            } else {
                c.writeAndFlush("[you] " + msg + '\n');
            }
        }
        // Close the connection if the client has sent 'bye'.
        if ("bye".equals(msg.toLowerCase())) {
            ctx.close();
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // Once session is secured, send a greeting and register the channel to
        // the global channel
        // list so the channel received the messages from others.
        ctx.pipeline().get(SslHandler.class).handshakeFuture()
                .addListener(new GenericFutureListener<Future<Channel>>() {

                    @Override
                    public void operationComplete(Future<Channel> arg)
                            throws Exception {
                        ctx.writeAndFlush("Welcome to " + InetAddress.getLocalHost().getHostName() + " secure chat service!\n");
                        ctx.writeAndFlush("Your session is protected by " + ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite() + " cipher suite.\n");
                        channels.add(ctx.channel());

                    }
                });

    }

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

} 

客户端:

 package com.what.netty01.demo02.client;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

public class SecureChatClient {

    public void start(String host,int port) throws Exception{
        EventLoopGroup group = new NioEventLoopGroup();
        try{
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class)
                    .handler(new SecureChatClientInitializer());

            // Start the connection attempt.
            Channel ch = b.connect(host, port).sync().channel();
            // Read commands from the stdin.
            ChannelFuture lastWriteFuture = null;
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    System.in));
            for (;;) {
                String line = in.readLine();
                if (line == null) {
                    break;
                }
                // Sends the received line to the server.
                lastWriteFuture = ch.writeAndFlush(line + "\r\n");
                // If user typed the 'bye' command, wait until the server closes
                // the connection.
                if ("bye".equals(line.toLowerCase())) {
                    ch.closeFuture().sync();
                    break;
                }
            }
            // Wait until all messages are flushed before closing the channel.
            if (lastWriteFuture != null) {
                lastWriteFuture.sync();
            }
        }finally{
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new SecureChatClient().start("localhost",);

    }

} 
 package com.what.netty01.demo02.client;

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

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

public final class SecureChatClientSslContextFactory {

    private static final String PROTOCOL = "TLS";

    //客户端安全套接字协议
    private static SSLContext CLIENT_CONTEXT;

    public static SSLContext getClientContext(String caPath) {
        if (CLIENT_CONTEXT != null) {
            return CLIENT_CONTEXT;
        }
        InputStream tIN = null;
        try {
            //信任库
            TrustManagerFactory tf = null;
            if (caPath != null) {
                //密钥库KeyStore
                KeyStore tks = KeyStore.getInstance("JKS");
                //加载客户端证书
                tIN = new FileInputStream(caPath);
                tks.load(tIN, "".toCharArray());
                tf = TrustManagerFactory.getInstance("SunX");
                // 初始化信任库
                tf.init(tks);
            }
            CLIENT_CONTEXT = SSLContext.getInstance(PROTOCOL);
            //设置信任证书
            TrustManager[] gtf = tf.getTrustManagers();
            CLIENT_CONTEXT.init(null, tf == null ? null : gtf, null);

        } catch (Exception e) {
            throw new Error("Failed to initialize the client-side SSLContext");
        } finally {
            if (tIN != null) {
                try {
                    tIN.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return CLIENT_CONTEXT;
    }

} 
 package com.what.netty01.demo02.client;

import javax.net.ssl.SSLEngine;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslHandler;

public class SecureChatClientInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        String cChatPath = "d:/client.jks";
        //创建SSLEngine
        SSLEngine engine = SecureChatClientSslContextFactory.getClientContext(cChatPath).createSSLEngine();
        //客户方模式
        engine.setUseClientMode(true);
        pipeline.addLast("ssl", new SslHandler(engine));
        // On top of the SSL handler, add the text line codec.
        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        // and then business logic.
        pipeline.addLast("handler", new SecureChatClientHandler());
    }

} 
 package com.what.netty01.demo02.client;

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

public class SecureChatClientHandler extends SimpleChannelInboundHandler<String> {

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

    @Override
    protected void channelRead(ChannelHandlerContext ctx, String msg) throws Exception {
        System.err.println(msg);
    }

} 

双向认证代码

 package com.what.netty01.demo03;

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

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

/**
 * 初始化sslcontext类
 */
public class ContextSSLFactory {

    private static final SSLContext SSL_CONTEXT_S;

    private static final SSLContext SSL_CONTEXT_C;

    static {
        SSLContext sslContext = null;
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSLv");
            sslContext = SSLContext.getInstance("SSLv3");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        try {
            if (getKeyManagersServer() != null && getTrustManagersServer() != null) {
                sslContext.init(getKeyManagersServer(), getTrustManagersServer(), null);
            }
            if (getKeyManagersClient() != null && getTrustManagersClient() != null) {
                sslContext.init(getKeyManagersClient(), getTrustManagersClient(), null);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        sslContext.createSSLEngine().getSupportedCipherSuites();
        sslContext.createSSLEngine().getSupportedCipherSuites();
        SSL_CONTEXT_S = sslContext;
        SSL_CONTEXT_C = sslContext;
    }

    public ContextSSLFactory() {

    }

    public static SSLContext getSslContext() {
        return SSL_CONTEXT_S;
    }

    public static SSLContext getSslContext() {
        return SSL_CONTEXT_C;
    }

    private static TrustManager[] getTrustManagersServer() {
        FileInputStream is = null;
        KeyStore ks = null;
        TrustManagerFactory keyFac = null;

        TrustManager[] kms = null;
        try {
            // 获得KeyManagerFactory对象. 初始化位默认算法
            keyFac = TrustManagerFactory.getInstance("SunX");
            is = new FileInputStream("d:/server.jks");
            ks = KeyStore.getInstance("JKS");
            String keyStorePass = "";
            ks.load(is, keyStorePass.toCharArray());
            keyFac.init(ks);
            kms = keyFac.getTrustManagers();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return kms;
    }

    private static TrustManager[] getTrustManagersClient() {
        FileInputStream is = null;
        KeyStore ks = null;
        TrustManagerFactory keyFac = null;

        TrustManager[] kms = null;
        try {
            // 获得KeyManagerFactory对象. 初始化位默认算法
            keyFac = TrustManagerFactory.getInstance("SunX");
            is = new FileInputStream("d:/client.jks");
            ks = KeyStore.getInstance("JKS");
            String keyStorePass = "";
            ks.load(is, keyStorePass.toCharArray());
            keyFac.init(ks);
            kms = keyFac.getTrustManagers();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return kms;
    }

    private static KeyManager[] getKeyManagersServer() {
        FileInputStream is = null;
        KeyStore ks = null;
        KeyManagerFactory keyFac = null;

        KeyManager[] kms = null;
        try {
            // 获得KeyManagerFactory对象. 初始化位默认算法
            keyFac = KeyManagerFactory.getInstance("SunX");
            is = new FileInputStream("d:/server.jks");
            ks = KeyStore.getInstance("JKS");
            String keyStorePass = "";
            ks.load(is, keyStorePass.toCharArray());
            keyFac.init(ks, keyStorePass.toCharArray());
            kms = keyFac.getKeyManagers();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return kms;
    }

    private static KeyManager[] getKeyManagersClient() {
        FileInputStream is = null;
        KeyStore ks = null;
        KeyManagerFactory keyFac = null;

        KeyManager[] kms = null;
        try {
            // 获得KeyManagerFactory对象. 初始化位默认算法
            keyFac = KeyManagerFactory.getInstance("SunX");
            is = new FileInputStream("d:/client.jks");
            ks = KeyStore.getInstance("JKS");
            String keyStorePass = "";
            ks.load(is, keyStorePass.toCharArray());
            keyFac.init(ks, keyStorePass.toCharArray());
            kms = keyFac.getKeyManagers();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return kms;
    }
} 

服务器端:

 package com.what.netty01.demo03.server;


import javax.net.ssl.SSLEngine;

import com.what.netty01.demo03.ContextSSLFactory;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslHandler;

public class NettySocketServer {

    private static SslHandler sslHandler = null;

    private EventLoopGroup bossGroup = null;

    private EventLoopGroup workerGroup = null;

    public void start() {
        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverStrap = new ServerBootstrap();
            serverStrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, * 5 * 60)
                    .handler(new LoggingHandler(LogLevel.DEBUG))
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pie = socketChannel.pipeline();
                            pie.addLast("decoder", new MyServerDecoder());
                            pie.addLast("encoder", new MyServerEncoder());
                            pie.addLast("handler", new NettySocketSSLHandler());
                            SSLEngine engine = ContextSSLFactory.getSslContext().createSSLEngine();
                            engine.setUseClientMode(false);
                            engine.setNeedClientAuth(true);
                            pie.addFirst("ssl", new SslHandler(engine));
                        }

                    });
            serverStrap.bind().sync();
            System.out.println("服务已开启...");
        } catch (Exception e) {
            e.printStackTrace();
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

    private SslHandler getSslHandler() {
        if (sslHandler == null) {
            SSLEngine sslEngine = ContextSSLFactory.getSslContext().createSSLEngine();
            // false为服务器端模式,true为客户端模式
            sslEngine.setUseClientMode(false);
            //false为单向认证,true为双向认证
            sslEngine.setNeedClientAuth(true);
            sslHandler = new SslHandler(sslEngine);
        }
        return sslHandler;
    }

    public static void main(String[] args) {
        new NettySocketServer().start();
    }

}
 package com.what.netty01.demo03.server;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

import java.net.InetAddress;
import java.nio.ByteBuffer;

public class NettySocketSSLHandler extends SimpleChannelInboundHandler<ByteBuffer> {

    @Override
    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
        // Once session is secured, send a greeting and register the channel to the global channel
        // list so the channel received the messages from others.
        ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
                new GenericFutureListener<Future<Channel>>() {
                    @Override
                    public void operationComplete(Future<Channel> future) throws Exception {
                        if (future.isSuccess()) {
                            System.out.println("握手成功");
                            byte[] array = new byte[]{(byte)d, 04};
                            ByteBuffer bu = ByteBuffer.wrap(array);
                            ctx.channel().writeAndFlush(bu);
                        } else {
                            System.out.println("握手失败");
                        }
                        ctx.writeAndFlush(
                                "Welcome to " + InetAddress.getLocalHost().getHostName() +
                                        " secure chat service!\n");
                        ctx.writeAndFlush(
                                "Your session is protected by " +
                                        ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite() +
                                        " cipher suite.\n");

                    }
                });
    }

    @Override
    protected void channelRead(ChannelHandlerContext ctx, ByteBuffer byteBuffer) throws Exception {
        ctx.channel().writeAndFlush(byteBuffer);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx)
            throws Exception {
        System.out.println("增加:" + ctx.channel().remoteAddress());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        System.out.println("移除:" + ctx.channel().remoteAddress());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("Unexpected exception from downstream.");
        ctx.close();
    }


} 
 package com.what.netty01.demo03.server;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

import java.nio.ByteBuffer;

public class MyServerEncoder extends MessageToByteEncoder<ByteBuffer>{

    @Override
    protected void encode(ChannelHandlerContext ctx, ByteBuffer message,
                          ByteBuf out) throws Exception {
        if(message==null){
            return;
        }
        if(message.hasArray()){
            byte[] msg =message.array();
            if(msg == null || msg.length <=){
                return;
            }
            out.writeBytes(msg) ;
        }
    }

} 
 package com.what.netty01.demo03.server;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.nio.ByteBuffer;
import java.util.List;

public class MyServerDecoder extends ByteToMessageDecoder {


    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
                          List<Object> out) throws Exception {
        //UnpooledUnsafeDirectByteBuf(ridx:, widx: 1, cap: 1024)
        if (buffer != null) {
            ByteBuffer msg = null;
            try {
                if(buffer.readableBytes() > ){
                    msg = ByteBuffer.allocate(buffer.readableBytes()) ;
                    byte[] bb = new byte[buffer.readableBytes()] ;
                    buffer.readBytes(bb) ;
                    msg.put(bb);
                    msg.flip();
                }
            } catch (Exception e) {
                e.printStackTrace();
                msg = null ;
            }
            if (msg != null) {
                out.add(msg);
            }
        }
    }

} 

客户端:

 package com.what.netty01.demo03.client;

import java.net.InetSocketAddress;
import java.net.SocketAddress;

import javax.net.ssl.SSLEngine;

import com.what.netty01.demo03.ContextSSLFactory;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslHandler;

public class NettySocketClient {

    private EventLoopGroup group ;

    private Channel channel = null ;
    public void connect(String ip , int port){
        group = new NioEventLoopGroup();
        try{
            Bootstrap strap = new Bootstrap();
            strap.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .option(ChannelOption.SO_KEEPALIVE , true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pieple = socketChannel.pipeline() ;
                            pieple.addLast("decoder" , new MyClientDecoder()) ;
                            pieple.addLast("encoder" , new MyClientEncoder()) ;
                            pieple.addLast("handler" , new NettySocketSSLClientHandler()) ;
                            SSLEngine engine = ContextSSLFactory.getSslContext().createSSLEngine();
                            engine.setUseClientMode(true);
                            pieple.addFirst("ssl", new SslHandler(engine));
                        }
                    });
            SocketAddress address = new InetSocketAddress(ip, port);
            final ChannelFuture future = strap.connect(address).sync();
            channel = future.awaitUninterruptibly().channel();
            System.out.println("连接成功, channel =" + channel.remoteAddress());
        }catch(Exception e ){
            e.printStackTrace();
            group.shutdownGracefully() ;
        }finally{

        }
    }
    private static SslHandler sslHandlerClient = null ;
    public static SslHandler getSslHandler(){
        if(sslHandlerClient == null){
            SSLEngine sslEngine = ContextSSLFactory.getSslContext().createSSLEngine() ;
            sslEngine.setUseClientMode(true) ;
            sslHandlerClient = new SslHandler(sslEngine);
        }
        return sslHandlerClient ;
    }
    public static void main(String[] args) {
        new NettySocketClient().connect(".0.0.1", 16161) ;
    }
} 
 package com.what.netty01.demo03.client;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

import java.net.InetAddress;
import java.nio.ByteBuffer;

public class NettySocketSSLClientHandler extends SimpleChannelInboundHandler<ByteBuffer> {

    @Override
    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
        // Once session is secured, send a greeting and register the channel to the global channel
        // list so the channel received the messages from others.
        ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
                new GenericFutureListener<Future<Channel>>() {
                    @Override
                    public void operationComplete(Future<Channel> future) throws Exception {
                        if (future.isSuccess()) {
                            System.out.println("握手成功");
                            byte[] array = new byte[]{(byte)d, 04};
                            ByteBuffer bu = ByteBuffer.wrap(array);
                            ctx.channel().writeAndFlush(bu);
                        } else {
                            System.out.println("握手失败");
                        }
                        ctx.writeAndFlush("Welcome to " + InetAddress.getLocalHost().getHostName() +
                                " secure chat service!\n");
                        ctx.writeAndFlush("Your session is protected by " +
                                ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite() +
                                " cipher suite.\n");

                    }
                });
    }

    @Override
    protected void channelRead(ChannelHandlerContext ctx, ByteBuffer byteBuffer) throws Exception {
        Thread.sleep();
        System.out.println("客户端 receive msg ");
        byte[] array = new byte[]{, 01, 00, 00, 00, 06, 05, 03, (byte) 7d, 00, 00, 07};
        ByteBuffer bu = ByteBuffer.wrap(array);
        ctx.channel().writeAndFlush(bu);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx)
            throws Exception {
        System.out.println("增加:" + ctx.channel().remoteAddress());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        System.out.println("移除:" + ctx.channel().remoteAddress());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("Unexpected exception from downstream.");
        ctx.close();
    }


} 
 package com.what.netty01.demo03.client;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

import java.nio.ByteBuffer;

public class MyClientEncoder extends MessageToByteEncoder<ByteBuffer>{

    @Override
    protected void encode(ChannelHandlerContext ctx, ByteBuffer message,
                          ByteBuf out) throws Exception {
        if(message==null){
            return;
        }
        if(message .hasArray()){
            byte[] msg =message.array();
            if(msg == null || msg.length <=){
                return;
            }
            out.writeBytes(msg);
        }
    }


} 
 package com.what.netty01.demo03.client;


import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.nio.ByteBuffer;
import java.util.List;

public class MyClientDecoder extends ByteToMessageDecoder {


    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
                          List<Object> out) throws Exception {
        //UnpooledUnsafeDirectByteBuf(ridx:, widx: 1, cap: 1024)
        if (buffer != null) {
            ByteBuffer msg = null;
            try {
                if(buffer.readableBytes() > ){
                    msg = ByteBuffer.allocate(buffer.readableBytes()) ;
                    byte[] bb = new byte[buffer.readableBytes()] ;
                    buffer.readBytes(bb) ;
                    msg.put(bb);
                    msg.flip();
                }
            } catch (Exception e) {
                e.printStackTrace();
                msg = null ;
            }
            if (msg != null) {
                out.add(msg);
            }
        }
    }

}