Question

I try to create a simple ssl connection between a client and a server in QtNetwork.

But I have a problem. First I run the server. Then I run the client. When I first run the client nothing happens, but when I run it second time I get QSslSocket::startServerEncryption: cannot start handshake on non-plain connection. I don't know how to fix it.

Here is the server:

//server.h

#ifndef SERVER_H
#define SERVER_H

#include <QtNetwork>
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QSslSocket>

class Server: public QTcpServer
{
    Q_OBJECT

public:
    Server(QObject * parent = 0);
    void incomingConnection(int handle);
    ~Server();

public slots:
    void startRead();

private:
    QSslSocket* socket;
};

#endif // SERVER_H

Server Source File :

//server.cpp

#include "server.h"
#include <iostream>
#include <QByteArray>
#include <QSslCertificate>
#include <QSslKey>
using namespace std;

Server::Server(QObject* parent) :
    QTcpServer(parent)
{
    socket = new QSslSocket;

    connect(socket, SIGNAL(encrypted()),
            this, SLOT(startRead()));

    listen(QHostAddress::Any, 8889);
}

void Server::startRead()
{
    char buffer[1024] = { 0 };
    socket->read(buffer, socket->bytesAvailable());
    cout << buffer << endl;
    socket->close();
}

void Server::incomingConnection(int socketDescriptor)
{
    if (socket->setSocketDescriptor(socketDescriptor))
    {
        connect(socket, SIGNAL(encrypted()),
                this, SLOT(startRead()));

        QByteArray key;
        QByteArray cert;

        QFile file_key("/path_to_key/rsakey");

        if(file_key.open(QIODevice::ReadOnly))
        {
            key = file_key.readAll();
            file_key.close();
        }
        else
        {
            qDebug() << file_key.errorString();
        }

        QFile file_cert("/path_to_certificate/mycert.pem");
        if(file_cert.open(QIODevice::ReadOnly))
        {
            cert = file_cert.readAll();
            file_cert.close();
        }
        else
        {
            qDebug() << file_cert.errorString();
        }

        QSslKey ssl_key(key, QSsl::Rsa);
        QSslCertificate ssl_cert(cert);

        socket->setPrivateKey(ssl_key);
        socket->setLocalCertificate(ssl_cert);

        QSslConfiguration cfg = socket->sslConfiguration();
        cfg.caCertificates();

        socket->startServerEncryption();
    }
}

Server::~Server()
{
    delete socket;
}

Server Main File :

//server main

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

int main(int argc, char** argv)
{
    QCoreApplication app(argc, argv);

    Server server;

    return app.exec();
}

Here is the Client :

//client.h
#ifndef CLIENT_H
#define CLIENT_H

#include <QtNetwork>
#include <QObject>
#include <QString>
#include <QSslSocket>

class Client: public QObject
{
    Q_OBJECT

public:
    Client(QObject* parent = 0);
    ~Client();
    void start(QString address, quint16 port);

public slots:
    void startTransfer();

private:
    QSslSocket client;
};


#endif // CLIENT_H

Client Source File :

// client.cpp

#include "client.h"
#include <QDebug>

Client::Client(QObject* parent) :
    QObject(parent)
{
    connect(&client, SIGNAL(encrypted()),
            this, SLOT(startTransfer()));
}

Client::~Client()
{
    client.close();
}

void Client::start(QString address, quint16 port)
{
    client.connectToHostEncrypted(address, port);
}

void Client::startTransfer()
{
    qDebug() << "startTransfer()";
    client.write("Hello, world", 13);
}

Client Main File :

//client main

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

int main(int argc, char** argv)
{
    QCoreApplication app(argc, argv);

    Client client;
    client.start("127.0.0.1", 8889);

    return app.exec();
}

Anyone can tell me what's missing?

Was it helpful?

Solution

The problem here is that QSslSocket can't be reused (I opened a bug about this QTBUG-59348), so once you call setSocketDescriptor for the second time (once a new connection arives) the internal mode is in Encrypted state.

Your code also has the issue that even if QSslSocket could be reused, you create a single socket at the constructor, so you can only accept a single connection at time. You must instead create a new QSslSocket inside incommingConnection, and NO YOU DON'T NEED to call nextPendingConnection() if you have an implementation of QTcpServer, if you do you will get two objects pointing to the same FD, one QTcpSocket and one QSsqSocket created by you.

OTHER TIPS

You should try to get the socket descriptor from the nextPendingConnection and set the socket descriptor of the QSslsocket

First: You have to connect the signal of the QTcpServer::newConnection with a self made slot (for example newConnectionRecognized)

Second: Set the socketDescriptor of the QSslSocket with the socket descriptor of the QTcpServer::nextPendingConnection()->socketDescriptor

constructor:
{
server = new QTcpServer;
...
server->listen(QHostAddress::Any,1234)
...
connect(server,SIGNAL(newConnection()),this,SLOT(newConnectionRecognized()));
...
}

void SslServer::newConnectionRecognized()
{
incomingConnection(server->nextPendingConnection()->socketDescriptor());
...
}

void SslServer::incomingConnection(int socket_descriptor)
{

   socket = new QSslSocket(this);

   ...

   if (!socket->setSocketDescriptor(socket_descriptor))
   {
      qWarning("! Couldn't set socket descriptor");
      delete socket;
      return;
   }

  ...
}

I hope it helped...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top