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配合要注意的地方)