本篇内容主要讲解“Netty与Qt如何实现wss双向认证”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Netty与Qt如何实现wss双向认证”吧!
Netty 4.1.42.Final + Qt 5.9 + SpringBoot2.1.8
要使用ssl双向验证,就必须先要生成服务端和客户端的证书,并相互添加信任,具体流程如下(本人调试这个用例的时候,花了很多时间来验证证书是否正确,以及握手失败的原因,这里证书生成过程只要按流程走,本人能保证绝对没有问题) 现在打开cmd,在哪个目录下打开,证书就会放在哪个目录下:
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
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
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的时候会用到
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 ;
}
}
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 {
// 只针对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<String> values = new MultiMap<String>();
UrlEncoded.decodeTo(query, values, "UTF-8");
request.setUri(uri.substring(0, idx));
}
}
ctx.fireChannelRead(msg);
}
}
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("与客户端断开连接,通道关闭");
}
@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();
}
}
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());
// 对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() ;
// }
}
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测试
#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();
}
到此,相信大家对“Netty与Qt如何实现wss双向认证”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/manfredlee/blog/3113471