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();
        }

客戶端代碼基本是類似的,這裏就不貼了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章