配置QSslConfiguration让客户端程序跳过本地SSL验证



  • 大家下午好哦。今天我们在重新制作我们萌梦聊天室的时候,出现了这样的问题。那就是我们的客户端能够对qtdream.com服务器进行登录,但是不能对localhost服务器(也就是本机啦)进行登录。这究竟是什么原因呢?
    原图来自http://www.pixiv.net/member_illust.php?mode=medium&illust_id=60269167
    因为这个问题是在我们引入了https的时候出现的,所以我们把原因定位到为什么会导致https出现问题。后面我看到了QNetworkRequest这个类,看到了里面有这样一个函数:🍓

    QNetworkRequest::setSslConfiguration(const QSslConfiguration &configuration);
    

    这个函数呢,可以设置SSL的配置,包括OpenSSL以及其它的SSL,都是这样的配置。然后我又看QSslConfiguration这个类:,并且看到了这样的函数:

    QSslSocket::setPeerVerifyMode(QSslSocket::PeerVerifyMode mode);
    

    我在仔细研读文档后,发现其中的端倪。原来默认设置是对服务器的安全证书进行验证的。我们本地的网站由于证书有问题,从而无法导入证书,这个时候呢,无法通过验证。这个时候,默认的验证模式无法通过,从而服务端无法获得到客户端传入的数据,因此我们需要进行这样的操作:

        // 设置SSL配置
        QSslConfiguration sslConf;
        sslConf.setPeerVerifyMode( QSslSocket::VerifyNone );
        r.setSslConfiguration( sslConf );
    

    这样操作的话,我们就可以不再检测SSL了。这样我们就可以顺利地通过我们的客户端连接到我们的服务器了。怎样?很简单吧。



  • 既然是VerifyNone,那还要QSslConfiguration干什么?完全去掉QSslConfiguration可以吗?



  • @stlcours 因为不配置这个,会采用默认的配置,导致我的聊天室软件连不上我的https的服务器。



  • 所以只是对本机登录不行的。对远程登录https://qtdream.com这边儿是可以的。



  • @jiangcaiyang123 你好,我正在学习QWebSocket编程,跟着示例做了服务器和客户端,服务器设置了VerifyNone,也生成了证书。客户端没有生成证书,因为服务器端设置了verifyNone。然后在同一台机器上运行测试,也就是这里所说的客户端连接本地服务器,但是客户端的state一直停留在connectingState,服务器端没有任何回馈,然后就一直这个状态,没有后续,没有emit任何错误。
    1.试过在客户端设置VerifyNone,无效
    2.试过在客户端生成证书,也无效
    3.试过非安全模式,成功,但没意义。

    困扰好几天了,请问你是怎么建立连接的?望指教

    平台是mac os sierra
    附上我的代码:
    服务器端:
    #include "fd_sslwebsocket_server.h"

    #include "fd_sslwebsocket_server.h"
    #include "QtWebSockets/QWebSocketServer"
    #include "QtWebSockets/QWebSocket"
    #include <QtCore/QDebug>
    #include <QtCore/QFile>
    #include <QtNetwork/QSslCertificate>
    #include <QtNetwork/QSslKey>

    QT_USE_NAMESPACE

    fd_sslwebsocket_server::fd_sslwebsocket_server(quint16 port, QObject *parent) :
    QObject(parent),
    m_pWebSocketServer(Q_NULLPTR),
    m_clients()
    {
    m_pWebSocketServer = new QWebSocketServer(QStringLiteral("SSL Echo Server"),
    QWebSocketServer::SecureMode,
    this);

      QSslConfiguration sslConfiguration;
      QFile certFile(QStringLiteral(":/ssl/test/CARoot1024.cert"));
      QFile keyFile(QStringLiteral(":/ssl/test/CARoot1024.key"));
      certFile.open(QIODevice::ReadOnly);
      keyFile.open(QIODevice::ReadOnly);
      QSslCertificate certificate(&certFile, QSsl::Pem);
      QSslKey sslKey(&keyFile, QSsl::Rsa, QSsl::Pem);
      certFile.close();
      keyFile.close();
      sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
      sslConfiguration.setLocalCertificate(certificate);
      sslConfiguration.setPrivateKey(sslKey);
      sslConfiguration.setProtocol(QSsl::TlsV1SslV3);
      m_pWebSocketServer->setSslConfiguration(sslConfiguration);
    
    
      if (m_pWebSocketServer->listen(QHostAddress::Any, port))
      {
          qDebug() << "SSL Echo Server listening on port" << port;
          connect(m_pWebSocketServer, &QWebSocketServer::newConnection,
                  this, &fd_sslwebsocket_server::onNewConnection);
          connect(m_pWebSocketServer, &QWebSocketServer::sslErrors,
                  this, &fd_sslwebsocket_server::onSslErrors);
          connect(m_pWebSocketServer, &QWebSocketServer::peerVerifyError,
                  this, &fd_sslwebsocket_server::onPeerVerifyError);
      }
    

    }

    void fd_sslwebsocket_server::onPeerVerifyError(const QSslError &error)
    {
    qDebug() << error;
    }

    fd_sslwebsocket_server::~fd_sslwebsocket_server()
    {
    m_pWebSocketServer->close();
    qDeleteAll(m_clients.begin(), m_clients.end());
    }

    void fd_sslwebsocket_server::onNewConnection()
    {
    QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();

      qDebug() << "Client connected:" << pSocket->peerName() << pSocket->origin();
    
      connect(pSocket, &QWebSocket::textMessageReceived, this, &fd_sslwebsocket_server::processTextMessage);
      connect(pSocket, &QWebSocket::binaryMessageReceived,
              this, &fd_sslwebsocket_server::processBinaryMessage);
      connect(pSocket, &QWebSocket::disconnected, this, &fd_sslwebsocket_server::socketDisconnected);
      //connect(pSocket, &QWebSocket::pong, this, &fd_sslwebsocket_server::processPong);
    
      m_clients << pSocket;
    

    }

    void fd_sslwebsocket_server::processTextMessage(QString message)
    {
    QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
    qDebug() << "Received message:" << message;
    if (pClient)
    {
    pClient->sendTextMessage(message);
    }
    }

    void fd_sslwebsocket_server::processBinaryMessage(QByteArray message)
    {
    QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
    if (pClient)
    {
    pClient->sendBinaryMessage(message);
    }
    }

    void fd_sslwebsocket_server::socketDisconnected()
    {
    qDebug() << "Client disconnected";
    QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
    if (pClient)
    {
    m_clients.removeAll(pClient);
    pClient->deleteLater();
    }
    }

    void fd_sslwebsocket_server::onSslErrors(const QList<QSslError> &)
    {
    qDebug() << "Ssl errors occurred";
    }


    客户端:
    #include "fd_sslwebsocket_client.h"
    #include <QtCore/QDebug>
    #include <QtWebSockets/QWebSocket>
    #include <QCoreApplication>

    #include <QtCore/QFile>
    #include <QtNetwork/QSslCertificate>
    #include <QtNetwork/QSslKey>

    QT_USE_NAMESPACE

    fd_sslwebsocket_client::fd_sslwebsocket_client(const QUrl &url, QObject *parent) :
    QObject(parent),
    m_webSocket()
    {

    /*
    QSslConfiguration sslConfiguration;

      QFile certFile(QStringLiteral(":/ssl/test/CARoot1024.cert"));
      QFile keyFile(QStringLiteral(":/ssl/test/CARoot1024.key"));
      certFile.open(QIODevice::ReadOnly);
      keyFile.open(QIODevice::ReadOnly);
      QSslCertificate certificate(&certFile, QSsl::Pem);
      QSslKey sslKey(&keyFile, QSsl::Rsa, QSsl::Pem);
      certFile.close();
      keyFile.close();
    
      sslConfiguration.setLocalCertificate(certificate);
      sslConfiguration.setPrivateKey(sslKey);
    
      sslConfiguration.setProtocol(QSsl::TlsV1SslV3);
      sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyPeer);
      m_webSocket.setSslConfiguration(sslConfiguration);
    

    */

      connect(&m_webSocket, &QWebSocket::connected, this, &fd_sslwebsocket_client::onConnected);
    
      connect(&m_webSocket, static_cast<void(QWebSocket::*)(QAbstractSocket::SocketError)>(&QWebSocket::error), this,  &fd_sslwebsocket_client::onError);
    
      typedef void (QWebSocket:: *sslErrorsSignal)(const QList<QSslError> &);
      connect(&m_webSocket, static_cast<sslErrorsSignal>(&QWebSocket::sslErrors),
              this, &fd_sslwebsocket_client::onSslErrors);
    
      connect(&m_webSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
                        this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
    
      m_webSocket.open(QUrl(url));
      qDebug() << "Starting open..." << url;
    

    }

    void fd_sslwebsocket_client::socketStateChanged(QAbstractSocket::SocketState state)
    {
    qDebug() << "state:" << state;
    }

    void fd_sslwebsocket_client::onError(QAbstractSocket::SocketError error)
    {
    qDebug() << "error code:" << error << ". error string" << m_webSocket.errorString();
    }

    void fd_sslwebsocket_client::onConnected()
    {

      qDebug() << "WebSocket connected";
      connect(&m_webSocket, &QWebSocket::textMessageReceived,
              this, &fd_sslwebsocket_client::onTextMessageReceived);
    
      m_webSocket.sendTextMessage(QStringLiteral("Hello, world!"));
    
      int lv_count = 0;
      while (lv_count < 10000) {
          m_webSocket.sendTextMessage(QStringLiteral("Hello, world!") + QString::number(lv_count, 10));
          lv_count++;
      }
      qApp->quit();
    

    }

    void fd_sslwebsocket_client::onTextMessageReceived(QString message)
    {
    qDebug() << "Message received:" << message;
    //qApp->quit();
    }

    void fd_sslwebsocket_client::onSslErrors(const QList<QSslError> &errors)
    {
    Q_UNUSED(errors);

      // WARNING: Never ignore SSL errors in production code.
      // The proper way to handle self-signed certificates is to add a custom root
      // to the CA store.
    
      qDebug() << m_webSocket.errorString();
    
      //m_webSocket.ignoreSslErrors();
    

    }



  • @shwth33 用SSL的key和cer来验证的吗?这个我没有尝试过,我好像只是用open(),没有那么复杂的样子啊。



  • @shwth33 你可以在客户端里嵌入一个证书试试。



  • @stlcours 这个问题估计2018年还会出现,我们层经做过静态编译带聊天,曾经实现了,现在由于架构更改了,可能问题又是一大堆吧。


Log in to reply
 

走马观花

最近的回复

  • C

    Qt for MCU需要商业授权的

    read more
  • Qt for MCUs

    搭建Qt for MCUs PC端开发环境。qt for mcus提供了一个完整的图形框架和工具包,包含了在MCUs上设计、开发和部署gui所需的一切。它允许您在裸机或实时操作系统上运行应用程序。

    先决条件

    开发主机环境支持仅限于Windows 10

    MSVC compiler v19.16 (Visual Studio 2017 15.9.9 or newer) x64

    CMake v3.13 or newer (you can install it using the Qt Online installer) x64

    使用Qt联机安装程序安装Qt for MCUs,该安装程序可通过Qt帐户下载

    安装Qt 5.14和Qt Creator 4.11 or higher

    安装链接

    › Qt: https://account.qt.io/downloads
    › CMake: https://cmake.org/download/
    › Python 2.7 32-bit: https://www.python.org/downloads/release/python-2716/
    › Arm GCC: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnutoolchain/gnu-rm/downloads
    › J-Link Software Pack: https://www.segger.com/downloads/jlink/JLink_Windows.exe
    › J-Link OpenSDA Firmware: https://www.segger.com/downloads/jlink/OpenSDA_MIMXRT1050-EVKHyperflash
    › STM32CubeProgrammer: https://www.st.com/en/development-tools/stm32cubeprog.html
    › STM32 ST-LINK Utility: https://www.st.com/en/development-tools/stsw-link004.html​​​​​​​

    Qt Creator设置 启用Qt Creator插件 选择“帮助>关于插件”,然后从列表中选择“MCU支持(实验性)”插件,重新启动Qt Creator以应用更改
    替代文字 为MCU创建Qt工具包

    选择工具>选项>设备>MCU

    选择Qt for MCUs-Desktop 32bpp作为目标

    如果尚未设置,请提供Qt for MCUs安装目录的路径。

    单击Apply应用。

    替代文字

    替代文字
    替代文字

    注意:

    编译器要选X64,Qt版本要选64bit,CMake Tool选x64

    打开恒温器项目demo

    选择文件>打开文件或项目。。。

    打开CMakefiles.txt文件来自thermo文件夹的文件。

    选择Qt作为MCU-桌面32bpp套件。

    单击“配置项目”以完成。

    替代文字

    问题

    开发主机环境支持仅限于Windows 10

    C++编译失败,文本大字体.pixelSize.

    文本类型无法正确呈现需要复杂文本布局的unicode序列。对复杂文本使用StaticText

    read more
  • H

    hi 有问题请教你,方便加个联系方式吗

    read more
  • boost.asio是一个很棒的网络库,这回儿我也开始系统地学习起来了。想想当年接触boost,也有八年多了。这次开始接触boost,觉得既熟悉又陌生。熟悉的是小写字母+下划线的命名方式、晦涩的模板、很慢的编译速度以及较大的程序体积,陌生的是asio的各种概念:io服务、接收器、套接字等等:我之前对网络编程不是非常了解。

    于是根据我的理解,参考《Boost.Asio C++网络编程》实现了这样一个简单的客户端和服务端通信的例子,例子非常简单,还不完善,但是幸运的是,可以在本机上互通了。
    下面是客户端的代码:

    #include <iostream> #include <boost/asio.hpp> #include <boost/proto/detail/ignore_unused.hpp> using namespace std; using namespace boost::asio; using namespace boost::system; using namespace boost::proto::detail;// 提供ignore_unused方法 void writeHandler( const boost::system::error_code& ec, size_t bytesTransferred ) { if ( ec ) { cout << "Write data error, code: " << ec.value( ) << "transferred: " << bytesTransferred << endl; } else { cout << "OK! " << bytesTransferred << "bytes written. " << endl; } } int main(int argc, char *argv[]) { ignore_unused( argc ); ignore_unused( argv ); io_service service; ip::tcp::socket sock( service ); ip::tcp::endpoint ep( ip::address::from_string( "127.0.0.1" ), 6545 ); boost::system::error_code ec; sock.connect( ep, ec ); if ( ec ) { cout << "Connect error, code: " << ec.value( ) << ", We will exit." << endl; return ec.value( ); } else { char buf[1024] = "Hello world!"; sock.async_write_some( buffer( buf ), writeHandler ); sock.close( ); } return service.run( ); }

    下面是服务端的代码:

    #include <iostream> #include <boost/asio.hpp> #include <boost/proto/detail/ignore_unused.hpp> using namespace std; using namespace boost::asio; using namespace boost::system; using namespace boost::proto::detail;// 提供ignore_unused方法 void acceptHandle( const boost::system::error_code& code ) { cout << "Accepted." << endl; } int main(int argc, char *argv[]) { ignore_unused( argc ); ignore_unused( argv ); io_service service; ip::tcp::endpoint ep( ip::address::from_string( "127.0.0.1" ), 6545 ); boost::system::error_code ec; ip::tcp::socket sock( service ); ip::tcp::acceptor acceptor( service, ep ); acceptor.async_accept( sock, acceptHandle ); if ( ec ) { cout << "There is an error in server. code: " << ec.value( ) << endl; } return service.run( );// 阻塞运行 }

    运行结果是这样的:
    78448d7b-b3ae-42fc-9e2e-4dd2fbdac2c2-image.png

    我对boost.asio中几个概念的理解:

    io_service,这就是一个类似事件循环的东西,它为io设备提供服务,故名。不管是套接字、文件还是串口设备,都要使用它的服务。它的run()函数相当于启动了一个事件循环。一旦有消息了,即进行响应。这也是实现异步编程的重要基础。 socket,这个类则是套接字,可以处理TCP或者是UDP请求。有同步以及异步的处理方式,也有带异常以及不带异常的处理方式。 acceptor,接收器,仅仅是服务端使用。相当于其余框架中的listener,作接收用的。

    比较浅显,如果有不当之处,敬请指正。

    read more

关注我们

微博
QQ群