1. 程式人生 > 其它 >Netty服務新增https支援,jdk 證書

Netty服務新增https支援,jdk 證書

技術標籤:javassljavahttps

1、使用JDK自帶的keytool生成一個keystore,這個keystore是服務端使用的,直接在bin目錄下執行

keytool -genkey -alias netty -keypass 123456 -keyalg RSA -keysize 1024 -validity 365 -keystore D:/test/netty.keystore -storepass 123456

2、新建一個安全工具類,用於返回一個SSLEngine

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import java.security.KeyStore;
import java.security.Security;
/**
 * @description: SSL伺服器端認證
 */
public class HttpSslContextFactory {
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpSslContextFactory.class);
//    private static final String PROTOCOL = "SSLv2";
    private static final String PROTOCOL = "SSLv3";//客戶端可以指明為SSLv3或者TLSv1.2
    /**針對於伺服器端配置*/
    private static SSLContext sslContext = null;
    static {
        String algorithm = Security
                .getProperty("ssl.KeyManagerFactory.algorithm");
        if (algorithm == null) {
            algorithm = "SunX509";
        }
        SSLContext serverContext = null;
        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(HttpsKeyStore.getKeyStoreStream(), HttpsKeyStore.getKeyStorePassword());
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
            kmf.init(ks, HttpsKeyStore.getCertificatePassword());
            serverContext = SSLContext.getInstance(PROTOCOL);
            serverContext.init(kmf.getKeyManagers(), null, null);
        } catch (Exception e) {
            LOGGER.error("初始化server SSL失敗", e);
            throw new Error("Failed to initialize the server SSLContext", e);
        }
        sslContext = serverContext;
    }
    public static SSLEngine createSSLEngine() {
        SSLEngine sslEngine = sslContext.createSSLEngine();
        sslEngine.setUseClientMode(false);
        sslEngine.setNeedClientAuth(false);
        return sslEngine ;
    }
}

上面的有一個HttpsKeyStore類如下,分別如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class HttpsKeyStore {
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpsKeyStore.class);

    /**
     * 讀取金鑰
     * @version V1.0.0
     * @return InputStream
     */
    public static InputStream getKeyStoreStream() {
        InputStream inStream = null;
        try {
            inStream = new FileInputStream(VarConstant.keystorePath);
        } catch (FileNotFoundException e) {
            LOGGER.error("讀取金鑰檔案失敗", e);
        }
        return inStream;
    }

    /**
     * 獲取安全證書密碼 (用於建立KeyManagerFactory)
     * @version V1.0.0
     * @return char[]
     */
    public static char[] getCertificatePassword() {
        return VarConstant.certificatePassword.toCharArray();
    }

    /**
     * 獲取金鑰密碼(證書別名密碼) (用於建立KeyStore)
     * @version V1.0.0
     * @return char[]
     */
    public static char[] getKeyStorePassword() {
        return VarConstant.keystorePassword.toCharArray();
    }
}

其中常量類VarConstant的幾個屬性如下,其他的不再貼上:

//SSL
public static Boolean sslEnabled = true;
public static String keystorePath = "D:/test/netty.keystore";
public static String certificatePassword = "123456";
public static String keystorePassword = "123456";

3、上面的步驟做好之後,我們只需給Netty的pipeline的新增第一個的handler,如下:

//是否開啟SSL
if (VarConstant.sslEnabled) {
   //務必放在第一位
   ch.pipeline().addLast("sslHandler", new SslHandler(HttpSslContextFactory.createSSLEngine()));
}

4、基本的步驟就是上面這樣子,如果要新增雙向認證,還需另外生成客戶端證書,通過以上這種方式,客戶端只需跳過證書認證即可,如使用apachehttpclients,跳過認證程式碼如下:

public static CloseableHttpClient getHttpsClient(){
    SSLContext sslContext = null;
    try {
        //SSLv3或者TLSv1.2都可以
        sslContext = new SSLContextBuilder().useProtocol("TLSv1.2").loadTrustMaterial(null, new TrustStrategy() {
            // 預設信任所有證書
            public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                return true;
            }
        }).build();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } catch (KeyStoreException e) {
        e.printStackTrace();
    }
    HostnameVerifier hostnameVerifier = SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,hostnameVerifier);

    SocketConfig socketConfig = SocketConfig.custom()
            .setSoKeepAlive(true)
            .setTcpNoDelay(true)
            .setSoReuseAddress(true)
            .build();
    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(30000)
            .setSocketTimeout(30000).build();

    HttpClientBuilder clientBuilder = HttpClientBuilder.create();
    return clientBuilder
            .setSSLSocketFactory(sslsf)
            .setDefaultRequestConfig(requestConfig)
            .setDefaultSocketConfig(socketConfig).build();
}