Bonjour à tous,
Je développe un jeu. Le serveur est en Java et le client en C++. J’aimerais passer à SSL, mais je n’arrive pas à le faire correctement jusqu’au bout.
En suivant quelques ressources difficilement trouvées sur le net, j’ai réussi à faire en sorte que ça marche… presque.
La connexion sur le serveur est bien établie, la communication est bien chiffrée, mais ça pêche du côté de la vérification du certificat. Pour le moment, sur le client, je désactive la vérification du certificat provenant du serveur. Si je l’active ça ne fonctionne pas, le client refuse de se connecter. L’objectif c’est bien sûr que le client accepte le certificat du serveur… car sans cela, je perds évidemment une bonne partie de la sécurité.
Code d’initialisation d’OpenSSL:
1 2 3 4 5 6 7 | SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); SSL_METHOD* method = TLSv1_2_client_method(); ctx = SSL_CTX_new(method); //SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, 0); //C'est cette ligne qui permet d'activer la vérification du certificat provenant du serveur SSL_CTX_load_verify_locations(ctx, (APPDIR + "cert.pem").c_str(), NULL); |
Une partie du code de connexion au serveur:
1 2 3 4 5 6 7 8 9 10 11 12 13 | ssl = SSL_new(ctx); SSL_set_fd(ssl, sock); // sock c'est un handle sur le socket TCP, déjà connecté à ce moment-là int sslre = SSL_connect(ssl); int sslvr = SSL_get_verify_result(ssl); X509* cert = SSL_get_peer_certificate(ssl); if (!cert) debug << "No certificate returned by the server !" << endl; else X509_free(cert); debug << "SSL_connect returned " << sslre << endl; debug << "Last error = " << GetLastError() << endl; debug << "SSL_get_error = " << SSL_get_error(ssl, sslre) << endl; debug << "SSL_get_verify_result = " << sslvr << endl; debug << "SSL_state_string = " << SSL_state_string(ssl) << endl; debug << "SSL_state_string_long = " << SSL_state_string_long(ssl) << endl; |
Si j’active la vérification, le message "No certificate returned by the server !" s’affiche, et je reçois systématiquement une erreur 20 de la fonction SSL_get_verify_result. La doc que j’ai bien eu du mal à trouver m’en dit ceci:
20 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: unable to get local issuer certificate the issuer certificate could not be found: this occurs if the issuer certificate of an untrusted certificate cannot be found.
D’après ce que j’en comprends, il ne retrouve pas un certificat auquel il peut faire confiance dans ce que lui envoie le serveur. J’ai un certificat Letsencrypt installé sur le serveur.
A partir de là je ne comprends pas trop ce que je dois faire pour que ça marche…
Première question: Est-ce que le certificat renvoyé par le serveur est vraiment correct ? Comment est-ce que je peux le vérifier ?
Deuxième question: puis-je faire en sorte qu’il n’y ait pas besoin de mettre à jour le client tous les trois mois à cause du certificat (durée de validité d’un certificat Letsencrypt) ? Ca serait vraiment une tarre, même avec une mise à jour automatique il y a pas mal de joueurs qui ne savent pas comment faire, qui ont du mal de le faire, ou qui ont peur. En plus très peu de monde savent ce qu’est SSL et ce que ça apporte… en bref les joueurs ne sont pas nécessairement versés dans la technique (j’ai des joueurs qui ont passé 70 ans, si si je vous assure). Je tiens quand même à tout passer sur SSL, c’est fondamentalement une bonne chose, même si à la base c’est parce que 1/Google met la pression, et 2/un bon groupe de joueurs l’ont aussi demandé.
Troisième question: Je ne comprends pas trop ce que je dois lui donner comme fichier dans SSL_CTX_load_verify_locations. J’ai essayé plusieurs choses qui soit ne changent rien (toujours l’erreur 20), ou soit renvoie l’erreur 2 au lieu de 20 dans SSL_get_verify_result:
L’erreur 2 dit:
2 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: unable to get issuer certificate the issuer certificate of a looked up certificate could not be found. This normally means the list of trusted certificates is not complete.
Visiblement les fichiers qui donnent cette erreur 2 ne sont pas les bons, encore moins. Voici tous ceux que j’ai essayés jusqu’à présent sans succès:
- Le fichier cert.pem généré lors de l’enregistrement chez Letsencrypt, présent sur le serveur dans /etc/letsencrypt/live/domaine.tld/
- chain.pem aussi généré au même endroit
- fullchain.pem aussi généré à la même place
- un fichier all.pem qui est la concaténation des trois précédents (cat cert.pem chain.pem fullchain.pem > all.pem)
- ISRG Root X1 de https://letsencrypt.org/certificates/
- X3 cross signed du même site
- Authority X3 du même site
Je n’ai pas essayé privkey.pem mais c’est celui qui contient la clé privée, donc à ne surtout pas distribuer non ?
Bien entendu, je suis certain que le fichier que je lui donne dans SSL_CTX_load_verify_locations est bien lu (la fonction renvoie 1 en cas de succès, si le fichier n’existe pas elle renvoie 0).
Bien évidemment je ne suis pas fou, je n’ai pas compilé OpenSSL moi-même. J’ai voulu essayer au début, mais j’ai vite abandonné, je ne comprends rien. Heureusement on trouve des binaires assez facilement, par exemple ici (je ne me souviens plus si c’était là que je la’vais pris) J’ai la version 1.1.0c, ce n’est pas la toute dernière, ce n’est pas bien au niveau de la sécurité, mais pour le code ça ne change sûrement pas grand chose.
Comme vous aurez pu le constater, le serveur tourne sous linux, et le client est exclusivement prévu pour windows (pas de version multiplateforme prévue).
Du côté du serveur qui est en Java, je ne pense pas être faux. J’ai trouvé un script qui permet de convertir les .pem de Letsencrypt et la clé privée dans le format keystore accepté par Java. Ensuite, voici un bout de code permettant de créer le socket d’écoute, simplifié au maximum:
1 2 3 4 5 | System.setProperty("javax.net.ssl.keyStore", Config.get("keystore")); System.setProperty("javax.net.ssl.keyStorePassword", Config.get("keystorePassword")); server = SSLServerSocketFactory .getDefault() .createServerSocket(); server.bind(new InetSocketAddress(host, port), backlog); while(true) newConnection(server.accept()); |
Aucun moyen de savoir si le fichier keystore est bien pris en compte, mais comme si je n’en mets pas on ne peut pas se connecter même sans la vérification côté client, j’en conclus que ça doit être bon.
Merci pour l’aide que vous pourrez apporter !