Communication réseau chiffrée avec SSL

Le problème exposé dans ce sujet a été résolu.

Bonsoir,

Je cherche à créer un logiciel devant communiquer sur le réseau local. J’ai choisi pour cela Python, et du chiffrement SSL. Mais je suis un grand débutant, et je bloque sur un point.

J’ai réussi à faire fonctionner ensemble deux scripts (client et serveur): le client envoie un message au serveur, qui l’affiche. Cependant je ne parviens pas à faire en sorte que le client puisse envoyer plusieurs messages: il faut que je le relance pour qu’un autre message puisse être envoyé. Dans l’exemple ci-dessous, "coucou" n’est pas reçu par le serveur.

Voici le code du client et du serveur. J’utilise pour ce test un certificat autosigné, qui est également utilisé pour se vérifier lui-même…

Client:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import socket, pickle, pprint, ssl
host="192.168.0.2"
def client():
    context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    context.verify_mode = ssl.CERT_REQUIRED
    context.check_hostname = False
    context.load_verify_locations("./ssl_cert.pem")

    print("context ok")

    conn = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname=host)
    print("socket ok")

    conn.connect((host, 12345))
    print("connection ok")

    cert = conn.getpeercert()
    pprint.pprint(cert)

    conn.sendall(pickle.dumps("Hello how are you?",0))
    conn.sendall(pickle.dumps("coucou",0))

client()

Serveur:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import socket, ssl, pickle

def debug(text):
    print(text)

def deal_with_client(connstream):
    print("deal_with_client")
    data = connstream.recv(1024)
    # empty data means the client is finished with us
    while data:
        if not do_something(connstream, data):
            # we'll assume do_something returns False
            # when we're finished with client
            break
        data = connstream.recv(1024)
    # finished with client

def do_something(stream, data):
    print(pickle.loads(data))

def server():
    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    context.load_cert_chain(certfile="ssl_cert.pem", keyfile="ssl_key.pem")

    bindsocket = socket.socket()
    bindsocket.bind(('0.0.0.0', 12345))
    bindsocket.listen(5)

    debug("bindsocket")
    while True:
        print ("boucle")
        newsocket, fromaddr = bindsocket.accept()
        try:
            connstream = context.wrap_socket(newsocket, server_side=True)

            try:
                print ("attente?")
                deal_with_client(connstream)
                #print ("on ferme la connection...")
                #connstream.close() 
            finally:
                connstream.shutdown(socket.SHUT_RDWR)
                connstream.close()        
        except:
            print("connexion échouée")

server()

Je me suis basé sur la documentation Python officielle, mais dans les exemples ils n’envoient qu’un seul message.

J’espère que vous pourrez m’aider! Merci!

L’algo de ton serveur ne va pas : dans ta boucle, tu attends une connexion client, tu la traites et tu attends à nouveau une connexion. Si tu ne gères qu’une seule connexion, gère le connect hors de ta boucle. Dans le cas contraire, passe par un select.

Merci pour la réponse.

En fait j’ai trouvé le problème ailleurs: la fonction do_something renvoyait toujours False et donc le programme n’attendait plus de données. J’ai arrangé cela et j’ai pu ensuite gérer proprement la connexion et la déconnexion du client!

Je posterai le code fonctionnel, au cas où ça pourrait servir à quelqu’un ;)

Voici le code qui a l’air de fonctionner. Si quelqu’un s’y connaît: est ce que c’est correct la façon de vérifier les certificat?

J’aimerai bien, si le certificat n’est pas reconnu, demander à l’utilisateur si il doit l’accepter. Savez-vous comment faire ça?

client:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import socket, pickle, pprint, ssl, sys

host="server.local"
port=12345

def client():

    #Contexte personnalisé
    context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    # Certificat requis
    context.verify_mode = ssl.CERT_REQUIRED
    # On ne vérifie pas le domaine (réseau local)
    context.check_hostname = False
    # Utiliser le certificat du serveur pour vérifier la connexion
    context.load_verify_locations("./cert/server.crt")

    # Charger certificat et clé du client, pour s'authentifier
    context.load_cert_chain(certfile="cert/client.crt", keyfile="cert/client.key")

    print("context ok")

    conn = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname=host)
    print("socket ok")

    try:
        conn.connect((host, port))
    except:
        print("Connexion impossible")
        sys.exit()
    print("connection ok")

    cert = conn.getpeercert()
    pprint.pprint(cert)

    # Message personnalisé
    message="1"
    while message!="fin" and message!="":
        message=input("Message: ")
        conn.sendall(pickle.dumps(message,0))

    print("Fin du programme")

client()

serveur:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import socket, ssl, pickle, sys
# défaut: accepter les connexions de n'importe quelle IP
host="0.0.0.0"
port=12345

def debug(text):
    print(text)

def reception(connstream):
    debug("reception")
    data = connstream.recv(1024)
    # data vide signifie que le client a fini
    while data:
        if not afficher(connstream, data):
            # si afficher retourne False, on se déconnecte
            debug("le client se déconnecte")
            break
        try:
            # On réceptionne les données
            data = connstream.recv(1024)
        except:
            debug("réception arrêtée")
            break

def afficher(stream, data):
    if (pickle.loads(data)=="fin"):
        return False # Si on reçoit le message "fin", on se déconnecte
    else:
        print("   ## Message : " + pickle.loads(data)) # On affiche le message
        return True

def server():
    #Contexte personnalisé
    context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    # Certificat requis
    context.verify_mode = ssl.CERT_REQUIRED
    # On ne vérifie pas le domaine (réseau local)
    context.check_hostname = False
    # On vérifie le certificat du client avec celui qu'on a enregistré
    context.load_verify_locations("./cert/client.crt")

    # Charger certificat et clé du serveur, pour s'authentifier
    context.load_cert_chain(certfile="cert/server.crt", keyfile="cert/server.key")


    bindsocket = socket.socket()
    bindsocket.bind((host, port))
    bindsocket.listen(5)

    debug("bindsocket")


    while True:
        debug("Attente nouvelle connexion...")
        newsocket, fromaddr = bindsocket.accept()
        try:
            connstream = context.wrap_socket(newsocket, server_side=True)

            debug ("Connexion entrante")
            try:
                print ("...")
                reception(connstream)
            finally:
                connstream.shutdown(socket.SHUT_RDWR)
                connstream.close()
                debug("Fermeture de la connexion")

        except:
            debug("Connexion échouée")

server()
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte