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、基本的步骤就是上面这样子,如果要添加双向认证,还需另外生成客户端证书,通过以上这种方式,客户端只需跳过证书认证即可,如使用apache httpclients,跳过认证代码如下:
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();
}