Netty ssl双向认证

生成证书及代码中有关密码的操作,请按照你们自己的需要修改成自己的

使用keytool生成证书

这个命令一般在JDK\jre\lib\security\目录下操作

keytool常用命令

参数 释义
-alias 证书的别名
-keystore 证书库的名称
-storepass 证书库的密码
-keypass 证书的密码
-list 显示密钥库中的证书信息
-v 显示密钥库中的证书详细信息
-export 显示密钥库中的证书信息
-file 指定导出证书的文件名和路径
-delete 删除密钥库中某条目
-import 将已签名数字证书导入密钥库
-keypasswd 修改密钥库中指定条目口令
-dname 指定证书拥有者信息
-keyalg 指定密钥的算法
-validity 指定创建的证书有效期多少天
-keysize 指定密钥长度

具体生成证书操作

  1. 创建服务端秘钥
keytool -genkey -alias nettyServer -keysize 1024 -validity 3650 -keyalg RSA -dname "CN=localhost" -keypass 证书密码 -storepass 服务端的证书仓库密码 -keystore serverCerts.jks
  1. 导出服务端秘钥
keytool -export -alias nettyServer -keystore serverCerts.jks -storepass 服务端的证书仓库密码 -file serverCert.cer
  1. 创建客户端秘钥
keytool -genkey -alias nettyClient -keysize 1024 -validity 3650 -keyalg RSA -dname "CN=PF,OU=YJC,O=YJC,L=BJ,S=BJ,C=ZN" -keypass 证书密码 -storepass 客户端的证书仓库密码 -keystore clientCerts.jks
  1. 导出客户端秘钥
keytool -export -alias nettyClient -keystore clientCerts.jks -file nettyclientCert.cer -storepass 客户端的证书仓库密码
  1. 将客户端的证书导入到服务端的信任证书仓库中
keytool -import -trustcacerts -alias smcc -file nettyClientCert.cer -storepass 服务端的证书仓库密码 -keystore serverCerts.jks
  1. 将服务端的证书导入到客户端的信任证书仓库中
keytool -import -trustcacerts -alias smccClient -file serverCert.cer -storepass 客户端的证书仓库密码 -keystore clientCerts.jks

服务端创建ContextSSLFactory

package com.yjc.rpc.ssl;


import org.springframework.core.io.ClassPathResource;

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

import javax.net.ssl.*;


public class ContextSSLFactory {

    private static final SSLContext SSL_CONTEXT_S ;

    static{
        SSLContext sslContextServer = null ;
        try {
            sslContextServer = SSLContext.getInstance("SSLv3") ;

        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        }
        try{
            if(getKeyManagersServer() != null && getTrustManagersServer() != null ){
                sslContextServer.init(getKeyManagersServer(), getTrustManagersServer(), null);
            }


        }catch(Exception e){
            e.printStackTrace() ;
        }
        sslContextServer.createSSLEngine().getSupportedCipherSuites() ;
        SSL_CONTEXT_S = sslContextServer ;
    }
    public ContextSSLFactory(){

    }
    public static SSLContext getSslContext(){
        return SSL_CONTEXT_S ;
    }
    /**
     * 获取服务端信任的证书
     * @param:             @return
     * @return:         TrustManager[]
     * @throws
     */
    private static TrustManager[] getTrustManagersServer(){
        FileInputStream is = null ;
        TrustManager[] trustManagersw=null;
        TrustManagerFactory trustManagerFactory=null;
        KeyStore ks=null;
        try {
            trustManagerFactory = TrustManagerFactory.getInstance("SunX509") ;
            is =is =new FileInputStream( (new ClassPathResource("certs/serverCerts.jks")).getFile() );
            String keyStorePass = "服务端的证书仓库密码" ;
            ks=KeyStore.getInstance("JKS");
            ks.load(is,keyStorePass.toCharArray());
            trustManagerFactory.init(ks);
            trustManagersw=trustManagerFactory.getTrustManagers();
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally{
            if(is != null ){
                try {
                    is.close() ;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return trustManagersw;
    }


    /**
     * 获取keymanager列表
     * @param:             @return
     * @return:         KeyManager[]
     * @throws
     */
    private static KeyManager[] getKeyManagersServer(){
        FileInputStream is = null ;
        KeyStore ks = null ;
        KeyManagerFactory keyFac = null ;

        KeyManager[] kms = null ;
        try {
            // 获得KeyManagerFactory对象. 初始化位默认算法
            keyFac = KeyManagerFactory.getInstance("SunX509") ;
//            String keyStorePath = PropertyUtil.getProperty("httpsKeyStorePath");
            is =new FileInputStream( (new ClassPathResource("certs/serverCerts.jks")).getFile() );
            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 ;
    }
}

在pipeline中添加netty自带的sslHandler

这里需要注意的是 SslHandler需要添加到pipeline的最前面,否则即使加上了,不合法的客户端一样可以正常连接

   try {
            //设置事件处理
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) {
                    ChannelPipeline pipeline = ch.pipeline();
                    // 添加心跳支持
                    pipeline.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
                    // 基于定长的方式解决粘包/拆包问题
//                    pipeline.addLast(new LengthFieldBasedFrameDecoder(nettyConfig.getMaxFrameLength()
//                            , 0, 2, 0, 2));
//                    pipeline.addLast(new LengthFieldPrepender(2));
                    // 序列化
//                    pipeline.addLast(new MessagePackDecoder());
//                    pipeline.addLast(new MessagePackEncoder());
                    pipeline.addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
                    pipeline.addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
                    pipeline.addLast(channelHandlerAdapter);
                    SSLEngine engine = ContextSSLFactory.getSslContext().createSSLEngine();
                    engine.setUseClientMode(false); //设置为服务端模式
                    engine.setNeedClientAuth(true); //需要验证客户端
                    pipeline.addFirst("ssl", new SslHandler(engine));   //这个handler需要加到最前面
                }
            });
            LOGGER.info("netty服务器在[{}]端口启动监听", port);
            ChannelFuture f = serverBootstrap.bind(port).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            LOGGER.info("[出现异常] 释放资源");
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }

客户端代码基本是类似的,这里就不贴了。

猜你喜欢

转载自www.cnblogs.com/falcon-fei/p/11453727.html