Hi, I would like to make a secure connection to my server, so I've tried this :
Here is the implementation of the server :
#include "fenserveur.h"
#include <iostream>
FenServeur::FenServeur()
{
serveur = new AncServeur(this);
if (!serveur
->listen
(QHostAddress::Any,
50885)) // Démarrage du serveur sur toutes les IP disponibles et sur le port 50585 {
std::cout<<"Le serveur n'a pas pu être démarré. Raison :<br />"<<serveur->errorString().toStdString();
}
else
{
// Si le serveur a été démarré correctement
std::cout<<"Le serveur a été démarré sur le port : "<<QString::number(serveur->serverPort()).toStdString()<<" Des clients peuvent maintenant se connecter."<<std::endl;
connect(serveur, SIGNAL(nouveauClient(ClientServeur*)), this, SLOT(nouvelleConnexion(ClientServeur*)));
connect(serveur,
SIGNAL(sslErreur
(const QString &)),
this,
SLOT(sslErreur
(const QString &)) );
}
}
//on affiche les erreurs ssl
void FenServeur
::sslErreur(const QString &erreur
) {
std::cout<<"erreur : "<<erreur.toStdString()<<std::endl;
}
void FenServeur::nouvelleConnexion(ClientServeur* nouveauClient)
{
envoyerATous("Un nouveau client vient de se connecter");
connect(nouveauClient, SIGNAL(readyRead()), this, SLOT(donneesRecues()));
connect(nouveauClient, SIGNAL(disconnected()), this, SLOT(deconnexionClient()));
}
void FenServeur::donneesRecues()
{
// 1 : on reçoit un paquet (ou un sous-paquet) d'un des clients
// On détermine quel client envoie le message (recherche du QTcpSocket du client)
QTcpSocket *socket
= qobject_cast<QTcpSocket
*>
(sender
());
if (socket == 0) // Si par hasard on n'a pas trouvé le client à l'origine du signal, on arrête la méthode
return;
// Si tout va bien, on continue : on récupère le message
if (tailleMessage == 0) // Si on ne connaît pas encore la taille du message, on essaie de la récupérer
{
if (socket->bytesAvailable() < (int)sizeof(quint16)) // On n'a pas reçu la taille du message en entier
return;
in >> tailleMessage; // Si on a reçu la taille du message en entier, on la récupère
}
// Si on connaît la taille du message, on vérifie si on a reçu le message en entier
if (socket->bytesAvailable() < tailleMessage) // Si on n'a pas encore tout reçu, on arrête la méthode
return;
// Si ces lignes s'exécutent, c'est qu'on a reçu tout le message : on peut le récupérer !
in >> message;
// 2 : on renvoie le message à tous les clients
envoyerATous(message);
// 3 : remise de la taille du message à 0 pour permettre la réception des futurs messages
tailleMessage = 0;
}
void FenServeur::deconnexionClient()
{
envoyerATous("Un client vient de se déconnecter");
// On détermine quel client se déconnecte
QTcpSocket *socket
= qobject_cast<QTcpSocket
*>
(sender
());
if (socket == 0) // Si par hasard on n'a pas trouvé le client à l'origine du signal, on arrête la méthode
return;
clients.removeOne(socket);
socket->deleteLater();
}
void FenServeur
::envoyerATous(const QString &message
)
{
// Préparation du paquet
out << (quint16) 0; // On écrit 0 au début du paquet pour réserver la place pour écrire la taille
out << message; // On ajoute le message à la suite
out.device()->seek(0); // On se replace au début du paquet
out << (quint16) (paquet.size() - sizeof(quint16)); // On écrase le 0 qu'on avait réservé par la longueur du message
// Envoi du paquet préparé à tous les clients connectés au serveur
for (int i = 0; i < clients.size(); i++)
{
clients[i]->write(paquet);
}
}
#include "fenserveur.h"
#include <iostream>
FenServeur::FenServeur()
{
serveur = new AncServeur(this);
if (!serveur->listen(QHostAddress::Any, 50885)) // Démarrage du serveur sur toutes les IP disponibles et sur le port 50585
{
std::cout<<"Le serveur n'a pas pu être démarré. Raison :<br />"<<serveur->errorString().toStdString();
}
else
{
// Si le serveur a été démarré correctement
std::cout<<"Le serveur a été démarré sur le port : "<<QString::number(serveur->serverPort()).toStdString()<<" Des clients peuvent maintenant se connecter."<<std::endl;
connect(serveur, SIGNAL(nouveauClient(ClientServeur*)), this, SLOT(nouvelleConnexion(ClientServeur*)));
connect(serveur, SIGNAL(sslErreur(const QString &)),this, SLOT(sslErreur(const QString &)) );
}
}
//on affiche les erreurs ssl
void FenServeur::sslErreur(const QString &erreur)
{
std::cout<<"erreur : "<<erreur.toStdString()<<std::endl;
}
void FenServeur::nouvelleConnexion(ClientServeur* nouveauClient)
{
envoyerATous("Un nouveau client vient de se connecter");
connect(nouveauClient, SIGNAL(readyRead()), this, SLOT(donneesRecues()));
connect(nouveauClient, SIGNAL(disconnected()), this, SLOT(deconnexionClient()));
}
void FenServeur::donneesRecues()
{
// 1 : on reçoit un paquet (ou un sous-paquet) d'un des clients
// On détermine quel client envoie le message (recherche du QTcpSocket du client)
QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
if (socket == 0) // Si par hasard on n'a pas trouvé le client à l'origine du signal, on arrête la méthode
return;
// Si tout va bien, on continue : on récupère le message
QDataStream in(socket);
if (tailleMessage == 0) // Si on ne connaît pas encore la taille du message, on essaie de la récupérer
{
if (socket->bytesAvailable() < (int)sizeof(quint16)) // On n'a pas reçu la taille du message en entier
return;
in >> tailleMessage; // Si on a reçu la taille du message en entier, on la récupère
}
// Si on connaît la taille du message, on vérifie si on a reçu le message en entier
if (socket->bytesAvailable() < tailleMessage) // Si on n'a pas encore tout reçu, on arrête la méthode
return;
// Si ces lignes s'exécutent, c'est qu'on a reçu tout le message : on peut le récupérer !
QString message;
in >> message;
// 2 : on renvoie le message à tous les clients
envoyerATous(message);
// 3 : remise de la taille du message à 0 pour permettre la réception des futurs messages
tailleMessage = 0;
}
void FenServeur::deconnexionClient()
{
envoyerATous("Un client vient de se déconnecter");
// On détermine quel client se déconnecte
QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
if (socket == 0) // Si par hasard on n'a pas trouvé le client à l'origine du signal, on arrête la méthode
return;
clients.removeOne(socket);
socket->deleteLater();
}
void FenServeur::envoyerATous(const QString &message)
{
// Préparation du paquet
QByteArray paquet;
QDataStream out(&paquet, QIODevice::WriteOnly);
out << (quint16) 0; // On écrit 0 au début du paquet pour réserver la place pour écrire la taille
out << message; // On ajoute le message à la suite
out.device()->seek(0); // On se replace au début du paquet
out << (quint16) (paquet.size() - sizeof(quint16)); // On écrase le 0 qu'on avait réservé par la longueur du message
// Envoi du paquet préparé à tous les clients connectés au serveur
for (int i = 0; i < clients.size(); i++)
{
clients[i]->write(paquet);
}
}
To copy to clipboard, switch view to plain text mode
And here, the implementation of my client :
#include "fenclient.h"
#include <iostream>
FenClient::FenClient()
{
//on instancie un QSslSocket
socket = new QSslSocket(this);
//on connect les différents signaux
connect(socket, SIGNAL(encrypted()), this, SLOT(ready()));
connect(socket, SIGNAL(readyRead()), this, SLOT(donneesRecues()));
connect(socket, SIGNAL(connected()), this, SLOT(connecte()));
connect(socket, SIGNAL(disconnected()), this, SLOT(deconnecte()));
//on charge la clé privé du client
QFile file( "client-key.pem" );
{
qDebug() << "la clé du client ne peut pas être chargé" <<"client-key.pem";
return;
}
QSslKey key( &file, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "****************" );
file.close();
socket->setPrivateKey( key );
//on charge le certificat du client
socket-> setLocalCertificate( "client-crt.pem" );
//on charge le certificat de notre ca
if( ! socket->addCaCertificates( "ca/ca.pem" ) )
{
qDebug() << "ouverture du certificat de la ca impossible" << "ca/ca.pem";
return;
}
//on supprime la vérification du serveur
socket->setPeerVerifyMode(QSslSocket::VerifyNone);
//on ignore les erreurs car on a un certificat auto signé
socket->ignoreSslErrors();
// On désactive les connexions précédentes s'il y en a
socket->abort();
}
void FenClient::ready() {
//on se connecte au serveur
socket->connectToHostEncrypted("127.0.0.1", 50885);
//on commence l'encryptage
socket->startClientEncryption();
envoyerMessage("Test");
}
void FenClient::envoyerMessage(const QString& message) {
// Préparation du paquet
out << (quint16) 0; // On écrit 0 au début du paquet pour réserver la place pour écrire la taille
out << message; // On ajoute le message à la suite
out.device()->seek(0); // On se replace au début du paquet
out << (quint16) (paquet.size() - sizeof(quint16)); // On écrase le 0 qu'on avait réservé par la longueur du message
socket->write(paquet);
}
// On a reçu un paquet (ou un sous-paquet)
void FenClient::donneesRecues()
{
/* Même principe que lorsque le serveur reçoit un paquet :
On essaie de récupérer la taille du message
Une fois qu'on l'a, on attend d'avoir reçu le message entier (en se basant sur la taille annoncée tailleMessage)
*/
if (tailleMessage == 0)
{
if (socket->bytesAvailable() < (int)sizeof(quint16))
return;
in >> tailleMessage;
}
if (socket->bytesAvailable() < tailleMessage)
return;
// Si on arrive jusqu'à cette ligne, on peut récupérer le message entier
in >> messageRecu;
std::cout<<messageRecu.toStdString()<<std::endl;
// On remet la taille du message à 0 pour pouvoir recevoir de futurs messages
tailleMessage = 0;
}
// Ce slot est appelé lorsque la connexion au serveur a réussi
void FenClient::connecte()
{
std::cout<<"Connection réussie!"<<std::endl;
}
// Ce slot est appelé lorsqu'on est déconnecté du serveur
void FenClient::deconnecte()
{
std::cout<<"Déconnecté du serveur"<<std::endl;
}
// Ce slot est appelé lorsqu'il y a une erreur
{
switch(erreur) // On affiche un message différent selon l'erreur qu'on nous indique
{
std::cout<<"ERREUR : le serveur n'a pas pu être trouvé. Vérifiez l'IP et le port."<<std::endl;
break;
std::cout<<"ERREUR : le serveur a refusé la connexion. Vérifiez si le programme \"serveur\" a bien été lancé. Vérifiez aussi l'IP et le port."<<std::endl;
break;
std::cout<<"ERREUR : le serveur a coupé la connexion."<<std::endl;
break;
default:
std::cout<<"ERREUR : "<<socket->errorString().toStdString()<<std::endl;
}
}
#include "fenclient.h"
#include <iostream>
FenClient::FenClient()
{
//on instancie un QSslSocket
socket = new QSslSocket(this);
//on connect les différents signaux
connect(socket, SIGNAL(encrypted()), this, SLOT(ready()));
connect(socket, SIGNAL(readyRead()), this, SLOT(donneesRecues()));
connect(socket, SIGNAL(connected()), this, SLOT(connecte()));
connect(socket, SIGNAL(disconnected()), this, SLOT(deconnecte()));
//on charge la clé privé du client
QFile file( "client-key.pem" );
if( ! file.open( QIODevice::ReadOnly ) )
{
qDebug() << "la clé du client ne peut pas être chargé" <<"client-key.pem";
return;
}
QSslKey key( &file, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "****************" );
file.close();
socket->setPrivateKey( key );
//on charge le certificat du client
socket-> setLocalCertificate( "client-crt.pem" );
//on charge le certificat de notre ca
if( ! socket->addCaCertificates( "ca/ca.pem" ) )
{
qDebug() << "ouverture du certificat de la ca impossible" << "ca/ca.pem";
return;
}
//on supprime la vérification du serveur
socket->setPeerVerifyMode(QSslSocket::VerifyNone);
//on ignore les erreurs car on a un certificat auto signé
socket->ignoreSslErrors();
// On désactive les connexions précédentes s'il y en a
socket->abort();
}
void FenClient::ready() {
//on se connecte au serveur
socket->connectToHostEncrypted("127.0.0.1", 50885);
//on commence l'encryptage
socket->startClientEncryption();
envoyerMessage("Test");
}
void FenClient::envoyerMessage(const QString& message) {
// Préparation du paquet
QByteArray paquet;
QDataStream out(&paquet, QIODevice::WriteOnly);
out << (quint16) 0; // On écrit 0 au début du paquet pour réserver la place pour écrire la taille
out << message; // On ajoute le message à la suite
out.device()->seek(0); // On se replace au début du paquet
out << (quint16) (paquet.size() - sizeof(quint16)); // On écrase le 0 qu'on avait réservé par la longueur du message
socket->write(paquet);
}
// On a reçu un paquet (ou un sous-paquet)
void FenClient::donneesRecues()
{
/* Même principe que lorsque le serveur reçoit un paquet :
On essaie de récupérer la taille du message
Une fois qu'on l'a, on attend d'avoir reçu le message entier (en se basant sur la taille annoncée tailleMessage)
*/
QDataStream in(socket);
if (tailleMessage == 0)
{
if (socket->bytesAvailable() < (int)sizeof(quint16))
return;
in >> tailleMessage;
}
if (socket->bytesAvailable() < tailleMessage)
return;
// Si on arrive jusqu'à cette ligne, on peut récupérer le message entier
QString messageRecu;
in >> messageRecu;
std::cout<<messageRecu.toStdString()<<std::endl;
// On remet la taille du message à 0 pour pouvoir recevoir de futurs messages
tailleMessage = 0;
}
// Ce slot est appelé lorsque la connexion au serveur a réussi
void FenClient::connecte()
{
std::cout<<"Connection réussie!"<<std::endl;
}
// Ce slot est appelé lorsqu'on est déconnecté du serveur
void FenClient::deconnecte()
{
std::cout<<"Déconnecté du serveur"<<std::endl;
}
// Ce slot est appelé lorsqu'il y a une erreur
void FenClient::erreurSocket(QAbstractSocket::SocketError erreur)
{
switch(erreur) // On affiche un message différent selon l'erreur qu'on nous indique
{
case QAbstractSocket::HostNotFoundError:
std::cout<<"ERREUR : le serveur n'a pas pu être trouvé. Vérifiez l'IP et le port."<<std::endl;
break;
case QAbstractSocket::ConnectionRefusedError:
std::cout<<"ERREUR : le serveur a refusé la connexion. Vérifiez si le programme \"serveur\" a bien été lancé. Vérifiez aussi l'IP et le port."<<std::endl;
break;
case QAbstractSocket::RemoteHostClosedError:
std::cout<<"ERREUR : le serveur a coupé la connexion."<<std::endl;
break;
default:
std::cout<<"ERREUR : "<<socket->errorString().toStdString()<<std::endl;
}
}
To copy to clipboard, switch view to plain text mode
The server start and listen to the port, but when I want to connect the client, it fails and I get this error message :
QSslSocket : cannot resolve SSLv2_client_method
QSslSocket : cannot resolve SSLv2_server_method
Thanks for your help.
PS : My os is unbuntu 14.04
Bookmarks