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

Posted IT1995

tags:

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

这里使用Java作为服务端,使用Qt作为客户端。

程序运行截图如下:

这里的证书Qt使用的p12,Java使用的jks,看以前的博文生成。

源码打包下载地址:

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

Qt客户端源码:

源码如下:

QSSLClient.h

#ifndef QSSLCLIENT_H
#define QSSLCLIENT_H

#include <QObject>
#include <QSslError>

QT_BEGIN_NAMESPACE
class QSslCertificate;
class QSslKey;
class QSslSocket;
QT_END_NAMESPACE

class QSSLClient : public QObject
{
    Q_OBJECT
public:
    QSSLClient(QObject *parent = nullptr);
    ~QSSLClient();
    void connectServer();
    void sendMsg(const QString &msg);
    void closeSocket();

Q_SIGNALS:
    void disconnected(void);

protected:
    void loadCertificate();

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

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

#endif // QSSLCLIENT_H

main.cpp

#include <QCoreApplication>
#include <QTimer>
#include <QDebug>
#include "QSSLClient.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QSSLClient sslClient;
    sslClient.connectServer();
    sslClient.sendMsg("Hello Server");
    QTimer::singleShot(2 * 1000, &sslClient, &QSSLClient::closeSocket);
    return a.exec();
}

QSSLClient.cpp

#include "QSSLClient.h"
#include <QSslSocket>
#include <QSslCertificate>
#include <QFile>
#include <QSslKey>
#include <QJsonDocument>
#include <QList>
#include <QDebug>

QSSLClient::QSSLClient(QObject *parent) : QObject(parent)
{
    m_key = new QSslKey;
    m_privateCertificate = new QSslCertificate;
    m_client = new QSslSocket;
    loadCertificate();

    connect(m_client, &QSslSocket::readyRead, this, &QSSLClient::rx);
    connect(m_client, &QSslSocket::disconnected, this, &QSSLClient::serverDisconnect);
    connect(m_client, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>)));

    m_client->addCaCertificates(m_publicCertificateList);
    m_client->setPrivateKey(*m_key);
    m_client->setLocalCertificate(*m_privateCertificate);
    m_client->setPeerVerifyMode(QSslSocket::VerifyPeer);

    qDebug() << "QSSLClient load over";
}

QSSLClient::~QSSLClient()
{
    delete m_privateCertificate;
    delete m_client;
    delete m_key;
}

void QSSLClient::connectServer()
{
    m_client->connectToHostEncrypted("localhost", 19999);
    if(m_client->waitForEncrypted(5000)){

        qDebug() << "Authentication Suceeded";
    }
    else{

        qDebug("Unable to connect to server");
        exit(0);
    }
}

void QSSLClient::sendMsg(const QString &msg)
{
    m_client->write(msg.toUtf8());
}

void QSSLClient::closeSocket()
{
    if(m_client->disconnect()){

        m_client->close();
        qDebug() << "close success";
    }
}

void QSSLClient::loadCertificate()
{
    QFile p12File(":/res/p_client.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 QSSLClient::sslErrors(const QList<QSslError> &errors)
{
    foreach (const QSslError &error, errors)
        qDebug() << error.errorString();
}

void QSSLClient::rx()
{
    QString getMsg = m_client->readAll();
    qDebug() << getMsg;
}

void QSSLClient::serverDisconnect()
{
//    m_client->close();
//    exit(0);
}

Java服务端源码:

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;
    }
}

SslServer.java

package cn.it1995;

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

public class SslServer implements SslContextProvider{
    @Override
    public TrustManager[] getTrustManagers() throws Exception {

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

    @Override
    public KeyManager[] getKeyManagers() throws Exception {

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

    @Override
    public String getProtocol() {

        return "TLSv1.2";
    }

    private ServerSocket createSSLSocket(int port) throws Exception {

        SSLServerSocket sslServerSocket = SslUtil.createSSLServerSocket(port, this);
        return sslServerSocket;
    }

    public void run(int port) throws Exception {

        ServerSocket serverSocket = createSSLSocket(port);
        System.out.println("服务端启动成功,等待客户端连接 ...... ...... ...... ......");

        while(true){

            try(SSLSocket client = (SSLSocket) serverSocket.accept(); OutputStream os = client.getOutputStream(); InputStream is = client.getInputStream()){

                System.out.println("客户端: " + SslUtil.getPeerIdentity(client) + " 成功连接!");

                byte[] b = new byte[1024];
                is.read(b);
                System.out.println("接收到客户端消息:" + new String(b));
                System.out.println("发送消息给客户端!");
                os.write("Hello Client".getBytes());
                os.flush();
                System.out.println("发送完成!");
            }
            catch (Exception e){

                e.printStackTrace();
            }
        }
    }

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

        new SslServer().run(19999);
    }
}

以上是关于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学习笔记窗口部件整理