Qt&Java笔记-Qt与Java进行SSL双向认证(Qt服务端,Java客户端)

Posted IT1995

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt&Java笔记-Qt与Java进行SSL双向认证(Qt服务端,Java客户端)相关的知识,希望对你有一定的参考价值。

程序运行截图如下:

Qt作为服务端,Java作为客户端。

这里的服务端是用的p12证书,客户端使用的是jks。

具体的生成方式看以前的博文。

QSSLServer.h

#ifndef QSSLSERVER_H
#define QSSLSERVER_H

#include <QObject>
#include <QList>
#include <QTcpServer>
#include <QSslError>

QT_BEGIN_NAMESPACE
class QSslCertificate;
class QSslKey;
class QSslSocket;
QT_END_NAMESPACE

class QSSLServer : public QTcpServer
{
    Q_OBJECT
public:
    QSSLServer(QObject *parent = nullptr);
    ~QSSLServer();

protected:
    void loadCertificate();
    void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE;

private slots:
    void sslErrors(const QList<QSslError> &errors);
    void link();
    void rx();
    void disconnected();

private:
    QList<QSslCertificate> m_publicCertificateList;
    QSslCertificate *m_privateCertificate;
    QSslKey *m_key;
    QSslSocket *m_client;
};

#endif // QSSLSERVER_H

QSSLServer.cpp

#include "QSSLServer.h"
#include <QSslSocket>
#include <QJsonDocument>
#include <QSslCertificate>
#include <QMap>
#include <QFile>
#include <QSslKey>
#include <QDebug>

QSSLServer::QSSLServer(QObject *parent) : QTcpServer(parent)
{
    m_key = new QSslKey;
    m_privateCertificate = new QSslCertificate;
    loadCertificate();
    if(!this->listen(QHostAddress::Any, 19999)){

        qCritical() << "Unable to start the TCP server";
        exit(0);
    }
    connect(this, &QSSLServer::newConnection, this, &QSSLServer::link);
    qDebug() << "The SSLServer started succeefully";
    qDebug() << "port: 19999";
}

QSSLServer::~QSSLServer()
{
    delete m_privateCertificate;
    delete m_key;
}

void QSSLServer::loadCertificate()
{
    QFile p12File(":/res/p_server.p12");
    if(!p12File.open(QIODevice::ReadOnly)){

        qDebug() << "The certificate file open failed!";
        exit(0);
    }

    bool ok = QSslCertificate::importPkcs12(&p12File, m_key, m_privateCertificate, &m_publicCertificateList, "cccccc");
    if(!ok){

        qDebug() << "The certificate import error!";
        exit(0);
    }
    p12File.close();
}

void QSSLServer::incomingConnection(qintptr socketDescriptor)
{
    QSslSocket *sslSocket = new QSslSocket(this);
    connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>)));
    sslSocket->setSocketDescriptor(socketDescriptor);
    sslSocket->setPrivateKey(*m_key);
    sslSocket->setLocalCertificate(*m_privateCertificate);
    sslSocket->addCaCertificates(m_publicCertificateList);
    sslSocket->setPeerVerifyMode(QSslSocket::VerifyPeer);
    sslSocket->startServerEncryption();
    addPendingConnection(sslSocket);
}

void QSSLServer::sslErrors(const QList<QSslError> &errors)
{
    foreach(const QSslError &error, errors)
        qDebug() << error.errorString();
}

void QSSLServer::link()
{
    QTcpSocket *clientSocket;
    clientSocket = nextPendingConnection();
    connect(clientSocket, &QTcpSocket::readyRead, this, &QSSLServer::rx);
    connect(clientSocket, &QTcpSocket::disconnected, this, &QSSLServer::disconnected);
}

void QSSLServer::rx()
{
    QTcpSocket* clientSocket = qobject_cast<QTcpSocket*>(sender());
    QString clientString = clientSocket->readAll();
    qDebug() << "client:" << clientString;

    //再发一条数据
    clientSocket->write("Hello Client");
}

void QSSLServer::disconnected()
{
    qDebug("Client Disconnected");
    QTcpSocket* clientSocket = qobject_cast<QTcpSocket*>(sender());
    clientSocket->deleteLater();
}

main.cpp

#include <QCoreApplication>
#include "QSSLServer.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QSSLServer sslServer;
    return a.exec();
}

Java客户端:

SslClient.java

package cn.it1995;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import java.io.InputStream;
import java.io.OutputStream;

public class SslClient implements SslContextProvider{


    public static void main(String[] args) throws Exception {

        new SslClient().run("127.0.0.1", 19999);
    }

    public TrustManager[] getTrustManagers() throws Exception {

        return SslUtil.createTrustManagers("D:\\\\IDEAProject\\\\SSLDemo\\\\src\\\\main\\\\resources\\\\client.jks", "cccccc");
    }

    public KeyManager[] getKeyManagers() throws Exception {

        return SslUtil.createKeyManagers("D:\\\\IDEAProject\\\\SSLDemo\\\\src\\\\main\\\\resources\\\\client.jks", "cccccc");
    }

    public String getProtocol() {

        return "TLSv1.2";
    }

    private SSLSocket createSSLSocket(String host, Integer port) throws Exception{

        return SslUtil.createSSLSocket(host, port, this);
    }

    public void run(String host, Integer port) throws Exception {

        try(SSLSocket sslSocket = createSSLSocket(host, port); OutputStream os = sslSocket.getOutputStream(); InputStream is = sslSocket.getInputStream()){

            System.out.println("已成功连接到服务端.......");

            os.write("Hello Server".getBytes());
            os.flush();

            System.out.println("已发送 Hello Server 到服务端");

            byte[] buf = new byte[1024];
            is.read(buf);

            System.out.println("接收到服务端消息:" + new String(buf));
        }
        catch (Exception e){

            e.printStackTrace();
        }
    }
}

SslContextProvider.java

package cn.it1995;

import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;

public interface SslContextProvider {

    TrustManager[] getTrustManagers() throws Exception;
    KeyManager[] getKeyManagers() throws Exception;
    String getProtocol();
}

SslUtil.java

package cn.it1995;

import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.Socket;
import java.security.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;

public class SslUtil {

    private static final String JKS = "JKS";

    public static KeyManager[] createKeyManagers(String keyStorePath, String password) throws Exception {

        return createKeyManagers(keyStorePath, password, password);
    }

    public static KeyManager[] createKeyManagers(String keyStorePath, String storePassword, String keyPassword) throws Exception {

        String defaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
        KeyManagerFactory kmInstance = KeyManagerFactory.getInstance(defaultAlgorithm);

        KeyStore ksInstance = KeyStore.getInstance(JKS);
        FileInputStream fileInputStream = new FileInputStream(keyStorePath);

        try{

            ksInstance.load(fileInputStream, storePassword.toCharArray());
        }
        catch (IOException e){

            e.printStackTrace();
        }
        catch (CertificateException e){

            e.printStackTrace();
        }
        finally {

            if(fileInputStream != null){

                fileInputStream.close();
            }
        }

        try{

            kmInstance.init(ksInstance, keyPassword.toCharArray());
        }
        catch (UnrecoverableKeyException e){

            e.printStackTrace();
        }

        return kmInstance.getKeyManagers();
    }

    public static SSLContext createSSLContext(SslContextProvider provider) throws Exception{

        SSLContext context = SSLContext.getInstance(provider.getProtocol());
        context.init(provider.getKeyManagers(), provider.getTrustManagers(), new SecureRandom());
        return context;
    }

    public static SSLServerSocket createSSLServerSocket(int port, SslContextProvider provider) throws Exception{

        SSLContext sslContext = createSSLContext(provider);
        SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
        SSLServerSocket sslServerSocket = (SSLServerSocket)sslServerSocketFactory.createServerSocket(port);
        sslServerSocket.setEnabledProtocols(new String[]{provider.getProtocol()});
        sslServerSocket.setNeedClientAuth(true);
        return sslServerSocket;
    }

    public static SSLSocket createSSLSocket(String host, int port, SslContextProvider provider) throws Exception{

        SSLContext sslContext = createSSLContext(provider);
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        SSLSocket sslSocket = (SSLSocket)sslSocketFactory.createSocket(host, port);
        sslSocket.setEnabledProtocols(new String[]{provider.getProtocol()});
        return sslSocket;
    }

    public static TrustManager[] createTrustManagers(String keyStorePath, String password) throws Exception{

        String defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmInstance = TrustManagerFactory.getInstance(defaultAlgorithm);
        KeyStore ksInstance = KeyStore.getInstance(JKS);
        FileInputStream fileInputStream = new FileInputStream(keyStorePath);

        try{

            ksInstance.load(fileInputStream, password.toCharArray());
        }
        catch (IOException e){

            e.printStackTrace();
        }
        catch (CertificateException e){

            e.printStackTrace();
        }
        finally {

            if(fileInputStream != null){

                fileInputStream.close();
            }
        }

        tmInstance.init(ksInstance);
        return tmInstance.getTrustManagers();
    }

    public static String getPeerIdentity(Socket socket){

        if(!(socket instanceof SSLSocket)){

            return null;
        }

        SSLSession sslSession = ((SSLSocket)socket).getSession();

        try{

            Principal peerPrincipal = sslSession.getPeerPrincipal();
            return getCommonName(peerPrincipal);
        }
        catch (SSLPeerUnverifiedException e){

            e.printStackTrace();
        }

        return "unknown client";
    }

    private static String getCommonName(Principal subject){

        try{

            LdapName ldapName = new LdapName(subject.getName());
            for(Rdn rdn : ldapName.getRdns()){

                if("cn".equalsIgnoreCase(rdn.getType())){

                    return (String)rdn.getValue();
                }
            }
        }
        catch (Exception e){

            e.printStackTrace();
        }

        return null;
    }
}

这里的双向认证的关键:

Qt:

Java:

源码打包下载地址:

https://github.com/fengfanchen/Java/tree/master/Ssl_QtServer_JavaClient

以上是关于Qt&Java笔记-Qt与Java进行SSL双向认证(Qt服务端,Java客户端)的主要内容,如果未能解决你的问题,请参考以下文章

Qt持久性对象进行序列化(同时比较了MFC与Java的方法)

Qt文档阅读笔记-QAudioInput&QAudioFormat解析与实例

Qt文档阅读笔记-QAudioInput&QAudioFormat解析与实例

Qt笔记-QTcpSocket中调用readAll要注意的地方(Java中RestTemplate和Qt配合要注意的地方)

Qt笔记-QTcpSocket中调用readAll要注意的地方(Java中RestTemplate和Qt配合要注意的地方)

Qt学习笔记窗口部件整理