2015-10-18 1 views
3

J'ai une application client-serveur qui fonctionnait avec QTcpSocket. Maintenant, je voudrais utiliser une connexion SSL cryptée à la place, donc j'ai essayé de passer à QSslSocket. Mais je ne peux pas établir une connexion au serveur. Voici le code pour le client:Qt avec QSslSocket ne se connecte pas correctement

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(); 
} 

Voici celui du serveur:

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"; 

} 

La sortie pour le client que je reçois est:

QAbstractSocket::HostLookupState 
    QAbstractSocket::ConnectingState 
    QAbstractSocket::ConnectedState 
    "The remote host closed the connection" 
    QAbstractSocket::ClosingState 
    QAbstractSocket::UnconnectedState 

et pour le serveur :

QAbstractSocket::ConnectedState 
    "Error during SSL handshake: error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher" 
    QAbstractSocket::UnconnectedState 

Est-ce que quelqu'un sait comment résoudre ce problème?

Répondre

4

Je suppose qu'avec des sockets non cryptés tout va bien. Alors concentrons-nous seulement sur les particularités de détailler avec QSslSocket. Je peux partager des pièces plus grandes ou du code de travail si nécessaire. Il y a aussi une grande histoire concernant les certificats SSL que je vais aborder brièvement ici.

Pour commencer, nous allons vérifier que vous client sur certains serveurs SSL HTTP externes, par exemple:

socket->connectToHostEncrypted("gmail.com", 443); 

Il devrait fonctionner immédiatement avec le protocole SSL par défaut (sans setProtocol()). Sur le signal encrypted() vous pouvez écrire en-tête HTTP GET et sur la readyRead() la réponse viendra.

Maintenant, essayez de définir socket->setProtocol(QSsl::SslV3); pour "gmail.com". Résultat attendu:

sslErrorOccured: ("The host name did not match any of the valid hosts for this certificate") 

Notez que ce n'est pas le signal error() mais la notification du signal sslErrors() sur les questions de certificat qui peuvent être ignorés par le client. Par conséquent, pour simplifier, travaillons avec le protocole SSL par défaut sans utiliser setProtocol(). Comme le client est en état de marche, nous pouvons passer au serveur. Vous avez été interrompu sur le premier niveau de défi de certification SSL. Pour démarrer l'initialisation de la connexion SSL par le serveur, vous devez fournir au moins une clé de chiffrement privée et un certificat public. C'est votre erreur:

"Error during SSL handshake: error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher" 

Dans le cas parfait votre certificat devrait être signé par une compagnie d'autorité d'authentification. Dans ce cas, tout client disposant d'une liste de tels certificats racine est capable de vérifier que votre certificat est valide. Cependant, ce service est payé et cela peut prendre quelques semaines. Nous pouvons commencer avec un certificat auto-signé.

Vous pouvez trouver différentes recettes pour générer des certificats, par exemple: "How to create a self-signed SSL Certificate which can be used for testing purposes or internal usage"

court extrait:

#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 

Maintenant il y a des fichiers server.key et server.crt.Ces fichiers doivent être transmis par le serveur à QSslSocket avant startServerEncryption():

socket->setPrivateKey("c:/test/ssl/cert/server.key", QSsl::Rsa); 
    socket->setLocalCertificate("c:/test/ssl/cert/server.crt"); 

Maintenant, le serveur peut démarrer la connexion SSL. Vous pouvez le tester avec un navigateur. Typiquement, le navigateur déclenchera l'alarme que la connexion n'est pas fiable. C'est parce que le certificat est auto-signé et qu'il ne peut pas protéger de l'attaque "man in the middle". Cependant, vous pouvez demander à votre navigateur de continuer avec cette connexion. Ainsi, du côté serveur, vous aurez l'en-tête HTTP GET sur le signal readyRead(). Essayez de vous connecter avec le client Qt à ce serveur. Maintenant, l'erreur est soulevée par le client:

sslErrorOccured: ("The certificate is self-signed, and untrusted") 

Le serveur dit dans error(): « L'hôte distant a fermé la connexion ». Le client a soulevé l'erreur de certificat SSL, mais comme avec les navigateurs, nous pouvons continuer avec cette connexion. Mettez socket->ignoreSslErrors() dans le gestionnaire de signaux sslErrors():

void Client::sslErrorOccured(const QList<QSslError> & error) { 
    qDebug() << "sslErrorOccured:" << error; 
    socket->ignoreSslErrors(error); 
} 

Voilà. Bien sûr, votre client ne doit pas accepter toutes les erreurs de certificat SSL de tous les serveurs, car de telles erreurs signifient que la connexion n'est pas vraiment sécurisée et peut être piratée. C'est juste pour tester. L'objet QSslError contient les données de certificat, il est donc possible que votre client n'accepte qu'un seul certificat auto-signé spécifique de votre serveur et ignore toutes les autres erreurs de ce type. Il est également possible de créer votre propre «certificat racine d'autorité» que vous pouvez écrire manuellement sur votre système. Ensuite, ce certificat peut être utilisé pour signer votre certificat de serveur. Votre client pensera que c'est une connexion de confiance, car il sera capable de la valider par les certificats racine du système.

Notez que vous pouvez également rencontrer des problèmes avec la bibliothèque OpenSSL. Sous Linux ou OS X OpenSSL est configuré par défaut, mais pour Windows, il doit être installé manuellement. Certaines compilation d'OpenSSL peuvent déjà être présentes dans le système Windows PATH, par exemple CMake a une bibliothèque OpenSSL limitée. Cependant, en général pour Windows, vous devez déployer OpenSSL avec votre application.

+0

Exactement comme décrit, tant que tout fonctionne maintenant !! – wasp256