Qt6开发自签名证书的https代理服务器

news2024/12/18 3:26:37

目标:制作一个具备类似Fiddler、Burpsuit、Wireshark的https协议代理抓包功能,但是集成到自己的app内,这样无需修改系统代理设置,使用QWebengineview通过自建的代理服务器,即可实现https包的实时监测、注入等自定义功能。

实现:

一、https代理服务器

1.使用QSslSocket类收发https包;使用多线程,提升代理服务器的性能。

ProxyClientThread.h

#ifndef PROXYCLIENTTHREAD_H
#define PROXYCLIENTTHREAD_H

#include <QObject>
#include <QTcpSocket>
#include <QNetworkProxy>
#include <QThread>
#include <QDebug>
#include <QSslSocket>
#include <QSslConfiguration>
#include <QFile>
#include <QSslKey>
#include <QByteArray>
#include <QtZlib/zlib.h>
#include <QRegularExpression>

struct HTTPHDR{
    QString host;
    quint16 port;
    bool newReq;
};

struct HTTPHDR2{
    quint8 CMD;
    QString CMDi;
    QString HOST;
    quint16 PORT;
    bool status;
};

enum ClientConnectionState {
    InitialRequest,
    TlsHandshake,
    DataTransfer
};

class ProxyClientThread : public QThread
{
    Q_OBJECT

public:
    ProxyClientThread(qintptr  sockDesc, QObject *parent = 0);
    ~ProxyClientThread();
    void run();
    QByteArray LastResquest;


private:
    QSslSocket clientSocket;
    QSslSocket serverSocket;
    QSslConfiguration sslConfig;
    int m_client_state=0;
    bool m_serverSocketConnected=false;

    QByteArray cNewReqData;
    QByteArray clientSockData;
    QByteArray serverSockData;


    void processClient();
    HTTPHDR2 processHeader(QByteArray hdr);
    bool loadCertificateAndKey();

    //HTTPHDR getHostInfo(QByteArray httpHeaderPartial);
    int pid;

    bool targetFound=false;//是否找到要注入的目标
    bool istargetHeader=true;//是否头部
    bool finishInject=false;//已完成注入
    QString cachedStr="";//缓存的内容;

private slots:
    void clientSockReadyRead();
    void serverSockConnected();
    void clientSockDisconnected();
    void serverSockDisconnected();
    void serverSockReadyRead();
    void clientTlsHandOk();
    void serverSockError(QAbstractSocket::SocketError errorMsg);
    void clientSockError(QAbstractSocket::SocketError errorMsg);

signals:
    void complete();
};

#endif // PROXYCLIENTTHREAD_H

ProxyClientThread.cpp部分代码

#include "proxyclientthread.h"

//#define DEBUG 1
QString keyFile="9291.0d30ab5b.js";
QString keyStr="}else e=await V.ImSdk.sendMessage({text:r,textExtra:a,referenceMessage:eQ";
QString injectStr=",window.MySendMsg=e";
ProxyClientThread::ProxyClientThread(qintptr sockDesc, QObject *parent) : QThread(parent)
{
    this->pid = sockDesc;

    //服务端连接
    connect (&this->serverSocket,SIGNAL(disconnected()),this,SLOT(serverSockDisconnected()));
    connect (&this->serverSocket,SIGNAL(readyRead()),this,SLOT(serverSockReadyRead()));
    connect (&this->serverSocket,SIGNAL(errorOccurred(QAbstractSocket::SocketError)),this,SLOT(serverSockError(QAbstractSocket::SocketError)));
    connect (&this->serverSocket,SIGNAL(connected()),this,SLOT(serverSockConnected()));
    this->serverSocket.setProxy(QNetworkProxy::NoProxy);

    //客户端
    m_client_state=InitialRequest;//客户端状态为初始化状态
    this->clientSocket.setSocketDescriptor(sockDesc);

    connect(&this->clientSocket, SIGNAL(disconnected()),this,SLOT(clientSockDisconnected()));
    connect(&this->clientSocket, SIGNAL(readyRead()),this,SLOT(clientSockReadyRead()),Qt::DirectConnection);
    connect(&this->clientSocket, SIGNAL(encrypted()), this, SLOT(clientTlsHandOk()));
    connect(&this->clientSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(clientSockError(QAbstractSocket::SocketError)));
}


void ProxyClientThread::clientSockReadyRead()
{
    this->processClient();
    return;
}
void ProxyClientThread::processClient()
{
    HTTPHDR2 pHead;

    //recieved incoming client packet
    this->clientSockData.clear();
    this->clientSockData = this->clientSocket.readAll();
    #ifdef DEBUG
    qDebug()<<this->pid<<"**收到客户端数据"<<this->clientSockData;
    #endif

    //查找匹配文件请求
    QString reqStr=QString(clientSockData);
    if(reqStr.contains("GET") and reqStr.contains(keyFile)){
        targetFound=true;
        qDebug()<<"找到要注入的文件--------------"<<reqStr;
        //修改请求头,不压缩
        reqStr.replace("Accept-Encoding: gzip, deflate, br","Accept-Encoding: identity");
        clientSockData=reqStr.toLocal8Bit();
    }

    if (this->serverSocket.state() == QAbstractSocket::ConnectedState){
        #ifdef DEBUG
        qDebug() <<this->pid<<": 4.2.向服务器发送请求:";//<<this->clientSockData;
        #endif
        serverSocket.write(clientSockData);
        return;
    }


    //处理 header
    pHead = this->processHeader(clientSockData.mid(0,100));
    if (!pHead.status){
        this->LastResquest = this->clientSockData;
        return;
    }


    //process SSL/TLS Connection;
    if (pHead.CMD == 3){    //CONNECT类型
         if (serverSocket.state() == QAbstractSocket::UnconnectedState){
             #ifdef DEBUG
             qDebug() <<this->pid<<": 1.收到客户发起CONNECT连接" << pHead.CMD << pHead.HOST << pHead.PORT;
             #endif
             m_client_state=TlsHandshake;//握手状态
             serverSocket.connectToHostEncrypted(pHead.HOST, pHead.PORT);

             return;
         }
    }


    if (serverSocket.state() == QAbstractSocket::UnconnectedState){
        #ifdef DEBUG
        qDebug()<<"***连接服务器";
        #endif
        LastResquest=clientSockData;
        serverSocket.connectToHostEncrypted(pHead.HOST,pHead.PORT);
        return;
    }

    return;
}

void ProxyClientThread::clientTlsHandOk(){
    //clientSockData = clientSocket.readAll();//读取客户端请求
    #ifdef DEBUG
    qDebug()<<this->pid<<": 4.<-- 已经和客户端ssl握手成功:"<<LastResquest;
    #endif
    serverSocket.write(LastResquest);

}

...

}

/*
 * 加载自签名证书
*/
bool ProxyClientThread::loadCertificateAndKey() {
    QFile certFile(":/certs/server.crt");
    if (!certFile.open(QIODevice::ReadOnly)) {
        qWarning() << "Certificate file not found!";
        return false;
    }
    QSslCertificate cert(&certFile);

    QFile keyFile(":/certs/server.key");
    if (!keyFile.open(QIODevice::ReadOnly)) {
        qWarning() << "Private key file not found!";
        return false;
    }
    QSslKey key(&keyFile, QSsl::Rsa);

    sslConfig.setLocalCertificate(cert);
    sslConfig.setPrivateKey(key);
    sslConfig.setProtocol(QSsl::TlsV1_2);

    return true;
}

3.proxyserver.h

#ifndef PROXYSERVER_H
#define PROXYSERVER_H

#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
#include <QTcpServer>
//#include <proxyclient.h>
#include <proxyclientthread.h>

class proxyServer : public QTcpServer {
    Q_OBJECT

public:
    explicit proxyServer(QObject* parent = nullptr) : QTcpServer(parent) {}

protected:
    void incomingConnection(qintptr socketDescriptor) override {
        // 创建子线程并传递 socket 描述符
        ProxyClientThread* workerThread = new ProxyClientThread(socketDescriptor, this);
        // 启动子线程
        workerThread->run();
    }
};

#endif // PROXYSERVER_H

代码的逻辑其实不难,按照代理服务器的连接过程补全相关代码就可以了。

二、QWebengineView部分

使用代理服务连接,该设置仅在app内有效,不影响其他应用。

设置QWebengineView的page忽略证书错误(因为是自签名证书),不处理的话无法访问https页面。

// 配置 QWebEngineView 使用代理
QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", 8787);
QNetworkProxy::setApplicationProxy(proxy);

//忽略证书错误
connect(webPage,SIGNAL(certificateError(QWebEngineCertificateError)),this,SLOT(on_certerror(QWebEngineCertificateError)));

void xxxx::on_certerror(QWebEngineCertificateError certerror){
    auto mutableError = const_cast<QWebEngineCertificateError&>(certerror);
    mutableError.acceptCertificate();
    qDebug()<<"忽略证书错误。";
    if(certerror.type()==QWebEngineCertificateError::CertificateAuthorityInvalid)
        {
            auto error=const_cast<QWebEngineCertificateError&>(certerror);
            qDebug()<<"忽略证书错误。";
            error.acceptCertificate();
        }
}

经过验证,这个方案可行,可以在代理服务器端修改客户端发起的请求,也可以修改服务器端返回的任何数据(已解密过的)后再返回给客户端,但是前提是要做好对应的处理工作,比如Content-length记得要修改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261366.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深度学习项目】目标检测之YOLO系列详解(一)

介绍 YOLO&#xff08;You Only Look Once&#xff09;是一种实时目标检测算法&#xff0c;由Joseph Redmon等人提出。与传统的基于滑动窗口和区域提案的目标检测方法不同&#xff0c;YOLO将目标检测问题框架化为一个单一的回归问题&#xff0c;直接从图像像素预测边界框和类别…

SpringBoot + minio + kkfile 实现文件预览

1、容器安装kkfileviewer 1.1 下载文件 这里以kkfile 4.4.0-beta版本为例 下载kkfile安装包及Dockerfile&#xff1a; https://codeup.aliyun.com/6254dee9a923b68581caaf50/kkfileviewer.git 1.2、构建镜像 git clone https://codeup.aliyun.com/6254dee9a923b68581caaf50…

HCIE之OSPF基础(十九)

OSPF理论 一、OSPF基本原理&#xff08;回顾&#xff09;1. 邻居建立1.1&#xff08;411&#xff09;HELLO包影响邻居建立的因素。1.2 其它影响邻居建立的因素&#xff1a;1.3 基本配置1.4 验证命令 2 DR选举2.1 为什么选&#xff1f;2.2 在哪选&#xff1f;2.3 怎么选&#xf…

python 基于 docx 文件模板生成 docx 或 PDF 文件

需求背景 提供一个Word文档模板&#xff0c;使用python程序替换里边的占位符&#xff0c;替换内容包括文本和图片&#xff0c;然后输出docx或者PDF文件。 功能演示 输入示例 输出示例 实现程序 import os import shutil import subprocess import timefrom docx import Doc…

CSS 实现带tooltip的slider

现代 CSS 强大的令人难以置信 这次我们来用 CSS 实现一个全功能的滑动输入器&#xff0c;也就是各大组件库都有的slider&#xff0c;效果如下 还可以改变一下样式&#xff0c;像这样 特别是在拖动时&#xff0c;tooltip还能跟随拖动的方向和速度呈现不同的倾斜角度&#xff0c…

MybatisPlus-扩展功能

代码生成 在使用MybatisPlus以后&#xff0c;基础的Mapper、Service、PO代码相对固定&#xff0c;重复编写也比较麻烦。因此MybatisPlus官方提供了代码生成器根据数据库表结构生成PO、Mapper、Service等相关代码。只不过代码生成器同样要编码使用&#xff0c;也很麻烦。 这里…

中后台管理信息系统:Axure12套高效原型设计框架模板全解析

中后台管理信息系统作为企业内部管理的核心支撑&#xff0c;其设计与实现对于提升企业的运营效率与决策能力具有至关重要的作用。为了满足多样化的中后台管理系统开发需求&#xff0c;一套全面、灵活的原型设计方案显得尤为重要。本文将深入探讨中后台管理信息系统通用原型方案…

40 基于单片机的温湿度检测判断系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52单片机&#xff0c;采用dht11温湿度传感器检测温湿度&#xff0c; 通过lcd1602显示屏各个参数&#xff0c;四个按键分别可以增加温湿度的阈值&#xff0c; 如果超过阈值&#xff0c;则…

SAP ABAP-日期格式问题 SAP内部错误,反序列化JSON字符串时发生异常 值 20241215 不是根据 ABAP 的 XML 格式的有效日期

SAP ABAP-日期格式问题 SAP内部错误,反序列化JSON字符串时发生异常 值 20241215 不是根据 ABAP 的 XML 格式的有效日期 在SAP内部用 YYYYMMDD没有问题 外部传入参数

深度学习——激活函数、损失函数、优化器

深度学习——激活函数、损失函数、优化器 1、激活函数1.1、一些常见的激活函数1.1.1、sigmoid1.1.2、softmax1.1.3、tanh1.1.4、ReLU1.1.5、Leaky ReLU1.1.6、PReLU1.1.7、GeLU1.1.8、ELU 1.2、激活函数的特点1.2.1、非线性1.2.2、几乎处处可微1.2.3、计算简单1.2.4、非饱和性1…

YOLOv5-7.0训练过程中出现报错Example: export GIT_PYTHON_REFRESH=quiet

出现报错&#xff1a; This initial message can be silenced or aggravated in the future by setting the $GIT_PYTHON_REFRESH environment variable. Use one of the following values: - quiet|q|silence|s|silent|none|n|0: for no message or exception - warn…

发布/部署WebApi服务器(IIS+.NET8+ASP.NETCore)

CS软件授权注册系统-发布/部署WebApi服务器(IIS.NET8ASP.NETCore) 目录 本文摘要VS2022配置发布VS2022发布WebApiIIS服务器部署WebApi 将程序文件复制到云服务器添加网站配置应用程序池配置dns域名配置端口阿里云ECS服务器配置19980端口配置https协议 (申请ssl证书)测试WebAp…

从零开始:如何在.NET Core Web API中完美配置Swagger文档

目录 新建项目 RestFul Swagger配置 注释展示 版本控制 Token传值 方法封装 新建项目 打开visual studio创建新项目&#xff0c;这里我们选择.net core web api模板&#xff0c;然后输入项目名称及其解决方案创建新项目 这里使用配置一些其他信息&#xff0c;根据自己情…

零基础开始学习鸿蒙开发-基础页面的设计

目录 1.样例图 2.逐项分析 2.1 头顶布局分析&#xff1a;首先我们要把第一行的图标绘制出来&#xff0c;一个左一个右&#xff0c;很明显&#xff0c;需要放在一个Row容器中&#xff0c;具体代码如下&#xff1a; 2.2 和头像同一行的布局&#xff0c;需要注意的是&#xff0c…

vscode借助插件调试OpenFoam的正确的.vscode配置文件

正确的备份文件位置&#xff1a; /home/jie/桌面/理解openfoam/正确的调试爆轰单进程案例/mydebugblastFoam 调试爆轰案例流体 并且工作区和用户区都是openfoam-7版本 问题&#xff1a;F5以debug模式启动后不停在断点 解决方法&#xff1a; 这里备份一下.vsode正确的配置&…

【小白包会的】使用supervisor 管理docker内多进程

使用supervisor 管理docker内多进程 一般情况下&#xff0c;一个docker是仅仅运行一个服务的 但是有的情况中&#xff0c;希望一个docker中运行多个进程&#xff0c;运行多个服务&#xff0c;也就是一个docker容器执行多个服务。 调研了一下&#xff0c;发现可以通过**super…

day11 性能测试(3)——Jmeter 断言+关联

【没有所谓的运气&#x1f36c;&#xff0c;只有绝对的努力✊】 目录 1、复习 2、查看结果树 多个http请求原因分析 3、作业 4、Jmeter断言 4.1 响应断言 4.1.1 案例 4.1.2 小结 4.2 json断言 4.2.1 案例 4.2.2 小结 4.3 断言持续时间 4.3.1 案例 4.3.2 小结 4.…

热更新解决方案3 —— xLua

概述 xLua框架导入和AB包相关准备 xLua导入 其它的导入 C#调用Lua 1.Lua解析器 using System.Collections; using System.Collections.Generic; using UnityEngine; //引用命名空间 using XLua;public class Lesson1_LuaEnv : MonoBehaviour {// Start is called before the fi…

Rk3588 FFmpeg 拉流 RTSP, 硬解码转RGB

RK3588 ,基于FFmpeg, 拉取RTSP,使用 h264_rkmpp 实现硬解码. ⚡️ RK3588 编译ffmpeg参考: Ubuntu x64 架构, 交叉编译aarch64 FFmpeg mpp Code RTSPvoid hardwave_init(AVCo

谷粒商城—分布式高级①.md

1. ELASTICSEARCH 1、安装elastic search dokcer中安装elastic search (1)下载ealastic search和kibana docker pull elasticsearch:7.6.2 docker pull kibana:7.6.2(2)配置 mkdir -p /mydata/elasticsearch/config mkdir -p /mydata/elasticsearch/data echo "h…