溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Netty與Qt如何實(shí)現(xiàn)wss雙向認(rèn)證

發(fā)布時(shí)間:2021-06-26 15:05:21 來源:億速云 閱讀:441 作者:chen 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“Netty與Qt如何實(shí)現(xiàn)wss雙向認(rèn)證”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Netty與Qt如何實(shí)現(xiàn)wss雙向認(rèn)證”吧!

Netty 4.1.42.Final + Qt 5.9 + SpringBoot2.1.8

一、證書準(zhǔn)備

要使用ssl雙向驗(yàn)證,就必須先要生成服務(wù)端和客戶端的證書,并相互添加信任,具體流程如下(本人調(diào)試這個(gè)用例的時(shí)候,花了很多時(shí)間來驗(yàn)證證書是否正確,以及握手失敗的原因,這里證書生成過程只要按流程走,本人能保證絕對(duì)沒有問題) 現(xiàn)在打開cmd,在哪個(gè)目錄下打開,證書就會(huì)放在哪個(gè)目錄下:

第一步:生成Netty服務(wù)端私鑰和證書倉庫命令

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位(這個(gè)長度的密鑰目前可認(rèn)為無法被暴力破解)

  • -validity 365 證書有效期365天

  • -keyalg RSA 使用RSA非對(duì)稱加密算法

  • -dname "CN=localhost" 設(shè)置Common Name為localhost

  • -keypass sNetty密鑰的訪問密碼為sNetty

  • -storepass sNetty密鑰庫的訪問密碼為sNetty(其實(shí)這兩個(gè)密碼也可以設(shè)置一樣,通常都設(shè)置一樣,方便記)

  • -keystore sChat.jks 指定生成的密鑰庫文件為sChata.jks

  • -deststoretype pkcs12 必須要pkcs12

第二步:生成Netty服務(wù)端自簽名證書

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

第三步:生成客戶端的密鑰對(duì)和證書倉庫,用于將服務(wù)端的證書保存到客戶端的授信證書倉庫中

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

第四步:將Netty服務(wù)端證書導(dǎo)入到客戶端的證書倉庫中

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

最后一步:將客戶端的自簽名證書導(dǎo)入到服務(wù)端的信任證書倉庫中:

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

到這里,證書就生成完畢了,我們就可以得到兩個(gè)jks文件,一個(gè)是服務(wù)端的sChat.jks ,一個(gè)是客戶端的cChat.jks ,這兩個(gè)文件后面初始化sslCOntext的時(shí)候會(huì)用到

二、Netty服務(wù)端

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對(duì)象. 初始化位默認(rèn)算法
            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對(duì)象. 初始化位默認(rèn)算法
            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對(duì)象. 初始化位默認(rèn)算法
            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對(duì)象. 初始化位默認(rèn)算法
            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.Sharable
public class CustomUrlHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 只針對(duì)FullHttpRequest類型的做處理,其它類型的自動(dòng)放過
        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中參數(shù)的解析使用的是jetty-util包,其性能比自定義及正則性能高。
                MultiMap<String> values = new MultiMap<String>();
                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.Sharable
public 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("與客戶端斷開連接,通道關(guān)閉");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        TextWebSocketFrame message = (TextWebSocketFrame) msg;
        logger.info("服務(wù)端收到數(shù)據(jù): " + message.text());
        // 此處需注意返回的數(shù)據(jù)的格式為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
 */

@Component
public 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<SocketChannel>() {

                        @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());
                            // 對(duì)websocket url中的參數(shù)做解析處理的Handler
                            pie.addLast(new CustomUrlHandler());
                            // 對(duì)websocket做支持,其路徑為/ws
                            pie.addLast(new WebSocketServerProtocolHandler("/ws"));
                            // 自定義業(yè)務(wù)邏輯處理的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("服務(wù)已開啟");
        }catch(Exception e){
            e.printStackTrace() ;
            bossGroup.shutdownGracefully() ;
            workerGroup.shutdownGracefully() ;
        }

    }

    private SslHandler getSslHandler(){
        if(sslHandler == null ){
            SSLEngine sslEngine = ContextSSLFactory.getSslContext().createSSLEngine() ;
            sslEngine.setUseClientMode(false) ;
            //false為單向認(rèn)證,true為雙向認(rèn)證
            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;

@SpringBootApplication
public class OcpptestApplication {

    public static void main(String[] args) {
        SpringApplication.run(OcpptestApplication.class, args);
    }
}

三、Qt Wss測試

main.cpp

#include <QCoreApplication>
#include <QWebSocket>
#include <QNetworkRequest>
#include <QDebug>
#include <QSslConfiguration>
#include <QSslCertificate>
#include <QFile>
#include <QSslKey>

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<QSslCertificate> 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();
}

到此,相信大家對(duì)“Netty與Qt如何實(shí)現(xiàn)wss雙向認(rèn)證”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI