一、证书准备
要使用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); } } } }
测试通过,搞了因为在网上没有找到完整的实例,所以因为一个小问题,找了两天都没有找到原因,希望看到的同学能够有所收获
相关推荐
在Netty中实现SSL双向认证,我们需要以下步骤: 1. **生成证书**:首先,为服务器和客户端分别生成数字证书。这通常包括创建私钥和公钥,然后使用证书签名请求(CSR)将公钥提交给受信任的证书颁发机构(CA)进行...
你还需要考虑如何实现用户身份验证,以保护聊天系统的安全性。 5. **并发处理**: Netty的事件驱动模型非常适合处理大量并发连接。它利用多线程和非阻塞I/O,避免了传统I/O模型中的线程上下文切换开销。 6. **日志...
描述中提到,这个服务是利用Netty框架来实现的。Netty是一个高性能、异步事件驱动的网络应用框架,适用于开发服务器和客户端。它提供了高效的TCP/IP套接字通信,并且支持WebSocket等协议,但在高并发情况下,...
"Java开发的一个真正意义上的跨服聊天系统源码.zip"提供了一个实现这一功能的实例,它不仅包含了源代码,还揭示了设计和实现跨服聊天系统的关键技术。下面,我们将深入探讨这个项目中涉及的主要知识点。 首先,我们...
8. **网络安全**:包括加密技术如SSL/TLS,以及身份验证、授权等,Java的JSSE(Java Secure Socket Extension)提供相关支持。 9. **网络编程设计模式**:如工厂模式用于创建Socket或ServerSocket,单例模式用于...
4. **实时推送**:为了实现实时性,可以采用WebSocket协议,它提供双向通信,使得服务器能主动向客户端推送消息。Java的WebSocket API在Java EE 7及以上版本中已经内置。 5. **安全性**:SSL/TLS协议用于加密通信,...
在生产环境中,务必考虑WebSocket的安全性,例如使用SSL/TLS加密连接,配置Spring Security来控制访问权限,以及使用JWT令牌进行身份验证。 **七、性能优化** 为了提高WebSocket服务的性能,可以考虑使用NIO或Netty...
在安卓应用开发中,Socket通信是一种常见的网络编程方式,它允许设备之间进行双向通信。本DEMO主要展示了如何在Android平台上构建一个简单的Socket服务端,以便实现与其他客户端(如另一台Android设备或任何支持...
- 基于Netty实现高性能的网络通信。 - 采用SPI(Service Provider Interface)机制扩展功能。 - 通过Zookeeper实现服务发现和管理。 #### 数据结构和算法 - **单向链表的逆序排列**: - 可以通过迭代法或递归...
7. **网络安全**:网络通信涉及数据安全问题,如加密传输(SSL/TLS)、身份验证、防止DDoS攻击等。Java提供JSSE(Java Secure Socket Extension)框架来支持安全的网络通信。 8. **异常处理**:在网络通信中,错误...
Pusher是一个云服务,它提供了一种简单的方式来实现WebSocket接口,使得应用程序能够实现实时的双向通信。 首先,`nettyPusherAndroid`是Pusher客户端的核心组件,它基于Netty框架构建。Netty是一个高性能、异步...