Netty实现SSL双向验证完整实例

 一、证书准备

     要使用ssl双向验证,就必须先要生成服务端和客户端的证书,并相互添加信任,具体流程如下(本人调试这个用例的时候,花了很多时间来验证证书是否正确,以及握手失败的原因,这里证书生成过程只要按流程走,本人能保证绝对没有问题)

现在打开cmd,在哪个目录下打开,证书就会放在哪个目录下:

第一步:   生成Netty服务端私钥和证书仓库命令

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

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

第二步:生成Netty服务端自签名证书

             keytool -export -alias securechat -keystore sChat.jks -storepass sNetty -file sChat.cer

 第三步:生成客户端的密钥对和证书仓库,用于将服务端的证书保存到客户端的授信证书仓库中

    keytool -genkey -alias smcc -keysize 2048 -validity 365  -keyalg RSA -dname "CN=localhost" -keypass sNetty  -storepass sNetty -keystore cChat.jks

第四步:将Netty服务端证书导入到客户端的证书仓库中

keytool -import -trustcacerts -alias securechat -file sChat.cer -storepass sNetty -keystore cChat.jks

如果你只做单向认证,则到此就可以结束了,如果是双响认证,则还需继续往下走

第五步:生成客户端自签名证书

  keytool -export -alias smcc -keystore cChat.jks -storepass sNetty -file cChat.cer

最后一步:将客户端的自签名证书导入到服务端的信任证书仓库中:

   keytool -import -trustcacerts -alias smcc -file cChat.cer -storepass sNetty -keystore sChat.jks

            

到这里,证书就生成完毕了,我们就可以得到两个jks文件,一个是服务端的sChat.jks  ,一个是客户端的cChat.jks   ,这两个文件后面初始化sslCOntext的时候会用到

如果还想了解更多可以查看

http://dwj147258.iteye.com/blog/2339934

二、netty服务端

       下面就直接贴代码了,首先是实例化SSLContext的类:

package main.java.com.nionetty;

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;

import org.springframework.core.io.ClassPathResource;
/**
 * 初始化sslcontext类
 *
 */
public class ContextSSLFactory {
	
	private static final SSLContext SSL_CONTEXT_S ;
	
	private static final SSLContext SSL_CONTEXT_C ;
	
	static{
		SSLContext sslContext = null ;
		SSLContext sslContext2 = null ;
		try {
			sslContext = SSLContext.getInstance("SSLv3") ;
			sslContext2 = SSLContext.getInstance("SSLv3") ;
		} catch (NoSuchAlgorithmException e1) {
			e1.printStackTrace();
		}
		try{
			if(getKeyManagersServer() != null && getTrustManagersServer() != null ){
				sslContext.init(getKeyManagersServer(), getTrustManagersServer(), null);
			}
			if(getKeyManagersClient() != null && getTrustManagersClient() != null){
				sslContext2.init(getKeyManagersClient(), getTrustManagersClient(), null);
			}
			
		}catch(Exception e){
			e.printStackTrace() ;
		}
		sslContext.createSSLEngine().getSupportedCipherSuites() ;
		sslContext2.createSSLEngine().getSupportedCipherSuites() ;
		SSL_CONTEXT_S = sslContext ; 
		SSL_CONTEXT_C = sslContext2 ;
	}
	public ContextSSLFactory(){
		
	}
	public static SSLContext getSslContext(){
		return SSL_CONTEXT_S ;
	}
	public static SSLContext getSslContext2(){
		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("SunX509") ;
			is =new FileInputStream( (new ClassPathResource("main/java/conf/sChat.jks")).getFile() );
			ks = KeyStore.getInstance("JKS") ;
			String keyStorePass = "sNetty" ;
			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("SunX509") ;
			is =new FileInputStream( (new ClassPathResource("main/java/conf/cChat.jks")).getFile() );
			ks = KeyStore.getInstance("JKS") ;
			String keyStorePass = "sNetty" ;
			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("SunX509") ;
			is =new FileInputStream( (new ClassPathResource("main/java/conf/sChat.jks")).getFile() );
			ks = KeyStore.getInstance("JKS") ;
			String keyStorePass = "sNetty" ;
			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("SunX509") ;
			is =new FileInputStream( (new ClassPathResource("main/java/conf/cChat.jks")).getFile() );
			ks = KeyStore.getInstance("JKS") ;
			String keyStorePass = "sNetty" ;
			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 main.java.com.nionetty;

import javax.net.ssl.SSLEngine;
import javax.print.attribute.standard.MediaSize.Engineering;

import main.java.com.nettyTest.SecureChatServerHandler;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
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;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;

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, 128)
			.option(ChannelOption.SO_KEEPALIVE, true)
			.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 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 MyDecoder()) ;
					pie.addLast("encoder" , new MyEncoder()) ;
					pie.addLast("handler" , new NettySocketSSLHandler()) ;
					SSLEngine engine = ContextSSLFactory.getSslContext().createSSLEngine();
			        engine.setUseClientMode(false);
			        engine.setNeedClientAuth(true);
			        pie.addFirst("ssl", new SslHandler(engine));
				}
				
			});
			serverStrap.bind(161616).sync() ;
			System.out.println("服务已开启");
		}catch(Exception e){
			e.printStackTrace() ;
			bossGroup.shutdownGracefully() ;
			workerGroup.shutdownGracefully() ;
		}
		
	}
		private SslHandler getSslHandler(){
			if(sslHandler == null ){
				SSLEngine sslEngine = ContextSSLFactory.getSslContext().createSSLEngine() ;
				sslEngine.setUseClientMode(false) ;
				//false为单向认证,true为双向认证
				sslEngine.setNeedClientAuth(true) ;
				sslHandler = new SslHandler(sslEngine);
			}
			return sslHandler ;
		}
		public static void main(String[] args) {
			new NettySocketServer().start() ;
		}
		
}

 编码器:

package main.java.com.nionetty;

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

import java.nio.ByteBuffer;

public class MyEncoder 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 <= 0){
                return;
        	}
        	out.writeBytes(msg) ;
        }
	}
    

   
}

 解码器:

/*
 * Copyright (C) TD Tech<br>
 * All Rights Reserved.<br>
 * 
 */
package main.java.com.nionetty;

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

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

/**
 * Create Date: 2014-11-4 下午02:42:21<br>
 * Create Author: lWX232692<br>
 * Description :
 */
public class MyDecoder extends ByteToMessageDecoder {


	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
			List<Object> out) throws Exception {
		//UnpooledUnsafeDirectByteBuf(ridx: 0, widx: 1, cap: 1024)
		if (buffer != null) {
			ByteBuffer msg = null;
			try {
				if(buffer.readableBytes() > 0 ){
					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 main.java.com.nionetty;

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;
import java.util.Arrays;

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)7d,  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
    public void handlerAdded(ChannelHandlerContext ctx)
        throws Exception {
    	 System.out.println("服务端增加");
    }
    
    @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();
    }
    @Override
    public void messageReceived(ChannelHandlerContext ctx, ByteBuffer msg) throws Exception {
    	System.out.println("服务端receive msg ");
    	byte[] array = new byte[]{00, 01, 00, 00, 00, 06, 05, 03, (byte)7d, 00, 00, 07} ;
    	ByteBuffer bu = ByteBuffer.wrap(array) ;
    	ctx.channel().writeAndFlush(bu) ;
    }
	
}

三、客户端

     客户端实现类

package main.java.com.nionetty.client;

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

import javax.net.ssl.SSLEngine;

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;
import main.java.com.nionetty.ContextSSLFactory;
import main.java.com.nionetty.MyDecoder;
import main.java.com.nionetty.MyEncoder;

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.getSslContext2().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.getSslContext2().createSSLEngine() ;
			sslEngine.setUseClientMode(true) ;
			sslHandlerClient = new SslHandler(sslEngine);
		}
		return sslHandlerClient ;
	}
	public static void main(String[] args) {
		new NettySocketClient().connect("192.168.10.256", 161616) ;
	}
}

 编码器:

package main.java.com.nionetty.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 <= 0){
                return;
        	}
        	out.writeBytes(msg);
        }
	}
    

   
}

 解码器:

/*
 * Copyright (C) TD Tech<br>
 * All Rights Reserved.<br>
 * 
 */
package main.java.com.nionetty.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;

/**
 * Create Date: 2014-11-4 下午02:42:21<br>
 * Create Author: lWX232692<br>
 * Description :
 */
public class MyClientDecoder extends ByteToMessageDecoder {


	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
			List<Object> out) throws Exception {
		//UnpooledUnsafeDirectByteBuf(ridx: 0, widx: 1, cap: 1024)
		if (buffer != null) {
			ByteBuffer msg = null;
			try {
				if(buffer.readableBytes() > 0 ){
					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);
			}
		}
	}


}

 业务handler:

/*
 * Copyright (C) TD Tech<br>
 * All Rights Reserved.<br>
 * 
 */
package main.java.com.nionetty.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;

/**
 * Create Date: 2014-11-4 下午02:42:21<br>
 * Create Author: lWX232692<br>
 * Description :
 */
public class MyClientDecoder extends ByteToMessageDecoder {


	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
			List<Object> out) throws Exception {
		//UnpooledUnsafeDirectByteBuf(ridx: 0, widx: 1, cap: 1024)
		if (buffer != null) {
			ByteBuffer msg = null;
			try {
				if(buffer.readableBytes() > 0 ){
					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);
			}
		}
	}


}

 测试通过,搞了因为在网上没有找到完整的实例,所以因为一个小问题,找了两天都没有找到原因,希望看到的同学能够有所收获

猜你喜欢

转载自dwj147258.iteye.com/blog/2360535