在Qt中设计具有安全TSL连接的Server + Client时,我一直在努力解决各种问题。我正在使用:
Qt 5.2.0(QSslSocket) XCA(基于OpenSSL 32位)
为了测试,我创建了私钥(RSA,1024)和CA.然后由此CA使用另一个私钥(RSA,1024)签署的证书。当我在一台机器上测试时,通用名称设置为“localhost”。我在客户端和服务器应用程序中都有所需的OpenSSL DLL。
但无论我如何设置,加密连接都无法正常工作。常规连接工作正常,但尝试加密总是会导致错误。最常见的错误:
QSskError::CertificateSignatureFailed "The signature of the certificate is invalid"
QAbstractSocket::SslInternalError
QAbstractSocket::SslHandshakeFailedError
我可能会遗漏一些显而易见的东西,或者忽略了一些东西,但是我现在正试图解决这个问题一段时间。特别是因为我不知道问题是否在我的代码中,滥用openSSL,证书或这些或其他组合的组合我不知道。
我很感激任何指向常见错误的指针,特别是可靠的指南如何正确地执行此操作。
谢谢!
EDIT4:提供极简主义代码来证明问题。
EDIT3:如果我在建立连接之前在客户端移动CA证书的加载,并在连接后立即启动握手,它实际上是有效的。但是,延迟握手仍然没有与之前相同的错误(SslHandshakeFailedError)。文档仅说明在握手之前必须加载CA,而不是在连接之前加载...
EDIT2:此命令成功验证证书(验证返回0(确定)):
openssl s_client -connect <host>:<port> -CAfile <ca-file>
编辑:根据要求,我使用与实际生产中相同的代码来减少客户端应用程序和服务器应用程序,减去它周围的所有内容,如线程,日志记录等。
客户端
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.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();
}
答案 0 :(得分:1)
您的代码中存在一些错误。首先,在客户端连接到服务器之后,应该在incomingConnection上的服务器端调用函数startServerEncryption()。如果你没有调用startServerEncryption(),那么就不会建立连接,并且readRead信号也不会发出。
您还应该在插槽clientEncrypted中连接服务器的readyRead信号。所以应该是这样的:
void Server::incomingConnection(qintptr handle)
{
m_Socket.setSocketDescriptor(handle);
m_Socket.startServerEncryption();
}
void Server::clientEncrypted()
{
connect(&m_Socket,&QSslSocket::readyRead,
this,&Server::clientRead);
}
另一个问题是无需在客户端加载证书。通常在服务器上加载证书。
最后一点是你应该将套接字的信号sslErrors连接到插槽并从那里调用ignoreSslErrors()。这应该在客户端和服务器端完成。它就像:
connect( &m_Socket, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(onSslError(QList<QSslError>)) );
void MainWindow::sslError(QList<QSslError> errors)
{
m_Socket.ignoreSslErrors();
}
您可以通过以下方式查看错误:
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 );
m_Socket.ignoreSslErrors();
}
最后一点是关于加载证书。我已成功使用以下代码在SSL3连接中加载许可证。你可以尝试一下:
m_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);
m_Socket.addCaCertificate(ssl_cert);
m_Socket.setLocalCertificate(ssl_cert);
m_Socket.setPrivateKey(ssl_key);
答案 1 :(得分:0)
似乎我已经弄明白了。首先,我必须感谢@jww帮助我验证问题不是证书。
事实上,问题是我以错误的顺序设置套接字并读取加密数据并根据它执行操作(如启动第二次握手等)。
无论如何,这似乎现在得到了回答,但是我遇到了握手问题的新问题。我已经打开了Qt论坛帖子:http://qt-project.org/forums/viewthread/41293/
再次感谢所有试图提供帮助的人!