Netty SSL安全配置



title: Netty SSL安全配置
date: 2017-08-26 14:51:43
categories: 網絡安全
tags: [netty,ssl]

Netty SSL安全配置



摘要

在研發平台的過程中,涉及到平台網關和前置agent的通信加密,雖然目前軟件在內網中,但是由於平台和agent的特殊性,一旦被控制,部署的軟件就會受到很大威脅,平台網關采用Netty開發,下面主要介紹一下netty的ssl配置和安全軟件掃出的Diffie-Hellman弱密碼問題解決方法

主要名詞解釋

| 英文名稱或縮寫 | 名詞解釋 |
|-
| Netty | 高性能服務器端編程框架 |
| OpenSSL | 安全套接字層密碼庫 |
| KeyTool | 密鑰和證書管理工具 |
| Diffie-Hellman | 密鑰交換算法 |

SSL常用認證方式介紹

  • 單向認證
  • 雙向認證
  • CA認證

SSL單向認證

單向認證只需客戶端驗證服務端,即客戶端只需要認證服務端的合法性,服務端不需要。這種認證方式適用Web應用,因為web應用的用戶數目廣泛,且無需在通訊層對用戶身份進行驗證,一般都在應用邏輯層來保證用戶的合法登入。但如果是企業應用對接,情況就不一樣,可能會要求對客戶端(相對而言)做身份驗證。這時就需要做SSL雙向認證。

SSL雙向認證

雙向認證顧名思義,服務端也需要認證客戶端的合法性,這就意味着客戶端的自簽名證書需要導入服務端的數字證書倉庫。
采用這種方式會不太便利,一但客戶端或者服務端修改了秘鑰和證書,就需要重新進行證書交換,對於調試和維護工作量非常大,並且由於agent數目不確定,動態增加agent的時候需要平台和agent雙發互相加入相應各自的證書。

CA認證

CA認證的好處是只要服務端和客戶端只需要將CA證書導入各自的keystore,客戶端和服務端只需判斷這些證書是CA簽名過的即可,這也是蜂鳥平台內部采用的認證方式

生成證書

openSSL的安裝不是本經驗案例的重點,這里不介紹

根證書生成

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout root.key –out root.crt –subj /C=CN/ST=ZheJiang/L=HangZhou/O=MyCompany/OU=GA/CN=GA –config openssl.cnf

服務器端證書生成

服務器端秘鑰對生成

keytool -genkey -alias server -keypass **** -validity 1825 -keyalg RSA -keystore gateway.keystore -keysize 2048 -storepass **** -dname "CN=GA, OU=GA, O=MyCompany, L=HangZhou, ST=ZheJiang, C=CN"

keypass: 指定生成秘鑰的密碼
keystore:指定存儲文件的密碼,再次打開需要此密碼

生成證書簽名請求

keytool -certreq -alias server -keystore gateway.keystore -validity 1825 -file gateway.csr -storepass ****

用根證書私鑰進行簽名

openssl x509 -req -in gateway.csr -CA root.crt -CAkey root.key -CAcreateserial -out gateway.pem -days 1825 -extensions SAN -extfile san1.cnf

導入根證書

keytool -keystore gateway.keystore -importcert -alias CA -file root.crt -storepass **** -noprompt

導入服務端證書

keytool -keystore gateway.keystore -importcert -alias server -file gateway.pem -storepass **** 

客戶端證書生成方法與服務端基本相同,此處不再贅述,需要注意一點的是簽名根證書必須是同一個

Netty SSL配置

獲取SSLContext

public class SslContextFactory {

    private static final String	PROTOCOL = "TLS";

    private static volatile SSLContext SERVER_CONTEXT = null;

    private static final String DEFAULT_PROPERTIES = "application.properties";

    private static final String KEYSTORE_TYPE = "server.ssl.key-store-type";

    private static final String KEYSTORE_PASSWORD = "gateway.ssl.key-store-password";

    private static final String GATEWAY_KEYSTORE = "gateway.ssl.key-store";

    private SslContextFactory() {
    }

    private static void init(){
        Properties properties = null;
        InputStream gatewayKeyStore = null;
        InputStream gatewayTrustStore = null;
        try {
            properties = PropertiesTool.getInstance().getProperties(DEFAULT_PROPERTIES, false);
            //初始化keyManagerFactory
            KeyStore ks = KeyStore.getInstance(properties.getProperty(KEYSTORE_TYPE));
            gatewayKeyStore = new FileInputStream(properties.getProperty(GATEWAY_KEYSTORE));
            ks.load(gatewayKeyStore, properties.getProperty(KEYSTORE_PASSWORD).toCharArray());
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, properties.getProperty(KEYSTORE_PASSWORD).toCharArray());
			 //初始化TrustManagerFacotry
            KeyStore ts = KeyStore.getInstance(properties.getProperty(KEYSTORE_TYPE));
            gatewayTrustStore = new FileInputStream(properties.getProperty(GATEWAY_KEYSTORE));
            ts.load(gatewayTrustStore, properties.getProperty(KEYSTORE_PASSWORD).toCharArray());
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(ts);
			//生成SSLContext
            SERVER_CONTEXT = SSLContext.getInstance(PROTOCOL);
            SERVER_CONTEXT.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        } catch (IOException e) {
            throw new GatewayException(e.getMessage(), e);
        } catch (Exception  e) {
            throw new GatewayException(e.getMessage(), e);
        } finally {
            if (null != gatewayKeyStore) {
                try {
                    gatewayKeyStore.close();
                } catch (IOException e) {
                }
            }
            if (null != gatewayTrustStore) {
                try {
                    gatewayTrustStore.close();
                } catch (IOException e) {
                }
            }
        }
    }

    public static SSLContext getServerContext() {
        if(SERVER_CONTEXT == null){
            synchronized (SslContextFactory.class) {
                if (SERVER_CONTEXT == null) {
                    init();
                }
            }
        }
        return SERVER_CONTEXT;
    }
}

加入NettyHandler

Netty 提供了一個SslHandler,主要用於加密和解密
在大多數情況下,SslHandler 將成為 ChannelPipeline 中的第一個 ChannelHandler 。這將確保所有其他 ChannelHandler 應用他們的邏輯到數據后加密后才發生,從而確保他們的變化是安全的。
這里寫圖片描述

圖片來自網絡

		SSLContext sslCtx = SslContextFactory.getServerContext();
        SSLEngine sslEngine = sslCtx.createSSLEngine();
        //設置加密套件
        sslEngine.setEnabledCipherSuites(Constants.CIPHER_ARRAY);
        sslEngine.setUseClientMode(false);
        sslEngine.setNeedClientAuth(true);
        pipeline.addLast("SslEstablish",new SslHandler(sslEngine));

Diffie-Hellman 密碼過弱問題

研究人員Alex Halderman和Nadia Heninger提出NSA已經能夠通過攻擊1024位素數的Diffie-Hellman密鑰交換算法解密大量HTTPS、SSH和VPN連接。
提供安全的加密算法

 public static final String[] CIPHER_ARRAY = {"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
 "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
 "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"};

參考資料

Netty權威指南
https://weakdh.org/sysadmin.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM