Netty服務新增https支援,jdk 證書
阿新 • • 發佈:2021-01-09
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();
}