Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Diffie Helman

4 réponses
Avatar
Guillaume Kaddouch
Bonjour,

cette fois ci je tente d'implementer une autentification par Diffie Helman
sur un programme de messagerie instantanée afin d'echanger une clé secrete
pour un algo symetrique.
Il me semble que par defaut, tout est en clair, permettant le "man in the
middle", et que le seul moyen est de rajouter une autentification par clé
publique/privée.
Mais il y a deja une notion de clé publique/privée dans Diffie Helman mais
qui ne ressemble pas du tout a la notion que j'en ai, ce sont des envois en
clair.

Voivi un peu de doc trouvé sur le net :

"A choisit un grand nombre entier aléatoire x .
A calcule X=gx mod n et l'envoie à B.

B choisit un grand nombre entier aléatoire y.

B calcule Y=gy mod n et l'envoie à A.

Ensuite, chacun de leur coté :

a.. A calcule k=Yx mod n

b.. B calcule k'=Xy mod n"
Avec

"
La clef publique correspond aux valeurs X et Y échangées par les deux
protagonistes.

La clef privée correspond aux valeurs x et y conservées par les deux
protagonistes"


Or dans le code suivant

########
LPCSTR keyInit(puinKey ptr){

Integer p2("0x12A567BC9ABCDEF1234567823BCDEF1E");
//2048 bit integer
Integer
p("0xD494AAFBCD2EAC6A36DB8E7DD4A2A64512A5BBB15B9BFB581C7C1CAFB647D4612973C37
70C2166D75EEA695F67EA8261557591DB78BCF5A886AA5294F3AEE4D25B57C8EE8C7FE8DBF70
C132CD7FFCB6F89426F807F552C5DAE2FB1F329E340094E4B30D8EF6265AB4D350E9837B151C
86AC524DE4E1FC04746C668BE318275E420D51AEDDFBDF887D435CDEEF6AC81293DB45287132
F8236A43AD8F4D6642D7CA6732DA06A1DE008259008C9D74403B68ADAC788CF8AB5BEFFC310D
CCCD32901D1F290E5B7A993D2CF6A652AF81B6DA0FD2E70678D1AE086150E41444522F206211
95AD2A1F0975652B4AF7DE5261A9FD46B9EA8B443641F3BBA695B9B020103");

AutoSeededRandomPool autorng; //génère un nbre aléatoire

ptr->dh = DH(p,p2);
byte priv1[keySize]= {0}; //key of 2048 bit
byte pub1[keySize]= {0}; //key of 2048 bit


ptr->dh.GenerateKeyPair(autorng, priv1, pub1); //génère une clé publique et
privée aléatoire //de 2048 bits
#############

DH serait le fameux X, donc la clé publique de Diffie Helman, ce qui
signifierai que "pub1" serait une autre "vrai" clé publique permettant
d'empecher le man in the middle ?
Je ne demande pas de code sources, mais juste si en theorie cela existe de
rajouter ce procédé de clef publique/privée a Diffie Helman ?

4 réponses

Avatar
pornin
According to Guillaume Kaddouch :
cette fois ci je tente d'implementer une autentification par Diffie Helman
sur un programme de messagerie instantanée afin d'echanger une clé secrete
pour un algo symetrique.


Diffie-Hellman ne procure pas d'authentification, mais un moyen
d'"échange de clé", c'est-à-dire de détermination d'un secret ephémère
commun (une clé de session, quoi).

Voilà comment ça se passe dans le Diffie-Hellman classique : il y a un
entier premier p de grande taille (genre 1024 bits ou plus) et un entier
g modulo p tel que le groupe multiplicatif engendré par g modulo p a une
taille multiple d'au moins un entier premier de grande taille (au moins
160 bits). Si vous ne savez pas ce qu'est un groupe, ce n'est pas grave :
ce qu'il faut retenir, c'est que p et g sont fixés dans le protocole,
et connus de tout un chacun. Typiquement, ils seront codés en dur dans
le logiciel.

A et B veulent déterminer un secret commun pour en tirer une clé secrète
qui servira ensuite à faire du chiffrement symétrique (par exemple).
Voilà comment ça se passe :

A choisit n entier a aléatoire (d'au moins 160 bits), calcule l'entier
Za = g^a mod p (c'est une exponentiation). A envoie Za à B.

B choisit n entier a aléatoire (d'au moins 160 bits), calcule l'entier
Zb = g^b mod p (c'est une exponentiation). B envoie Zb à A.

A connaît alors a et Zb, et calcule Zb^a mod p.

B connaît alors b et Za, et calcule Za^b mod p.

Il s'avère que ces deux derniers calculs donnent la même valeur,
à savoir g^(a*b) mod p, et que cette valeur n'est pas calculable
par quiconque ne connaissant ni a ni b (donc, entre autres, toute
personne espionnant la ligne). Cette valeur sera donc le secret
commun.


Le problème fondamental de ce système est que justement il ne contient
pas d'authentification. En cryptographie, le pouvoir c'est la
connaissance, et "B" n'est défini comme étant vraiment "B" que s'il
connaît une information secrète que seul "B" connaît "officiellement".
L'attaque "man-in-the-middle" marche ainsi : le méchant se place en
coupure sur le réseau, et se fait passer pour B auprès de A, et pour A
auprès de B. Il négocie une clé de session avec A et une autre avec B.
Ensuite, pour chaque message chiffré envoyé de A vers B, il déchiffre
le message avec la clé négociée avec A, puis le rechiffre avec la clé
négociée avec B, et envoie le message à B. Et pareil dans l'autre sens.
L'illusion est parfaite.


Donc il faut authentifier. Il y a essentiellement trois façons de faire :

1. Une fois la clé de session établie, A et B s'échangent des
messages, où B "prouve" selon un protocole d'authentification qu'il
connaît simultanément le secret qui fait de lui le B authentique,
et la clé de session qu'il a échangé avec A.

2. B signe son Zb avec un algorithme de signature quelconque, par
rapport à une clé publique que A connaît préalablement (diffusion par
annuaire, certificats...).

3. B choisit son Zb a priori, une fois pour toutes, et le diffuse en
temps que clé publique. Il faut que A connaisse ce Zb préalablement à
la tentative de connexion, et soit assuré par un moyen quelconque
qu'il s'agit bien du Zb de l'authentique B.


Ces trois solutions authentifient B auprès de A (mais pas l'inverse :
B ne sait pas à qui il parle au juste) et évitent ainsi l'attaque
"man-in-the-middle". La solution 1 est la plus compliquée, je ne
connais pas de protocole standard qui s'en serve. La solution 2 est
celle utilisée dans SSL/TLS pour les "cipher suites" de type "DH"
(Diffie-Hellmman), auquel cas le serveur possède un certificat X.509
avec son Zb dedans en tant que clé publique (note : personnellement,
je n'ai jamais vu un tel certificat). La solution 3 est celle utilisée
dans SSL/TLS pour les "cipher suites" de type "DHE" (Diffie-Hellman
Ephemeral) ; c'est typiquement le système qui sera choisi quand le
serveur SSL dispose d'un certificat qui ne permet de faire que de la
signature (un certificat avec une clé publique DSS par exemple).


Or dans le code suivant

########
LPCSTR keyInit(puinKey ptr){

Integer p2("0x12A567BC9ABCDEF1234567823BCDEF1E");
//2048 bit integer
Integer
p("0xD494AAFBCD2EAC6A36DB8E7DD4A2A64512A5BBB15B9BFB581C7C1CAFB647D4612973C37
70C2166D75EEA695F67EA8261557591DB78BCF5A886AA5294F3AEE4D25B57C8EE8C7FE8DBF70
C132CD7FFCB6F89426F807F552C5DAE2FB1F329E340094E4B30D8EF6265AB4D350E9837B151C
86AC524DE4E1FC04746C668BE318275E420D51AEDDFBDF887D435CDEEF6AC81293DB45287132
F8236A43AD8F4D6642D7CA6732DA06A1DE008259008C9D74403B68ADAC788CF8AB5BEFFC310D
CCCD32901D1F290E5B7A993D2CF6A652AF81B6DA0FD2E70678D1AE086150E41444522F206211
95AD2A1F0975652B4AF7DE5261A9FD46B9EA8B443641F3BBA695B9B020103");


Là, déja, c'est louche : p n'est pas un nombre premier (il est divisible
par 7). Sinon, en supposant que les différentes fonctions font ce que
leur nom semble impliquer, et en admettant que p et p2 soit corrigés
pour que p soit un nombre premier et p2 soit un nombre premier diviseur
de p-1, alors dans le code suivant :

AutoSeededRandomPool autorng; //génère un nbre aléatoire

ptr->dh = DH(p,p2);
byte priv1[keySize]= {0}; //key of 2048 bit
byte pub1[keySize]= {0}; //key of 2048 bit

ptr->dh.GenerateKeyPair(autorng, priv1, pub1); //génère une clé publique et
privée aléatoire //de 2048 bits


je suppose que le GenerateKeyPair() remplit priv1 avec l'exposant privé
choisi aléatoirement (le "a" pour A, dans mes notations ci-dessus) et
priv2 avec la valeur publique correspondante ("Za" pour A).

Il y a un autre truc louche : je ne vois nulle mention du "g". Pourtant,
on en a besoin pour calculer "Za". Alors soit cette implémentation de
Diffie-Hellman utilise un "g" conventionnel et suppose que "p" est
choisi pour que ce "g" fasse le boulot, soit ce code est incorrect.

Dans tous les cas, ce n'est qu'un morceau de Diffie-Hellman : il manque
encore la prise en compte du message de l'autre parti (le "Zb") et le
calcul de la clé secrète commune.


--Thomas Pornin

Avatar
pornin
According to Alain D. :
Tu n'aurais pas inversé 2 et 3?


Euh oui, en effet : DH, c'est la solution 3, DHE, c'est la solution 2.
La chaleur altère mon fonctionnement cérébral.


--Thomas Pornin

Avatar
Guillaume Kaddouch
");
//2048 bit integer
Integer

p("0xD494AAFBCD2EAC6A36DB8E7DD4A2A64512A5BBB15B9BFB581C7C1CAFB647D4612973C37



70C2166D75EEA695F67EA8261557591DB78BCF5A886AA5294F3AEE4D25B57C8EE8C7FE8DBF70



C132CD7FFCB6F89426F807F552C5DAE2FB1F329E340094E4B30D8EF6265AB4D350E9837B151C



86AC524DE4E1FC04746C668BE318275E420D51AEDDFBDF887D435CDEEF6AC81293DB45287132



F8236A43AD8F4D6642D7CA6732DA06A1DE008259008C9D74403B68ADAC788CF8AB5BEFFC310D



CCCD32901D1F290E5B7A993D2CF6A652AF81B6DA0FD2E70678D1AE086150E41444522F206211


95AD2A1F0975652B4AF7DE5261A9FD46B9EA8B443641F3BBA695B9B020103");


Là, déja, c'est louche : p n'est pas un nombre premier (il est divisible
par 7). Sinon, en supposant que les différentes fonctions font ce que
leur nom semble impliquer, et en admettant que p et p2 soit corrigés
pour que p soit un nombre premier et p2 soit un nombre premier diviseur
de p-1, alors dans le code suivant :



En fait nous analysons un plugins de chiffrement de messagerie instantanée,
et déjà d'une part nous découvrons que l'echange publique menant a la clef
secrete n'est pas autenthifié, mais en plus le nombre choisi n'est pas un
nombre premier? je m'etait déjà posé ma question mais comment voir si le
nombre est un nombre premier ou pas ? il est beaucoup trop grand pour
rentrer dans la calculatrice windows, le convertir en décimal, bon apres
avec un programme on pourrait tester les divisions par 2,3,4,....,9 et bien
vérifier qu'il est bien un nombre premier... existe t il des générateur de
nombre premier avec une taille que l'on peut choisir ? (ex 2048 bits).
Excusez moi si la solution est "évidente" je n'ai jamais été un as en maths
:-)



AutoSeededRandomPool autorng; //génère un nbre aléatoire




Question que je me posait egalement, je sais que l'alétoire est impossible
réellement en informatique, mais qu'il y a des fonctions meilleurs que
d'autres, je ne sais pas du tout ce que vaut celle ci... j'ai tres envie de
me faire la mienne.



ptr->dh = DH(p,p2);
byte priv1[keySize]= {0}; //key of 2048 bit
byte pub1[keySize]= {0}; //key of 2048 bit

ptr->dh.GenerateKeyPair(autorng, priv1, pub1); //génère une clé
publique et


privée aléatoire //de 2048 bits


je suppose que le GenerateKeyPair() remplit priv1 avec l'exposant privé
choisi aléatoirement (le "a" pour A, dans mes notations ci-dessus) et
priv2 avec la valeur publique correspondante ("Za" pour A).

Il y a un autre truc louche : je ne vois nulle mention du "g". Pourtant,
on en a besoin pour calculer "Za". Alors soit cette implémentation de
Diffie-Hellman utilise un "g" conventionnel et suppose que "p" est
choisi pour que ce "g" fasse le boulot, soit ce code est incorrect.



c'est la librairie crypto C++ 5.1 qui est utilisée, mais l'appel est peut
etre en effet éronné, c'est ce que nous cherchons a découvrir...
Je sens que si on arrive pas a autentifier et a sécuriser tout ca, il va
falloir songer a autre chose que Diffie Helman mais je ne vois pas comment
m'en sortir, etant donné que l'on m'a dit que tout système asymétrique etait
vulnérable a du "man in the middle", et je ne peut pas non plus coder en dur
une clé pour un crypyage symétrique, c'est un vrai casse tête, on dirait que
le seul moyen ce sont les certificats, et les moyens de certifications que
vous m'avez énoncés ne sont pas forcement simple a faire, mais cela semble
être l'unique solution.

Guillaume.


Avatar
pornin
According to Guillaume Kaddouch :
je m'etait déjà posé ma question mais comment voir si le nombre est un
nombre premier ou pas ?


Des tests de primalités peuvent se programmer ; par ailleurs, quand on
programme en Java, on peut utiliser la classe BigInteger qui comporte
une méthode isProbablePrime() pour tester la primalité d'un grand
nombre. Mais là, j'ai utilisé mupad, un outil de calcul symbolique
(cf http://www.mupad.de/).


existe t il des générateur de nombre premier avec une taille que l'on
peut choisir ? (ex 2048 bits).


Là encore, on peut utiliser une bibliothèque de grands nombre qui
dispose de ce genre de fonction (méthode probablePrime() de la classe
BigInteger, en Java) ou un outil en ligne de commande tel qu'OpenSSL
(http://www.openssl.org/).


Question que je me posait egalement, je sais que l'alétoire est
impossible réellement en informatique, mais qu'il y a des fonctions
meilleurs que d'autres, je ne sais pas du tout ce que vaut celle ci...
j'ai tres envie de me faire la mienne.


Fabriquer un générateur aléatoire avec de bonnes propriétés
cryptographiques (c'est-à-dire un générateur "imprédictible"), c'est
_très_ difficile à faire correctement. Vous pouvez avoir un aperçu dans
le chapitre 5 du "Handbook of Applied Cryptography", téléchargeable
gratuitement là : http://www.cacr.math.uwaterloo.ca/hac


Sinon, de façon générale, faire un protocole cryptographique censé
apporter de la sécurité, c'est quelque chose qui ne s'improvise
pas du tout. C'est un métier, ça demande beaucoup d'efforts et de
connaissances. Si vous voulez avoir quelque chose qui tienne vaguement
la route, il vous faut l'aide d'un spécialiste.


--Thomas Pornin