我有一个与QTcpSocket
一起工作的客户机-服务器应用程序。现在我想使用加密的SSL连接,因此我尝试切换到QSslSocket
。但我无法连接到服务器。下面是客户端的代码:
ConnectionHandler::ConnectionHandler(QString ip, int port, QObject *parent) : QObject(parent) {
// connect(this->socket, SIGNAL(connected()), this, SLOT(connected()));
connect(this->socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
connect(this->socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(this->socket, SIGNAL(encrypted()), this, SLOT(encryptedReady()));
connect(this->socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(SSLerrorOccured(const QList<QSslError> &)));
connect(this->socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
connect(this->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
this->ip = ip;
this->port = port;
}
void ConnectionHandler::socketStateChanged(QAbstractSocket::SocketState state) {
qDebug() << state;
}
void ConnectionHandler::socketError(QAbstractSocket::SocketError) {
qDebug() << this->socket->errorString();
}
void ConnectionHandler::encryptedReady() {
qDebug() << "READY";
}
void ConnectionHandler::SSLerrorOccured(const QList<QSslError> &) {
qDebug() << "EEROR";
}
void ConnectionHandler::connectToServer() {
// this->socket->connectToHost(this->ip, this->port);
this->socket->connectToHostEncrypted(this->ip, this->port);
if (!this->socket->waitForConnected(5000)) {
this->socket->close();
this->errorMsg = this->socket->errorString();
}
}
void ConnectionHandler::connected() {
qDebug() << "connected";
this->connectedHostAddress = this->socket->peerAddress().toString();
this->connectionEstablished = true;
this->localIP = this->socket->localAddress().toString();
this->localPort = this->socket->localPort();
}
这里是服务器:
ClientHandler::ClientHandler() {
this->socket->setProtocol(QSsl::SslV3);
this->socket->setSocketOption(QAbstractSocket::KeepAliveOption, true);
}
void ClientHandler::run() {
if (!this->fd)
return;
connect(this->socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(this->socket, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::DirectConnection);
connect(this->socket, SIGNAL(encrypted()), this, SLOT(encryptedReady()));
connect(this->socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(sslErrorOccured(const QList<QSslError> &)));
connect(this->socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
connect(this->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
if (!this->socket->setSocketDescriptor(this->fd)) {
emit error(socket->error());
return;
} else {
connect(this->socket, SIGNAL(encrypted()), this, SLOT(ready()));
this->socket->startServerEncryption();
}
this->peerIP = socket->peerAddress().toString();
QString tmp;
tmp.append(QString("%1").arg(socket->peerPort()));
this->peerPort = tmp;
QHostInfo::lookupHost(this->peerIP, this, SLOT(lookedUp(QHostInfo)));
}
void ClientHandler::socketStateChanged(QAbstractSocket::SocketState state) {
qDebug() << state;
}
void ClientHandler::socketError(QAbstractSocket::SocketError) {
qDebug() << this->socket->errorString();
}
void ClientHandler::setFileDescriptor(int fd) {
this->fd = fd;
}
void ClientHandler::ready() {
qDebug() << "READY";
}
void ClientHandler::sslErrorOccured(const QList<QSslError> &) {
qDebug() << "EEROR";
}
void ClientHandler::encryptedReady() {
qDebug() << "READY";
}
我收到的客户端的输出是:
QAbstractSocket::HostLookupState
QAbstractSocket::ConnectingState
QAbstractSocket::ConnectedState
"The remote host closed the connection"
QAbstractSocket::ClosingState
QAbstractSocket::UnconnectedState
,对于服务器:
QAbstractSocket::ConnectedState
"Error during SSL handshake: error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher"
QAbstractSocket::UnconnectedState
有人知道如何解决这个问题吗?
我假设使用非加密套接字一切都很好。所以让我们只关注QSslSocket
细节的特点。如果需要,我可以共享更大的片段或工作代码。还有一个关于SSL证书的大故事,我将在这里简要介绍一下。
首先让我们检查一些外部HTTP SSL服务器上的客户端,例如:
socket->connectToHostEncrypted("gmail.com", 443);
它应该可以立即使用默认SSL协议(没有任何setProtocol()
)。在信号encrypted()
上,你可以写HTTP GET头,在readyRead()
上,回复会来。
现在尝试将socket->setProtocol(QSsl::SslV3);
设置为"gmail.com"
。预期结果:
sslErrorOccured: ("The host name did not match any of the valid hosts for this certificate")
请注意,它不是error()
信号,而是sslErrors()
信号,通知客户端可以忽略的证书问题。
因此,为了简单起见,让我们使用默认SSL协议而不使用setProtocol()
。
由于客户端处于工作状态,我们可以移动到服务器。您在SSL认证挑战的第一级被打断了。要开始通过服务器初始化SSL连接,您必须至少提供私有加密密钥和公共证书。这是你的错误:
"Error during SSL handshake: error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher"
在完美的情况下,您的证书应该由认证机构公司签名。在这种情况下,任何拥有此类根证书列表的客户端都能够检查您的证书是否有效。然而,这项服务是付费的,可能需要几周的时间。我们可以从自签名证书开始。
您可以找到各种生成证书的方法,例如:"如何创建可用于测试目的或内部使用的自签名SSL证书"
短精华:
#Step 1: Generate a Private Key
openssl genrsa -des3 -out server.key 1024
#Step 2: Generate a CSR (Certificate Signing Request)
#Common Name (eg, your name or your server's hostname) []:example.com
openssl req -new -key server.key -out server.csr
#Step 3: Remove Passphrase from Key
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key
#Step 4: Generating a Self-Signed Certificate
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
现在有server.key
和server.crt
文件。这些文件应该在startServerEncryption()
之前由服务器传递给QSslSocket
:
socket->setPrivateKey("c:/test/ssl/cert/server.key", QSsl::Rsa);
socket->setLocalCertificate("c:/test/ssl/cert/server.crt");
现在服务器可以启动SSL连接。您可以用浏览器进行测试。通常,浏览器会发出连接不受信任的警报。这是因为证书是自签名的,它不能防止"中间人"攻击。但是,您可以要求浏览器继续该连接。因此,在服务器端,您将在信号readyRead()
上拥有HTTP GET头。
尝试将Qt客户端连接到该服务器。现在这个错误由客户端抛出:
sslErrorOccured: ("The certificate is self-signed, and untrusted")
服务器在error()
中说:"远程主机关闭连接"。客户端抛出SSL证书错误,但与浏览器一样,我们可以继续该连接。将socket->ignoreSslErrors()
放到sslErrors()
信号处理程序中:
void Client::sslErrorOccured(const QList<QSslError> & error) {
qDebug() << "sslErrorOccured:" << error;
socket->ignoreSslErrors(error);
}
就是这样。当然,您的客户机不应该接受来自所有服务器的所有SSL证书错误,因为这些错误意味着连接并不真正安全,并且可能被黑客攻击。这只是为了测试。对象QSslError
包含证书数据,因此您的客户端可能只接受来自服务器的一个特定的自签名证书,而忽略所有其他此类错误。也可以创建您自己的"权威根证书",您可以手动将其写入系统。然后可以使用该证书对服务器证书进行签名。您的客户端将认为它是受信任的连接,因为它将能够通过系统根证书验证它。
注意,你也可以有一些问题与OpenSSL库。在Linux或OS X上,OpenSSL是默认设置,但是对于Windows,它应该手动安装。一些OpenSSL的编译可能已经存在于Windows系统PATH
中,例如CMake有一些有限的OpenSSL库。但是,一般来说,对于Windows,您应该将OpenSSL与应用程序一起部署。