AC certificate not trusted during handshake

399 views Asked by At

I have two application (a client and a server) which communicate with QSslSocket (protocol TLS). I create a key and a certificate for my server. I sign the certificate with an AC. (I have also create the AC).

#create AC
$ openssl genrsa -des3 -out ca.key
$ openssl req -new -x509 -days 365 -key ca.key -out ca.crt

#create server key
$ openssl genrsa -des3 -out server.key

#create server certificate (sign by AC)
$ openssl req -key server.key -new -out server.csr
$ openssl x509 -days 365 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial

I want my client verify the certificate of the server. My client :

bool MyClient::Connect()
{
    if(MySocket == nullptr)
    {
        MySocket = new QSslSocket();
    }

    //connect signal
    connect(MySocket, SIGNAL (sslErrors (QList<QSslError>)), this, SLOT (GererErreurs (QList<QSslError>)));
    ...

    MySocket->setProtocol(QSsl::TlsV1_2);

    QString path = "path/to/certificateSSL/";
    QSslConfiguration configuration = MySocket->sslConfiguration();

    QString ca = "ca.cert";
    if(configuration.addCaCertificates(chemin+ca) )
    {
        qDebug()<<"> CA OK";
    }

    MySocket->setSslConfiguration(configuration);

    MySocket->setPeerVerifyMode(QSslSocket::VerifyPeer);
    MySocket->setPeerVerifyName("myHostname");
    
    MySocket->abort();

    MySocket->connectToHostEncrypted(ServerAdress, static_cast<quint16> (PortServeur));

    if (!MySocket->waitForEncrypted(Timeout * 1000))
    {
        qDebug()<<("Error");
        return false;
    }

    qDebug()<<("Connexion client/serveur encrypted");

    ...
}

My server :

void MyServeur::incomingConnection(qintptr descriptionSocket)
{
    MySocket = new QSslSocket(this);
    MySocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);

    // signal connection
    ...
    

    QString path = "path/to/certificatsSSL/";
    QSslConfiguration configuration = Soquette->sslConfiguration();

    chargePrivateKey(path, configuration);
    if (!configuration.privateKey().isNull())
    {
        qDebug()<<"> Private key OK";
    }

    QString ca = "ca.cert";
    if(configuration.addCaCertificates(chemin+ca) )
    {
        qDebug() << "> CA OK";
    }
    
    chargeCertificate(path, configuration);
    if (!configuration.localCertificate().isNull())
    {
        qDebug() << "> server certificate OK";
    }

    MySocket->setSslConfiguration(configuration);

    MySocket->setPeerVerifyMode(QSslSocket::VerifyNone);

    MySocket->startServerEncryption();

    if (MySocket->waitForReadyRead (TIMEOUT_SOCKET * 1000) == false)
    {
        qDebug()<<"Notification de connexion cryptée du logiciel non-reçue";
        return;
    }

    qDebug()<<"Connection encrypted";
}

void MyServer::chargePrivateKey(const QString &chemin, QSslConfiguration &conf)
{
    QString serverKey = "server.key";
    QFile   fileKey(chemin + serverKey);
    if (!fileKey.open(QIODevice::ReadOnly))
    {
        qDebug() << "error" << chemin + serverKey;
        return;
    }
    QSslKey key(&fileKey, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "password");
    fileKey.close();
    conf.setPrivateKey(key);
}

void MyServer::chargeCertificat(const QString &chemin, QSslConfiguration &conf)
{
    QString serverCRT =  "server-signe.cert";
    QFile fileCertificat( chemin+serverCRT);
    if( ! fileCertificat.open( QIODevice::ReadOnly ) )
    {
        qDebug() << "Error"<<chemin+serverCRT ;
        return;
    }
    QSslCertificate certificate(&fileCertificat, QSsl::Pem);
    fileCertificat.close();
    conf.setLocalCertificate( certificate );
}

During the handshake, I have the error :

QSslError::CertificateUntrusted : The root CA certificate is not trusted for this purpose

I'm on MacOS I have add my AC in the keystore and the AC certificate is "reliable" enter image description here

I have no problem on Windows or Linux.

1

There are 1 answers

0
Alexander Dyagilev On

This is caused by SecureTransport backend, which Qt5 is using by default. There is no this issue in Qt6 in case it uses OpenSSL backend (which is by default in case OpenSSL libraries present).

To work it around, I'm using the following approach:

connect(m_socket.data(), QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors),
        this, &SslSocketUser::onSslErrors);

void SslSocketUser::onSslErrors(
        const QList<QSslError> &errors)
{
    if (errors.size() == 1 && errors.front().error() == QSslError::CertificateUntrusted)
    {
        auto chain = m_socket->peerCertificateChain();
        if (chain.empty())
            return;

        const auto caCerts = m_socket->sslConfiguration().caCertificates();

        for (const auto &caCert : caCerts)
        {
            for (const auto &cert : chain)
            {
                if (caCert.publicKey() == cert.publicKey())
                {
                    m_socket->ignoreSslErrors();
                    return;
                }
            }
        }
    }
}