_ _____________________________________ _ -6- `^°*;:,.>Securite et cartes bleues<.,:;*°^` _____________________/¯¯¯¯¯¯¯¯¯¯maddany¯¯¯¯¯¯¯¯¯¯\_____________________ ¤º°`°º¤ø,¸¸,ø¤º°`°º¤ø,¸¸,ø¤º°`°º¤ø,¸¸,ø¤º°`°º¤º°`°º¤ø,¸¸,ø¤º°`°º¤ø,¸¸,ø¤º°`°º¤ø, 5/6/2002 SECURITE ET CARTES BLEUES par maddany@madchat.org et crilu I. INTRODUCTION ET DISCLAIMER II. INTRODUCTION AU RSA III. PRELIMINAIRES MATHEMATIQUES IV. APPLICATION A LA CARTE BLEUE APPENDICE I. PROGRAMMES D'EXEMPLE EN JAVA APPENDICE II. SOURCES ET BIBLIOGRAPHIE I. INTRODUCTION ET DISCLAIMER Bon, on va commencer par décourager les SK : cet article ne traite pas directement des yescards et ne devrait pas vous servir à grand-chose si ce que vous cherchez c'est comment faire des yescards. Le but de cet article est essentiellement de donner un bon background mathématique qui permette de comprendre le cryptage avec le RSA, et son application à la sécurité de la carte bancaire. Le côté pratique sera délaissé pour donner une compréhension en profondeur de l'origine de la faille (mise en évidence par Humpich), donc si vous voulez savoir qu'est-ce que c'est que cette faille Humpich et comment elle marche, ce texte est fait pour vous. Les démonstrations mathématiques de la partie III sont de niveau maths spé mais n'utilisent que des outils de maths sup (comprendre que c'est de niveau maths sup mais que ce n'est pas au programme de la sup), et je pense qu'elles sont accessibles à un bon élève de terminale S avec un peu de recherche (et une petite culture des programmes mathématiques du supérieur, notamment en algèbre). Ces démonstrations ne sont pas indispensables à la compréhension de l'article, elles sont surtout là pour les étudiants qui cherchent à aller au fond des choses ou juste pour les matheux curieux de savoir le fondement du RSA. II. INTRODUCTION AU RSA Le RSA est un système de cryptage asymétrique basé sur un résultat d'algèbre que nous démontrerons dans la partie III (on dit qu'un cryptage est asymétrique quand on n'utilise pas la même clé pour coder et décoder les données, on parle également de cryptage à clé publique/privée; l'autre type de cryptage est le cryptage symétrique ou à clé secrète). Dans la théorie, le RSA fonctionne ainsi : on prend p et q deux nombres premiers au hasard (on rappelle que les nombres premiers) on pose n = p*q on prend un entier e m = c^d mod n et c = m^d mod n => m = c^e mod n Théorème de Lagrange : Si G est un groupe fini et H un sous-groupe de G, alors l'ordre de H divise l'ordre de G (l'ordre d'un groupe est égal à son cardinal) (pour avoir la définition et quelques propriétés sur les groupes, je vous conseille de chercher un site ou un livre sur l'algèbre de 1er cycle (les-mathematiques.net ou le HPrépaMath Algèbre peuvent vous être utiles)) Preuve : soit G un groupe fini et H un sous-groupe de G. On définit la relation R ainsi : Pour tout (x,y) élt de G², on a xRy <=> x'*y élt de H où x' est l'inverse de x cette relation est une relation d'équivalence (cela se montre aisément) si on note xH l'ensemble { x*h | x élt de H } (classe à gauche de x), alors il est direct que la classe d'équivalence de x pour la relation R est xH. les classes d'équivalences sur G ainsi définies ont toutes le même cardinal, car la fonction f qui à tout élt h de H associe l'élt x*h de xH est une bijection, Donc pour tout x de G on a card(xH) = card(H) On en vient au théorème de Lagrange à proprement parler : on sait qu'il existe un entier naturel n et x_1,...,x_n n élts de G tels que { x_iH | i élt de {1, ..., n} } soit une partition de G, or les ensembles x_iH ont tous le même nombre d'éléments card(H), donc on récupère card(G) = n*card(H), ce qui revient à dire que l'ordre de H divise l'ordre de G, CQFD note : selon la légende, le petit Lagrange aurait démontré ce théorème à 10 ans devants les élèves de l'école polytechnique. C'est énervant hein ;) Corollaire du théorème de Lagrange : Si G est un groupe et g un élt de G d'ordre fini alors l'ordre de g divise l'ordre de G Preuve : posons n = ordre(g) (càd n est le plus petit entier naturel tel que g^n = e où e est le neutre pour *) L'ensemble { e, g, ..., g^(n-1) } est un sous groupe de G d'ordre n (les savants l'appellent le groupe engendré par g :), donc d'après le théorème de Lagrange n divise l'ordre de G. Prop : a inversible dans Zn <=> pgcd(a,n) = 1 Preuve : se démontre à l'aide de l'ami Bezout : un sens est trivial, l'autre l'est moins (life is unfair) On travaille maintenant dans Z/nZ, que l'on va noter Zn pour économiser votre bande passante (petit rappel pour les idiots, les enfants et les femmes, pour n entier naturel non nul, Zn = { k élt de N | k <= n }) Notons phi la fonction indicatrice d'Euler définie ainsi : pour tout n élt de N*, phi(n) = card({ a élt de N | pgcd(a,n) = 1 }) = card({ a élt de Zn | a inversible dans Zn }) Théorème d'Euler : pour tout a inversible dans Zn, on a : a^phi(n) = 1 mod n Preuve : notons A l'ensemble des éléments inversibles de Zn, on a donc phi(n) = card(A) et (A,*) est un groupe posons p = ordre(A) Soit a élt de A, on sait que ordre(a) divise ordre(A), donc il existe k élt de N tel que p = k*ordre(a) On a donc a^phi(n) = a^p = a^(k*ordre(a)) = (a^ordre(a))^k = 1^k = 1 mod n (attention, on travaille dans Zn donc à chaque fois on applique mod n au résultat d'un calcul) Théorème chinois : si p et q sont deux nombres premiers, alors Zpq et Zp*Zq sont isomorphes preuve : on montre aisément que la fonction f qui à tout élément n de Zpq associe (n mod p, n mod q) est un isomorphisme Extension du théorème d'Euler aux éléments non inversibles de Zn : pour tout x de Zn et pour tout k de N on a x^(1+k*phi(n)) = x preuve : soient p et q deux nombres premiers, et soit n = p*q Soit x élt de Zn, et f la fonction définie dans la preuve du théorème chinois. Si x est inversible dans Zn, alors la propriété découle directement du théorème d'Euler. Supposons maintenant que x n'est pas inversible dans Zn, on a alors pgcd(x,n) != 1 donc il existe d tel que d|x et d|n (si on note | la relation "divise"), donc il existe d tel que d|x et (d|p ou d|q) or p et q premiers donc il existe d tel que d|x et (d=p ou d=q) donc p|x ou q|x de manière exclusive (car si on avait p|x et q|x on aurait x>=pq càd x>=n, ce qui est contradictoire avec la définition de Zn) pour fixer les idées, supposons que q|x, donc x = a*q on a alors f(x)^(1+k*phi(n)) = (x^(1+k*phi(n)) mod p, x^(1+k*phi(n)) mod q) = (x*(x^phi(n))^k mod p, 0) = (x mod p, 0) = f(x) notons f' la fonction réciproque de f, on a alors : f'(f(x)^(1+k*phi(n))) = f'(f(x)) = x et d'autre part, comme f' est un morphisme : f'(f(x)^(1+k*phi(n))) = f'(f(x))^(1+k*phi(n)) = x^(1+k*phi(n)), CQFD Application au RSA : Placons nous dans le cadre du RSA : soient p et q deux nombres premiers, n = p*q soit e un nombre entier strictement inférieur à n soit d un nombre entier tel que e*d = 1 mod (p-1)*(q-1) Observons que phi(n) = phi(p*q) = phi(p)*phi(q) = (p-1)*(q-1) car p et q sont premiers (relisez la définition de la fonction phi si cela ne vous semble pas immédiat) soit m un entier strictement inférieur à n, posons c = m^e mod n et A = c^d mod n il s'agit de vérifier que A = m : A = c^d mod n = (m^e)^d mod n = m^(e*d) mod n = m^(1+k*phi(n)) mod n = m mod n = m car m nombre et nombre -> texte, pensez également que les nombres que vous cryptez doivent être plus petits que n. Si votre texte transformé en nombre est plus grand que n, une solution possible est de décomposer ce nombre en base n et de crypter séparément chacune de ses composantes, je vous laisse vous débrouiller pour retrouver le texte original :) /**********BEGIN RSAEngine.java************/ import java.math.*; import java.util.*; public class RSAEngine { Random r; public final static int PRIME_BITS = 1024; public RSAEngine() { r = new Random(); } public static void main(String[] arg) { RSAEngine engine = new RSAEngine(); System.out.println("generating key pair..."); RSAKeyPair keypair = engine.generateKeyPair(); System.out.println("n = "+keypair.getProduct().toString()); System.out.println("d = "+keypair.getPrivateExponent().toString()); System.out.println("e = "+keypair.getPublicExponent().toString()); System.out.println("key pair generated. Encoding your input"); BigInteger m = new BigInteger(arg[0]); BigInteger c = m.modPow(keypair.getPublicExponent(),keypair.getProduct()); System.out.println("ciphertext = "+c.toString()); BigInteger clear = c.modPow(keypair.getPrivateExponent(),keypair.getProduct()); System.out.println("decoded text : "+clear.toString()); } public RSAKeyPair generateKeyPair() { BigInteger p = generateBigPrime(PRIME_BITS); System.out.println("p = "+p); Thread sleeper = new Thread(); sleeper.start(); try { sleeper.sleep(1000); } catch(InterruptedException e) {} BigInteger q = generateBigPrime(PRIME_BITS); System.out.println("q = "+q); BigInteger n = p.multiply(q); BigInteger e = generatePublicExponent(p,q,n); BigInteger d = generatePrivateExponent(p,q,e); return new RSAKeyPair(new RSAPrivateKey(n,d), new RSAPublicKey(n,e)); } public static BigInteger generateBigPrime(int numbits) { BigInteger big; Random ran = new Random(); while(true) { big = new BigInteger(numbits, ran); if(big.isProbablePrime(7) && big.isProbablePrime(20)) break; } return big; } public BigInteger generatePublicExponent(BigInteger p, BigInteger q, BigInteger n) { BigInteger e; BigInteger prod = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE)); while(true) { e = new BigInteger(8, r); if(e.compareTo(n)<0 && gcd(e, prod).equals(BigInteger.ONE)) break; } return e; } public BigInteger generatePrivateExponent(BigInteger p, BigInteger q, BigInteger e) { BigInteger d; BigInteger prod = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE)); System.out.println("prod = "+prod); BigInteger i = BigInteger.ONE; while(true) { d = i.multiply(prod).add(BigInteger.ONE); BigInteger[] array = d.divideAndRemainder(e); d = array[0]; if(array[1].equals(BigInteger.ZERO)) break; else i = i.add(BigInteger.ONE); } return d; } public static BigInteger gcd(BigInteger a, BigInteger b) { return b.equals(BigInteger.ZERO)?a:((a.max(b)).equals(a)?gcd(b,a.mod(b)):gcd(b,a)); } } /***********END RSAEngine.java*************/ /**********BEGIN RSAKeyPair.java************/ import java.math.*; public class RSAKeyPair { RSAPrivateKey prv; RSAPublicKey pub; public RSAKeyPair(RSAPrivateKey prv, RSAPublicKey pub) { this.prv = prv; this.pub = pub; if(!pub.getProduct().equals(prv.getProduct())) throw new IllegalArgumentException("invalid key pair : different n values"); } public RSAPrivateKey getPrivateKey() { return prv; } public RSAPublicKey getPublicKey() { return pub; } public BigInteger getPublicExponent() { return pub.getPublicExponent(); } public BigInteger getProduct() { return pub.getProduct(); } public BigInteger getPrivateExponent() { return prv.getPrivateExponent(); } public void setPublicExponent(BigInteger e) { pub.setPublicExponent(e); } public void setProduct(BigInteger n) { pub.setProduct(n); } public void setPrivateExponent(BigInteger d) { prv.setPrivateExponent(d); } } /***********END RSAKeyPair.java*************/ /**********BEGIN RSAKey.java************/ public class RSAKey {} /***********END RSAKey.java*************/ /**********BEGIN RSAPrivateKey.java************/ import java.math.*; public class RSAPrivateKey extends RSAKey { private BigInteger n, d; RSAPrivateKey(BigInteger n, BigInteger d) { this.n = n; this.d = d; } public BigInteger getPrivateExponent() { return d; } public BigInteger getProduct() { return n; } public void setPrivateExponent(BigInteger d) { this.d = d; } public void setProduct(BigInteger n) { this.n = n; } public String toString() { return "("+n.toString()+","+d.toString()+");"; } } /***********END RSAPrivateKey.java*************/ /**********BEGIN RSAPublicKey.java************/ import java.math.*; public class RSAPublicKey extends RSAKey { private BigInteger n, e; RSAPublicKey(BigInteger n, BigInteger e) { this.n = n; this.e = e; } public BigInteger getPublicExponent() { return e; } public BigInteger getProduct() { return n; } public void setPublicExponent(BigInteger e) { this.e = e; } public void setProduct(BigInteger n) { this.n = n; } public String toString() { return "("+n.toString()+","+e.toString()+");"; } } /***********END RSAPublicKey.java*************/ APPENDICE II. SOURCES ET BIBLIOGRAPHIE http://www.parodie.com/humpich http://dslab.csie.ncu.edu.tw/~lucas/rsamath.HTM http://nuts.citeglobe.com/indexrsa.htm http://www.rsasecurity.com/rsalabs/faq/ http://www.les-mathematiques.net PC et cartes à puces - Patrick Gueulle Cartes à puces - Patrick Gueulle Cours de cryptographie - Gilles Zémor Explications de wolf (wolf@madchat.org) et de mon prof de maths que je ne vais pas citer pour lui éviter des problèmes ;) Le mot de l'ami Schneier : "You see smart cards are used as credit cards all over Europe, but not in the United States. Why ? Because of the phone system. To combat fraud, US credit cards went to an online verification system. When you buy something, the merchant checks the validity of your card (and the availability of your credit) via modem. In Europe 15 years ago that type of system would not have worked in every country. Phones were expensive; many stores didn't even have one, and the average wait time for installation in Italy was one or two years. Phone calls were expensive and the connections were unreliable. Fielding an online system in Europe was expensive, so the credit card industry went with smart cards to give some measure of security for the transaction. It wasn't that smart cards were more secure than magnetic stripe cards, it was that the US solution to the problem of fraud - online verification - was less practical. Some intense lobbying by the European smart card vendors (Bull SA, Gemplus, and Schlumberger) didn't hurt, either." Secrets & Lies maddany@madchat.org ¤º°`°º¤ø,¸¸,ø¤º°`°º¤ø,¸¸,ø¤º°`°º¤ø,¸¸,ø¤º°`°º¤º°`°º¤ø,¸¸,ø¤º°`°º¤ø,¸¸,ø¤º°`°º¤ø,