2015-10-18 737 views
3

我有一個與QTcpSocket一起工作的客戶端 - 服務器應用程序。現在我想使用加密的SSL連接,因此我試圖切換到QSslSocket。但是我無法建立與服務器的連接。 這裏是爲客戶端的代碼:Qt與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 

有誰知道如何解決這個問題?

回答

4

我認爲使用非加密套接字一切都很好。所以我們只關注與QSslSocket詳細說明的特性。如果需要,我可以分享更大的作品或工作代碼。這裏還有一個關於SSL證書的大故事,我會在這裏簡單介紹一下。

首先讓我們檢查你的客戶一些外部SSL的HTTP服務器上,例如:

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" 

在完美的情況下,你的證書應該由認證授權公司簽名。在這種情況下,任何擁有此類根證書列表的客戶端都能夠檢查您的證書是否有效。但是,該服務已付款,可能需要幾周時間。我們可以從自簽名證書開始。

你可以找到各種食譜生成證書,例如:"How to create a self-signed SSL Certificate which can be used for testing purposes or internal usage"

簡短摘錄:

#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.keyserver.crt文件。這些文件應該由服務器QSslSocketstartServerEncryption()前通過:

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與您的應用程序一起部署。

+0

正如所描述的,thx如此多的一切現在工作! – wasp256