Kousalik
Kousalik

Reputation: 3137

What is the correct way to create succesfull SSL handshake in QT?

I have following three files: key.pem, cert.pem, ca.pem and working piece of code which creates SSL connection to my server. It is created using boost libraries.

Working boost code:

ssl::context ctx(io_service, ssl::context::sslv23);
ctx.set_password_callback(password_callback);
ctx.load_verify_file("ca.pem");
ctx.set_verify_mode(ssl::context_base::verify_peer);
ctx.use_certificate_file("cert.pem", ssl::context::pem);
ctx.use_private_key_file("key.pem", ssl::context::pem);

I have to rewrite this client code using Qt5 and its QSslSocket.

So far I created this code (with many small variations - dwitching files, changing password encofding):

    QSslSocket ssl;
    QList<QSslCertificate> certy = QSslCertificate::fromPath("C:\\ssltest\\cert.pem");

    ssl.setCaCertificates(certy);
    ssl.setLocalCertificate(QString("C:\\ssltest\\ca.pem"));
   ssl.setPrivateKey(QString("C:\\ssltest\\key.pem"),QSsl::Rsa,QSsl::Pem,QString("password").toUtf8());


    ssl.connectToHostEncrypted(QString("192.168.90.41"),6500);

    if(ssl.waitForEncrypted()){
        cout << "SSL handsake succesful!" << endl;
    } else {
        cout << "Something went terribly wrong!" << endl;
    }

But my Qt code cannot achieve succesfull SSL handshake (the TCP connection is succesful). What am I doing wrong ?

I immediatly get this SSL error: The host name did not match any of the valid hosts for this certificate. When i switch ca with cert, It tooks longer for it to fail, but i don't get any error.

Upvotes: 1

Views: 8764

Answers (3)

Mykola M.
Mykola M.

Reputation: 21

  1. Create key and certificate. I use next commands in Linux console:

openssl genrsa 2048 > server.key

openssl req -new -x509 -nodes -sha256 -days 365 -key server.key -out server.cert

When openssl ask you common name please type localhost, other can be blanc.

2.0. Create qt project ssl_client and add next code to main.c

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

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    ssl_client client(8000);
    return a.exec();
}

2.1. Create ssl_client.h with next code

#include <QtNetwork>

class ssl_client : public QObject
{
    Q_OBJECT
public:
    ssl_client(quint16 port);
private:
    void setUp();
    QSslSocket socket;
};

2.2. Create ssl_client.cpp with next code

#include "ssl_client.h"

ssl_client::ssl_client(quint16 port): socket(this)
{
    setUp();
    socket.connectToHostEncrypted("localhost", port);
    socket.write("hello world!");
}

void ssl_client::setUp(){
    QByteArray cert;

    QFile file_cert("server.cert");
    if(file_cert.open(QIODevice::ReadOnly)){
        cert = file_cert.readAll();
        file_cert.close();
    }
    else{
        qDebug() << file_cert.errorString();
    }
    QSslCertificate ssl_cert(cert);

    QList<QSslCertificate> listCA;
    listCA.append(ssl_cert);
    QSslConfiguration conf;
    conf.setCaCertificates(listCA);
    socket.setSslConfiguration(conf);
}

3.0. Create qt project ssl_server and add next code to main.c

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

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    ssl_server server(8000);
    return a.exec();
}

2.1. Create ssl_server.h with next code

#include <QtNetwork>

class ssl_server: public QTcpServer
{
    Q_OBJECT
public:
    ssl_server(quint16 port);
protected:
    void incomingConnection(qintptr socketDescriptor) override;
private slots:
    void ready();
    void printData();
private:
    void setUp();
    QSslSocket serverSocket;
};

2.2. Create ssl_server.cpp with next code

#include "ssl_server.h"

ssl_server::ssl_server(quint16 port): QTcpServer(), serverSocket(this)
{
    if (!this->listen(QHostAddress::LocalHost, port)) {
        qDebug() << "start listen port" << port << "failed";
        this->close();
        return;
    }
    else{
        foreach (const QNetworkInterface &netInterface, QNetworkInterface::allInterfaces()) {
            QNetworkInterface::InterfaceFlags flags = netInterface.flags();
            if( (bool)(flags & QNetworkInterface::IsRunning) && !(bool)(flags & QNetworkInterface::IsLoopBack)){
                foreach (const QNetworkAddressEntry &address, netInterface.addressEntries()) {
                    if(address.ip().protocol() == QAbstractSocket::IPv4Protocol){
                        qDebug() << "start listening " << address.ip().toString() <<  " on port" << port;
                    }
                }
            }
        }
    }
    setUp();
}

void ssl_server::incomingConnection(qintptr socketDescriptor)
 {
     if (serverSocket.setSocketDescriptor(socketDescriptor)) {
         addPendingConnection(&serverSocket);
         serverSocket.startServerEncryption();
     } else {
         serverSocket.close();
     }
 }

void ssl_server::ready(){
    qDebug() << "encrypted";
}

void ssl_server::printData(){
    QByteArray array = serverSocket.read(serverSocket.bytesAvailable());
    qDebug() << array;
}

void ssl_server::setUp(){

    QByteArray key;
    QByteArray cert;

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

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

    QSslKey ssl_key(key, QSsl::Rsa,QSsl::Pem,QSsl::PrivateKey,"localhost");
    QSslCertificate ssl_cert(cert);
    serverSocket.setLocalCertificate(ssl_cert);
    serverSocket.setPrivateKey(ssl_key);
    connect(&serverSocket, SIGNAL(encrypted()), this, SLOT(ready()));
    connect(&serverSocket, SIGNAL(readyRead()), this, SLOT(printData()) );
    serverSocket.setSocketOption(QAbstractSocket::KeepAliveOption, true );
}
  1. Add QT += network for each projects to *.pro file

  2. Build ssl_server and ssl_client projects

  3. Copy server.key and server.cert to bin directory of ssl_server project

  4. Copy only server.cert to bin directory of ssl_client project

  5. Run first ssl_server than run ssl_client, and you can see next results in console for ssl_server:

start listening "192.168.31.46" on port 8000

encrypted

"hello world!"

Upvotes: 1

GSc.
GSc.

Reputation: 1

I had the same task: SSL Server by BOOST:ASIO, SSL Client by Qt 5.8. For the Server side I used the given example. For the Qt Client side I had very long time to try but found meanwhile a solution - connectionion encrypted but not complete peer identification. Here is a code snipped - sorry for q&d:

CQtSsl::CQtSsl()
{
    m_SslSocket = NULL;
    QString SCertificatePath = "ca.pem";
    m_nSslError = -1;
    m_sSslError = "";
    if (QSslSocket::supportsSsl())
    {
        m_nSslError = 0;
        m_sSslError = "SSL is installed.";
    }
    else
    {
        m_nSslError = -101;
        m_sSslError = "Missing SSL installation.";
    }
    QList<QSslCertificate> CertList = QSslCertificate::fromPath(SCertificatePath);
    if (CertList.count() < 1)
    {
        m_nSslError = -11;
        m_sSslError = "No valid CA-Certifacte found: " + SCertificatePath;
    }
    m_caCertificate = CertList.at(0);
    QString sCertInfo = m_caCertificate.toText();
    if (m_nSslError == 0)
    {
        m_SslSocket = new QSslSocket();
        if (!m_SslSocket)
        {
            m_nSslError = -2;
            m_sSslError = "Creation of SSL-Socket failed!";
        }
    }
    if (m_nSslError == 0)
    {
        m_SslSocket->addCaCertificates(SCertificatePath);

    }
    if (m_nSslError == 0)
    {
        QSslConfiguration sslConfig = m_SslSocket->sslConfiguration();
        sslConfig.setPeerVerifyMode(QSslSocket::QueryPeer);
        //sslConfig.setProtocol(QSsl::SslV2);
        sslConfig.setSslOption(QSsl::SslOptionDisableServerNameIndication, true);
        m_SslSocket->setSslConfiguration(sslConfig);
    }
    if (m_nSslError == 0)
    {
        m_nSslError = 0;
        m_sSslError = "Client SSL-Socket established sucessfully.";
    }
}

int CQtSsl::Connect1(QString spHost, int npPort)
{
    if (m_nSslError != 0)
    {
        m_sSslError = "Client SSL-Socket not initialized properly";
        return m_nSslError;
    }
    m_nSslError = -10;
    connect(m_SslSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
    connect(m_SslSocket, SIGNAL(encrypted()), this, SLOT(connection_encrypted()));
    connect(m_SslSocket, SIGNAL(disconnected()), this, SLOT(connection_disconnected()));
    connect(m_SslSocket, SIGNAL(encrypted()), this, SLOT(ready()));
    connect(m_SslSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>))); 
    connect(m_SslSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
    m_SslSocket->connectToHostEncrypted(spHost, npPort, "client", QIODevice::ReadWrite, QAbstractSocket::IPv4Protocol);
    if (!m_SslSocket->waitForEncrypted())
    {
        QList<QSslError> errList = m_SslSocket->sslErrors();
        QAbstractSocket::SocketError err = m_SslSocket->error();
        m_sSslError = m_SslSocket->errorString() + ".";
        m_sSslError += " Connection failed.";
        m_nSslError = -10;
    }
    m_nSslError = 0;
    return m_nSslError;
}

int CQtSsl::Send(QByteArray paBuffer)
{
    int nReturn = -1;
    if (m_nSslError != 0)
    {
        return m_nSslError;
    }
    bool bEncrypted = m_SslSocket->isEncrypted();
    nReturn = m_SslSocket->write(paBuffer);
    bool bFlush = m_SslSocket->flush();
    return nReturn;
}

int CQtSsl::Send(QString spToSend)
{
    QByteArray aBuffer = spToSend.toUtf8();
    aBuffer.append((char)0);
    return Send(aBuffer);
}

void CQtSsl::connection_encrypted()
{
    int nDebug = 1; // Do something (log...)
}

void CQtSsl::connection_disconnected()
{
    int nDebug = 1; // Do something (log...)
}

void CQtSsl::ready()
{
    int nDebug = 1; // Do something (log...)
}

void CQtSsl::readyRead()
{
    ReadBuffer = m_SslSocket->readAll();
}

void sslErrors(QList<QSslError> sslErrors)
{
    int nDebug = 1; // Do something (log...)
}

int CQtSsl::GetLastError()
{
    return m_nSslError;
}

QString CQtSsl::GetLastErrorDesc()
{
    return m_sSslError;
}

Upvotes: 0

Nejat
Nejat

Reputation: 32655

Here is a Sample SSL client and server which uses a server.key and a server.crt file for encryption:

The Server Side:

#include <QtNetwork>
#include <QMessageBox>

class server : public QTcpServer
{
    Q_OBJECT
public:
    explicit server(QObject *parent = 0);
    ~server();

    QSslSocket server_socket;

public slots:


      void tcpReady();

      void encrypted();

      void sslError( QList<QSslError> errors );

      bool start_listen(int port_no);

protected:
    void incomingConnection( int descriptor );

};

server::server(QObject *parent) :
    QTcpServer(parent)
{

    server_socket.setProtocol(QSsl::SslV3);

    QByteArray key;
    QByteArray cert;

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

    QFile file_cert("server.crt");
    if(file_cert.open(QIODevice::ReadOnly))
    {
        cert = file_cert.readAll();
        file_cert.close();
    }
    else
    {
        qDebug() << file_cert.errorString();
    }


    QSslKey ssl_key(key, QSsl::Rsa,QSsl::Pem,QSsl::PrivateKey,"server");

    QSslCertificate ssl_cert(cert);

    server_socket.addCaCertificate(ssl_cert);
    server_socket.setLocalCertificate(ssl_cert);
    server_socket.setPrivateKey(ssl_key);


    connect( &server_socket, SIGNAL(sslErrors(QList<QSslError>)),
            this, SLOT(sslError(QList<QSslError>)) );

    connect(&server_socket,SIGNAL(encrypted()),this,SLOT(encrypted()));

    server_socket.setSocketOption(QAbstractSocket::KeepAliveOption, true );
}

server::~server()
{
    server_socket.disconnectFromHost();
    server_socket.waitForDisconnected();
}

void server::tcpReady()
{
    QByteArray array = server_socket.read( server_socket.bytesAvailable() );
    //...
}

void server::encrypted()
{
    connect( &server_socket, SIGNAL(readyRead()),
             this, SLOT(tcpReady()) );
    emit connection_established();
}

void server::sslError(QList<QSslError> errors)
{
    QString erroStr="";
    foreach (const QSslError &e, errors)
        erroStr.append(e.errorString()).append("\n");

    QMessageBox::warning( (QWidget *)this->parent(), tr("Error"),erroStr );

    server_socket.ignoreSslErrors();
}


bool server::start_listen(int port_no)
{
    if( !this->listen( QHostAddress::Any, port_no ) )
    {
        QMessageBox::warning( (QWidget *)this->parent(), tr("Error!"), tr("Cannot listen to port %1").arg(port_no) );

    }
    else
        return true;
}

void server::incomingConnection(int descriptor)
{
    if(server_socket.state()!=QAbstractSocket::UnconnectedState)
        return;
    if( !server_socket.setSocketDescriptor( descriptor ) )
    {
        QMessageBox::warning( (QWidget *)this->parent(), tr("Error!"), tr("Socket error!") );
        return;
    }
    else
    {
        server_socket.startServerEncryption();
    }
}

The client Side:

class Client : public QObject
{
    Q_OBJECT

public:
    explicit Client(QObject *parent = 0);
    ~Client();

    QSslSocket client_socket;

private slots:

    void tcpReady();

    void sslError( QList<QSslError> errors );
    void TCPError( QAbstractSocket::SocketError error );

    void connectToServer();

};


Client::Client(QObject *parent) :
    QObject(parent)
{

    client_socket.setProtocol(QSsl::SslV3);

    connect( &client_socket, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(TCPError(QAbstractSocket::SocketError)) );

    connect( &client_socket, SIGNAL(sslErrors(QList<QSslError>)),
            this, SLOT(sslError(QList<QSslError>)) );

    connect( &client_socket, SIGNAL(readyRead()),
            this, SLOT(tcpReady()) );

}

Client::~Client()
{

}

void Client::tcpReady()
{
    QByteArray array = client_socket.read( client_socket.bytesAvailable() );
    //...
}

void Client::sslError(QList<QSslError> errors)
{
    client_socket.ignoreSslErrors();
}

void Client::TCPError(QAbstractSocket::SocketError error)
{
}


void Client::connectToServer()
{
    client_socket.abort();
    client_socket.connectToHostEncrypted("192.168.0.10", 8000 );
}

You should generate a server.key and a server.crt file for your application.

Upvotes: 0

Related Questions