I have been struggling with all sorts of problems when designing the Server+Client with secure TSL connection in Qt. I am using:
Qt 5.2.0 (QSslSocket) XCA (based on OpenSSL 32-bit)
For testing I have created private key (RSA, 1024) and CA. Then a certificate signed by this CA with another private key (RSA, 1024). Common name set to 'localhost' as I am testing on one machine. I have the needed DLLs of OpenSSL in both client and server apps.
However no matter how I set things up the encrypted connection just does not work. Regular connection works fine but attempting the encryption always results in errors. Most common errors:
QSskError::CertificateSignatureFailed "The signature of the certificate is invalid"
QAbstractSocket::SslInternalError
QAbstractSocket::SslHandshakeFailedError
I might be missing something obvious or have overlooked something but I am at my wits end trying to solve this for some time now. Especially since I don't know if the problem is in my code, misusing openSSL, certificates or combination of these or something else in the mix I am not aware of.
I would appreciate any pointers to both common errors and especially reliable guides how to do this properly.
Thank you!
EDIT4: Provided minimalist code to demonstrate the problem.
EDIT3: If I move the loading of the CA certificate on client side before the connection is made and start the handshake immidiatelly after connection it actually works. However delayed handshake still does not with the same error as before (SslHandshakeFailedError). Documentation states only that CA must be loaded before handshake, not before connection...
EDIT2: This command succeeded in verifying the certificate (Verify return 0 (Ok)):
openssl s_client -connect <host>:<port> -CAfile <ca-file>
EDIT: On request, I have whipped up client app and server app using the same code as in real production minus all the stuff around it like threading, logging etc.
CLIENT
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_Connect_released();
void on_pushButton_Encrypt_released();
void socketEncrypted() { qDebug() << "Encrypted"; }
private:
QSslSocket m_Socket;
Ui::MainWindow *ui;
void socketWrite(const QByteArray &data);
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(&m_Socket,&QSslSocket::encrypted,
this,&MainWindow::socketEncrypted);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_Connect_released()
{
m_Socket.connectToHost("localhost", 1234);
}
void MainWindow::on_pushButton_Encrypt_released()
{
QFile file("PM_Client_CA.crt");
file.open(QIODevice::ReadOnly);
QSslCertificate certificate(&file);
m_Socket.addCaCertificate(certificate);
m_Socket.startClientEncryption();
QByteArray data("encrypt");
socketWrite(data);
}
void MainWindow::socketWrite(const QByteArray &data)
{
m_Socket.write(data);
m_Socket.flush();
}
SERVER
server.h
class Server : public QTcpServer
{
Q_OBJECT
public:
explicit Server(QObject *parent = 0);
public slots:
void clientEncrypted() { qDebug() << "Encrypted"; }
void clientRead();
protected:
void incomingConnection(qintptr handle);
private:
QSslSocket m_Socket;
};
server.cpp
Server::Server(QObject *parent) :
QTcpServer(parent)
{
QFile file("PM_Server.crt");
file.open(QIODevice::ReadOnly);
QSslCertificate certificate(&file);
file.close();
file.setFileName("PM_Server.pem");
file.open(QIODevice::ReadOnly);
QSslKey key(&file, QSsl::Rsa);
m_Socket.setLocalCertificate(certificate);
m_Socket.setPrivateKey(key);
connect(&m_Socket,&QSslSocket::encrypted,
this,&Server::clientEncrypted);
listen(QHostAddress::Any, 1234);
}
void Server::incomingConnection(qintptr handle)
{
m_Socket.setSocketDescriptor(handle);
connect(&m_Socket,&QSslSocket::readyRead,
this,&Server::clientRead);
}
void Server::clientRead()
{
disconnect(&m_Socket,&QSslSocket::readyRead,
this,&Server::clientRead);
m_Socket.startServerEncryption();
}