Netty结合TLS实现双向认证
netty代码如下
public static EventLoopGroup groupStream = new NioEventLoopGroup();
public void initClient() {
try {
Bootstrap b = new Bootstrap();
b.group(groupStream)
.channel(NioSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws SSLException {
if (v.isStreamsInTlsOn()) {//是否开启tls认证
SSLEngine engine = null;
if (Node.isOpenKnockerHandler){
//双向认证 tlsTowAuthorKeyPath:证书路径,双向证书,客户端证书路径 、 tlsTowAuthorCaPath:证书路径,双向证书,根证书路径
engine = SslContextFactory.getClientContext(Node.tlsTowAuthorKeyPath,Node.tlsTowAuthorCaPath).createSSLEngine();
}else {
//单向认证
String cChatPath = System.getProperty("user.dir") + "/Data/keys/client.jks";
engine = SslContextFactory.getClientContext(cChatPath,cChatPath).createSSLEngine();
}
engine.setUseClientMode(true);
sc.pipeline().addLast("ssl", new SslHandler(engine));
}
sc.pipeline().addLast(new IdleStateHandler(45, 0, 0)); //45s无接收时,发送心跳
ByteBuf buf = Unpooled.copiedBuffer(KConst.SEPERATOR.getBytes());
sc.pipeline().addLast(new DelimiterBasedFrameDecoder(KConst.MAX_FRAME_LENGTH, buf));
sc.pipeline().addLast(new StringDecoder(UTF_8));
sc.pipeline().addLast(new HandlerStream(Client.this, handleMsg, heartMsg));
}
});
} catch (Exception e) {
log.error("[Thread_Stream] exception!", e);
throw new RuntimeException(e);
}
}
import com.Node.Node.Node;
import lombok.extern.slf4j.Slf4j;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
/**
* @description: ssl工厂类
* @date: 2022/1/18 16:15
*/
@Slf4j
public class SslContextFactory {
private static SSLContext CLIENT_CONTEXT;
private static final String PROTOCOL = "TLS";
/**
* @description: 获取客户端SSLContext
* @date: 2022/1/18 16:16
*/
public static SSLContext getClientContext(String pkPath,String caPath){
String pkPassword = "";
String caPassword = "";
if (Node.isOpenKnockerHandler){
pkPassword = "123456";//授权登录密码
caPassword = Node.caJksPwd;//证书密码
}else {
pkPassword = "ponshine";
caPassword = "ponshine";
}
if(CLIENT_CONTEXT!=null) {
return CLIENT_CONTEXT;
}
InputStream in = null;
InputStream tIN = null;
try{
KeyManagerFactory kmf = null;
if (pkPath != null) {
KeyStore ks = KeyStore.getInstance("JKS");
in = new FileInputStream(pkPath);
ks.load(in, pkPassword.toCharArray());
kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, pkPassword.toCharArray());
}
TrustManagerFactory tf = null;
if (caPath != null) {
KeyStore tks = KeyStore.getInstance("JKS");
tIN = new FileInputStream(caPath);
tks.load(tIN, caPassword.toCharArray());
tf = TrustManagerFactory.getInstance("SunX509");
tf.init(tks);
}
CLIENT_CONTEXT = SSLContext.getInstance(PROTOCOL);
//初始化此上下文
//参数一:认证的密钥 参数二:对等信任认证 参数三:伪随机数生成器 。 由于单向认证,服务端不用验证客户端,所以第二个参数为null
CLIENT_CONTEXT.init(kmf.getKeyManagers(),tf.getTrustManagers(), null);
}catch(Exception e){
log.warn("[SslContextFactory-getClientContext] exception",e);
throw new Error("Failed to initialize the client-side SSLContext");
}finally{
if(in !=null){
try {
in.close();
} catch (IOException e) {
log.error("",e);
}
}
if (tIN != null){
try {
tIN.close();
} catch (IOException e) {
log.error("",e);
}
}
}
return CLIENT_CONTEXT;
}
}