准备
- JDK17
- Netty v4.1.80.Final
🔗原文地址:xilio.cn/post/100
证书生成
采用jdk自带的工具生成。
1、生成服务器密钥库**(server.jks)**
bash
keytool -genkeypair -alias serverkey -keyalg RSA -keysize 2048 -storetype JKS -keystore server.jks -dname "CN=localhost" -keypass 123456 -storepass 123456 -validity 3650
2、导出服务器公共证书 (server.cer)
vbscript
keytool -exportcert -alias serverkey -keystore server.jks -storepass 123456 -file server.cer
3、创建客户端信任库 (client.truststore) 并导入服务器证书
bash
keytool -importcert -alias servercert -file server.cer -keystore client.truststore -storepass 123456 -noprompt

证书上下文创建工厂
java
package cn.xilio.netty4.tls.single;
package cn.xilio.netty4.tls.single;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.ClientAuth;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
/**
* TLS/SSL 上下文工厂,用于创建 Netty 单向认证TLS所需的 SslContext 实例。
* * 此配置用于客户端验证服务器身份,而服务器不验证客户端身份。
* 默认使用 JKS 密钥库/信任库格式,并强制启用 TLSv1.3 协议。
*/
public class SslContextFactory {
private static final String KEYSTORE_PASS = "123456";
private static final String PROTOCOL_TLS_1_3 = "TLSv1.3";
// 证书文件路径配置
private static final File SERVER_KEYSTORE = new File("cert/server.jks");
private static final File CLIENT_TRUSTSTORE = new File("cert/client.truststore");
// =========================================================================
// 核心私有工具方法:加载 JKS 文件并创建工厂
// =========================================================================
/**
* 加载 JKS 密钥库文件并创建 KeyManagerFactory。
* 用于服务器端,提供私钥和证书链给 SSL/TLS 握手。
* * @param keyStoreFile JKS 密钥库文件
*
* @return KeyManagerFactory 实例
*/
private static KeyManagerFactory createKeyManagerFactory(File keyStoreFile) throws Exception {
KeyStore ks = KeyStore.getInstance("JKS");
try (FileInputStream is = new FileInputStream(keyStoreFile)) {
ks.load(is, KEYSTORE_PASS.toCharArray());
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
// 密钥库密码和密钥密码在生成时保持一致
kmf.init(ks, KEYSTORE_PASS.toCharArray());
return kmf;
}
/**
* 加载 JKS 信任库文件并创建 TrustManagerFactory。
* 用于客户端,验证服务器端提供的证书是否可信。
* * @param trustStoreFile JKS 信任库文件
*
* @return TrustManagerFactory 实例
*/
private static TrustManagerFactory createTrustManagerFactory(File trustStoreFile) throws Exception {
KeyStore ts = KeyStore.getInstance("JKS");
try (FileInputStream is = new FileInputStream(trustStoreFile)) {
ts.load(is, KEYSTORE_PASS.toCharArray());
}
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts);
return tmf;
}
// =========================================================================
// 单向认证TLS上下文创建
// =========================================================================
/**
* 创建单向认证的服务器 SslContext。
* 服务器提供证书,不要求客户端提供证书 (ClientAuth.NONE)。
*/
public static SslContext createServerContext() throws Exception {
System.out.println("【单向认证服务器】加载证书并创建 SslContext...");
KeyManagerFactory serverKmf = createKeyManagerFactory(SERVER_KEYSTORE);
return SslContextBuilder
.forServer(serverKmf)
.sslProvider(SslProvider.JDK)
.protocols(PROTOCOL_TLS_1_3)
.clientAuth(ClientAuth.NONE)
.build();
}
/**
* 创建单向认证的客户端 SslContext。
* 客户端信任服务器证书。
*/
public static SslContext createClientContext() throws Exception {
System.out.println("【单向认证客户端】加载信任库并创建 SslContext...");
TrustManagerFactory clientTmf = createTrustManagerFactory(CLIENT_TRUSTSTORE);
return SslContextBuilder
.forClient()
.sslProvider(SslProvider.JDK)
.protocols(PROTOCOL_TLS_1_3)
.trustManager(clientTmf)
.build();
}
}
服务端
java
final SslContext sslContext = SslContextFactory.createServerContext();
//部分代码省略
protected void initChannel(SocketChannel ch) {
// 1. SslHandler 必须添加到 Pipeline 的第一个位置
ch.pipeline().addLast("ssl", sslContext.newHandler(ch.alloc()));
//其他处理器...
}
客户端
java
final SslContext sslContext = SslContextFactory.createClientContext();
//部分代码省略
protected void initChannel(SocketChannel ch) {
// 1. 创建 SSLEngine,指定主机和端口用于 SNI 和主机名验证
SSLEngine engine = sslContext.newEngine(ch.alloc(), host, port);
engine.setUseClientMode(true); // 必须设置为客户端模式
// 2. SslHandler 必须添加到 Pipeline 的第一个位置
ch.pipeline().addLast("ssl", new SslHandler(engine));
//其他处理器...
}