千家信息网

Netty与Qt如何实现wss双向认证

发表于:2025-01-24 作者:千家信息网编辑
千家信息网最后更新 2025年01月24日,本篇内容主要讲解"Netty与Qt如何实现wss双向认证",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Netty与Qt如何实现wss双向认证"吧!Nett
千家信息网最后更新 2025年01月24日Netty与Qt如何实现wss双向认证

本篇内容主要讲解"Netty与Qt如何实现wss双向认证",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Netty与Qt如何实现wss双向认证"吧!

Netty 4.1.42.Final + Qt 5.9 + SpringBoot2.1.8

一、证书准备

要使用ssl双向验证,就必须先要生成服务端和客户端的证书,并相互添加信任,具体流程如下(本人调试这个用例的时候,花了很多时间来验证证书是否正确,以及握手失败的原因,这里证书生成过程只要按流程走,本人能保证绝对没有问题) 现在打开cmd,在哪个目录下打开,证书就会放在哪个目录下:

第一步:生成Netty服务端私钥和证书仓库命令

keytool -genkey -alias securechat -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass sNetty -storepass sNetty -keystore sChat.jks -deststoretype pkcs12

  • -keysize 2048 密钥长度2048位(这个长度的密钥目前可认为无法被暴力破解)

  • -validity 365 证书有效期365天

  • -keyalg RSA 使用RSA非对称加密算法

  • -dname "CN=localhost" 设置Common Name为localhost

  • -keypass sNetty密钥的访问密码为sNetty

  • -storepass sNetty密钥库的访问密码为sNetty(其实这两个密码也可以设置一样,通常都设置一样,方便记)

  • -keystore sChat.jks 指定生成的密钥库文件为sChata.jks

  • -deststoretype pkcs12 必须要pkcs12

第二步:生成Netty服务端自签名证书

keytool -export -alias securechat -keystore sChat.jks -storepass sNetty -file sChat.cer

第三步:生成客户端的密钥对和证书仓库,用于将服务端的证书保存到客户端的授信证书仓库中

keytool -genkey -alias smcc -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass sNetty -storepass sNetty -keystore cChat.jks -deststoretype pkcs12

第四步:将Netty服务端证书导入到客户端的证书仓库中

keytool -import -trustcacerts -alias securechat -file sChat.cer -storepass sNetty -keystore cChat.jks

第五步:生成客户端自签名证书

keytool -export -alias smcc -keystore cChat.jks -storepass sNetty -file cChat.cer

最后一步:将客户端的自签名证书导入到服务端的信任证书仓库中:

keytool -import -trustcacerts -alias smcc -file cChat.cer -storepass sNetty -keystore sChat.jks

到这里,证书就生成完毕了,我们就可以得到两个jks文件,一个是服务端的sChat.jks ,一个是客户端的cChat.jks ,这两个文件后面初始化sslCOntext的时候会用到

二、Netty服务端

ContextSSLFactory.java

package com.cccc.ocpptest;import java.io.FileInputStream;import java.io.IOException;import java.security.KeyStore;import java.security.NoSuchAlgorithmException;import javax.net.ssl.KeyManager;import javax.net.ssl.KeyManagerFactory;import javax.net.ssl.SSLContext;import javax.net.ssl.TrustManager;import javax.net.ssl.TrustManagerFactory;/** * ContextSSLFactory * * @author MFY * @date 2019/10/1 */public class ContextSSLFactory {    private static final SSLContext SSL_CONTEXT_S ;    private static final SSLContext SSL_CONTEXT_C ;    static{        SSLContext sslContext = null ;        SSLContext sslContext2 = null ;        try {            sslContext = SSLContext.getInstance("TLSv1") ;            sslContext2 = SSLContext.getInstance("TLSv1") ;        } catch (NoSuchAlgorithmException e1) {            e1.printStackTrace();        }        try{            if(getKeyManagersServer() != null && getTrustManagersServer() != null ){                sslContext.init(getKeyManagersServer(), getTrustManagersServer(), null);            }            if(getKeyManagersClient() != null && getTrustManagersClient() != null){                sslContext2.init(getKeyManagersClient(), getTrustManagersClient(), null);            }        }catch(Exception e){            e.printStackTrace() ;        }        sslContext.createSSLEngine().getSupportedCipherSuites() ;        sslContext2.createSSLEngine().getSupportedCipherSuites() ;        SSL_CONTEXT_S = sslContext ;        SSL_CONTEXT_C = sslContext2 ;    }    public ContextSSLFactory(){    }    public static SSLContext getSslContext(){        return SSL_CONTEXT_S ;    }    public static SSLContext getSslContext2(){        return SSL_CONTEXT_C ;    }    private static TrustManager[] getTrustManagersServer(){        FileInputStream is = null ;        KeyStore ks = null ;        TrustManagerFactory keyFac = null ;        TrustManager[] kms = null ;        try {            // 获得KeyManagerFactory对象. 初始化位默认算法            keyFac = TrustManagerFactory.getInstance("SunX509") ;            is =new FileInputStream( "D:\\work\\JavaProjects\\ocpptest\\keys\\sChat.jks" );            ks = KeyStore.getInstance("JKS") ;            String keyStorePass = "sNetty" ;            ks.load(is , keyStorePass.toCharArray()) ;            keyFac.init(ks) ;            kms = keyFac.getTrustManagers() ;        } catch (Exception e) {            e.printStackTrace();        }        finally{            if(is != null ){                try {                    is.close() ;                } catch (IOException e) {                    e.printStackTrace();                }            }        }        return kms ;    }    private static TrustManager[] getTrustManagersClient(){        FileInputStream is = null ;        KeyStore ks = null ;        TrustManagerFactory keyFac = null ;        TrustManager[] kms = null ;        try {            // 获得KeyManagerFactory对象. 初始化位默认算法            keyFac = TrustManagerFactory.getInstance("SunX509") ;            is =new FileInputStream( "D:\\work\\JavaProjects\\ocpptest\\keys\\cChat.jks" );            ks = KeyStore.getInstance("JKS") ;            String keyStorePass = "sNetty" ;            ks.load(is , keyStorePass.toCharArray()) ;            keyFac.init(ks) ;            kms = keyFac.getTrustManagers() ;        } catch (Exception e) {            e.printStackTrace();        }        finally{            if(is != null ){                try {                    is.close() ;                } catch (IOException e) {                    e.printStackTrace();                }            }        }        return kms ;    }    private static KeyManager[] getKeyManagersServer(){        FileInputStream is = null ;        KeyStore ks = null ;        KeyManagerFactory keyFac = null ;        KeyManager[] kms = null ;        try {            // 获得KeyManagerFactory对象. 初始化位默认算法            keyFac = KeyManagerFactory.getInstance("SunX509") ;            is =new FileInputStream( "D:\\work\\JavaProjects\\ocpptest\\keys\\sChat.jks" );            ks = KeyStore.getInstance("JKS") ;            String keyStorePass = "sNetty" ;            ks.load(is , keyStorePass.toCharArray()) ;            keyFac.init(ks, keyStorePass.toCharArray()) ;            kms = keyFac.getKeyManagers() ;        } catch (Exception e) {            e.printStackTrace();        }        finally{            if(is != null ){                try {                    is.close() ;                } catch (IOException e) {                    e.printStackTrace();                }            }        }        return kms ;    }    private static KeyManager[] getKeyManagersClient(){        FileInputStream is = null ;        KeyStore ks = null ;        KeyManagerFactory keyFac = null ;        KeyManager[] kms = null ;        try {            // 获得KeyManagerFactory对象. 初始化位默认算法            keyFac = KeyManagerFactory.getInstance("SunX509") ;            is =new FileInputStream("D:\\work\\JavaProjects\\ocpptest\\keys\\cChat.jks");            ks = KeyStore.getInstance("JKS") ;            String keyStorePass = "sNetty" ;            ks.load(is , keyStorePass.toCharArray()) ;            keyFac.init(ks, keyStorePass.toCharArray()) ;            kms = keyFac.getKeyManagers() ;        } catch (Exception e) {            e.printStackTrace();        }        finally{            if(is != null ){                try {                    is.close() ;                } catch (IOException e) {                    e.printStackTrace();                }            }        }        return kms ;    }}

CustomUrlHandler.java

package com.cccc.ocpptest;import io.netty.channel.ChannelHandler;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.handler.codec.http.FullHttpRequest;import org.eclipse.jetty.util.MultiMap;import org.eclipse.jetty.util.UrlEncoded;@ChannelHandler.Sharablepublic class CustomUrlHandler extends ChannelInboundHandlerAdapter {    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        // 只针对FullHttpRequest类型的做处理,其它类型的自动放过        if (msg instanceof FullHttpRequest) {            FullHttpRequest request = (FullHttpRequest) msg;            String uri = request.uri();            int idx = uri.indexOf("?");            if (idx > 0) {                String query = uri.substring(idx + 1);                // uri中参数的解析使用的是jetty-util包,其性能比自定义及正则性能高。                MultiMap values = new MultiMap();                UrlEncoded.decodeTo(query, values, "UTF-8");                request.setUri(uri.substring(0, idx));            }        }        ctx.fireChannelRead(msg);    }}

MyWebSocketHandler.java

package com.cccc.ocpptest;import io.netty.channel.ChannelHandler;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;import org.slf4j.Logger;import org.slf4j.LoggerFactory;//import java.net.InetAddress;@ChannelHandler.Sharablepublic class MyWebSocketHandler extends ChannelInboundHandlerAdapter {    private static final Logger logger = LoggerFactory.getLogger(MyWebSocketHandler.class);    @Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        logger.info("与客户端建立连接,通道开启");    }    @Override    public void channelInactive(ChannelHandlerContext ctx) throws Exception {        logger.info("与客户端断开连接,通道关闭");    }    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        TextWebSocketFrame message = (TextWebSocketFrame) msg;        logger.info("服务端收到数据: " + message.text());        // 此处需注意返回的数据的格式为TextWebSocketFrame。否则客户端收不到消息        ctx.channel().writeAndFlush(new TextWebSocketFrame(message.text()));    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        logger.error(cause.getMessage());//        cause.printStackTrace();    }}

NettySocketServer.java

package com.cccc.ocpptest;import javax.annotation.PostConstruct;import javax.annotation.PreDestroy;import javax.net.ssl.SSLEngine;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.ChannelPipeline;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.http.HttpObjectAggregator;import io.netty.handler.codec.http.HttpServerCodec;import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;import io.netty.handler.logging.LogLevel;import io.netty.handler.logging.LoggingHandler;import io.netty.handler.ssl.SslHandler;import io.netty.handler.stream.ChunkedWriteHandler;import org.springframework.stereotype.Component;/** * NettySocketServer * * @author MFY * @date 2019/10/1 */@Componentpublic class NettySocketServer {    private static SslHandler sslHandler = null ;    private EventLoopGroup bossGroup = null ;    private EventLoopGroup workerGroup = null ;    @PostConstruct    public void start(){        bossGroup = new NioEventLoopGroup() ;        workerGroup = new NioEventLoopGroup() ;        try{            ServerBootstrap serverStrap = new ServerBootstrap() ;            serverStrap.group(bossGroup , workerGroup)                    .channel(NioServerSocketChannel.class)                    .option(ChannelOption.SO_BACKLOG, 128)                    .option(ChannelOption.SO_KEEPALIVE, true)                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 5 * 60)                    .handler(new LoggingHandler(LogLevel.DEBUG))                    .childHandler(new ChannelInitializer() {                        @Override                        protected void initChannel(SocketChannel socketChannel) throws Exception {                            ChannelPipeline pie = socketChannel.pipeline() ;                            pie.addLast(new HttpServerCodec());                            pie.addLast(new HttpObjectAggregator(65535));                            pie.addLast(new ChunkedWriteHandler());                            // 对websocket url中的参数做解析处理的Handler                            pie.addLast(new CustomUrlHandler());                            // 对websocket做支持,其路径为/ws                            pie.addLast(new WebSocketServerProtocolHandler("/ws"));                            // 自定义业务逻辑处理的Handler                            pie.addLast(new MyWebSocketHandler());                            SSLEngine engine = ContextSSLFactory.getSslContext().createSSLEngine();                            engine.setUseClientMode(false);                            engine.setNeedClientAuth(true);                            pie.addFirst("ssl", new SslHandler(engine));                        }                    });            serverStrap.bind(30021).sync() ;            System.out.println("服务已开启");        }catch(Exception e){            e.printStackTrace() ;            bossGroup.shutdownGracefully() ;            workerGroup.shutdownGracefully() ;        }    }    private SslHandler getSslHandler(){        if(sslHandler == null ){            SSLEngine sslEngine = ContextSSLFactory.getSslContext().createSSLEngine() ;            sslEngine.setUseClientMode(false) ;            //false为单向认证,true为双向认证            sslEngine.setNeedClientAuth(true) ;            sslHandler = new SslHandler(sslEngine);        }        return sslHandler ;    }    @PreDestroy    public void close(){        bossGroup.shutdownGracefully() ;        workerGroup.shutdownGracefully() ;    }//    public static void main(String[] args) {//        new NettySocketServer().start() ;//    }}

OcpptestApplication.java

package com.cccc.ocpptest;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class OcpptestApplication {    public static void main(String[] args) {        SpringApplication.run(OcpptestApplication.class, args);    }}

三、Qt Wss测试

main.cpp

#include #include #include #include #include #include #include #include int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    QWebSocket webSocket;    QObject::connect(&webSocket, &QWebSocket::connected, [](){        qDebug() << "connected";    });    QObject::connect(&webSocket, &QWebSocket::disconnected, [](){        qDebug() << "disconnected";    });    QObject::connect(&webSocket,(void(QWebSocket::*)(QAbstractSocket::SocketError)) &QWebSocket::error, [&webSocket](QAbstractSocket::SocketError){        qDebug() << webSocket.errorString();    });    QFile cerFile1("D:/work/JavaProjects/ocpptest/keys/cChat.jks");        if (cerFile1.open(QFile::ReadOnly)) {                QSslKey sslKey;                QSslCertificate sslCer;                QList caCers;                if (QSslCertificate::importPkcs12(&cerFile1, &sslKey, &sslCer, &caCers, "sNetty")) {                        qDebug() << "OK";                        QSslConfiguration sslConfig;                        sslConfig.setCaCertificates(caCers);                        sslConfig.setPrivateKey(sslKey);                        sslConfig.setLocalCertificate(sslCer);            sslConfig.setProtocol(QSsl::AnyProtocol);                        webSocket.setSslConfiguration(sslConfig);                        QUrl url("wss://localhost:8000/ws");                        QNetworkRequest request(url);                        webSocket.open(request);                }                cerFile1.close();        }        return a.exec();}

到此,相信大家对"Netty与Qt如何实现wss双向认证"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0