Calculer la clé secrète de bureau d’émission de carte

Comme mentionné, l'authentification de Postcard se base sur une procédure de signature simplifiée par moyen d’algorithme cryptographique RSA. Lors de l’émission de la carte, les données en clair de la carte sont signées (composé entre autres du numéro de carte et la période de validité) avec la clé privée de l'éditeur (PostFinance). Le cryptogramme reçu est enregistré ensemble avec les données de la carte dans le secteur ROM de celle-ci..

Mathématiques de base

L’algorithme RSA permet théoriquement de calculée la clé privée à partir de la clé publique; la sécurité de la procédure est fonder sur l'impossibilité en pratique de la calculer et dépend directement de la longueur de la clé utilisée ainsi que les procédures mathématiques disponibles pour la factorisation de nombre premier des nombres très grands. Aujourd'hui, une clef RSA avec une longueur d'au moins 1024 bits sont considérés comme sûr.

Une paire de clés, de l’algorithme RSA, se compose de trois paramètres: modulo m, l'exposant public e et l'exposant privé d. La clé publique est alors définie par {e, m}, la clé privée, en conséquence, par {d, m}.

Le (dé)chiffrement des données en clair k et de cryptogramme c est mis en équation comme suivant:

Chiffrement: c = k^d mod m
Déchiffrement: k = c^e mod m

Tous les trois paramètres de paire de clés sont liés par des relations mathématiques suivant: modulo m est le produit de deux grands nombres premiers p et q, c.-à-d. que m = p · q. Comme variable auxiliaire on utilise le plus petit commun multiple de p-1 et de q-1. Il on résulte: r = PPCM (p-1, q-1) . Pour les exposants e et d tant e · d ? 1 mod r, et tant l'exposant public e qui est choisi librement, doit être un diviseur de r.

Si on réussit, à diviser modulo m dans ses deux produits de facteurs de nombres premiers p et q, on peut, avec la connaissance de l'exposant public e au moyen de l’équation ci-dessus, calculée l’exposant privé d et alors la clé privée.

Détermination du modulo m

Normalement on peut déduire modulo directement de la clé publique. Dans notre cas, la clé publique « n'est toutefois pas publiée ». Pour déterminer modulo de la paire de clés, il y a deux approches:

  1. Extraction de la clé publique d'un terminal TPE/PDV-Postcard:

    Si on est en possession d'un terminal de Postcard, on peut essayer extraire la clé de l’équipement. Cette procédure est toutefois très longue et exige les connaissances de l’équipement utilisé.

  2. Calcul du modulo à partir des données de la carte:

    Modulo rechercher peut être calculé aussi directement à partir des données de deux Postcards; cette procédure est nettement plus simple, puisqu’elle ne requit aucun équipement spécial.

Pour trouver modulo, nous choisissons la procédure 2 qui est plus simple. Nous nécessitons les données de deux Postcards, dont nous devons déterminer à jaque fois la valeur pour le texte en clair k ainsi bien que pour le cryptogramme c.

Pour recevoir la valeur modulo m, nous ne servons de la réflexion mathématique ci-dessous. Nous présumons que la valeur pour l'exposant public est 3 (comme pour la carte bancaire française).

Les équations suivantes sont en vigueur:

k1 = c1³ mod m ∧ k2 = c2³ mod m
→ c1³ - k1 = n1 · m ∧ c2³ - k2 = n2 · m
→ m = ggT (c1³ - k1, c2³ - k2)

Puisque nous pouvons utiliser deux Postcards quelconque, nous ne connaissons éventuellement pas le préfix de chaque carte. Par conséquent, nous calculons seulement kx (k'sans préfix) pour chaque carte. Le programme suivant calcule ainsi modulo avec les données des cartes:

import java.math.BigInteger;

public class ComputeMod2 {

  public static void main (String[] argv) {
    
    String c1Str  = "0EC6DA0D247A69CD093B6D852118CA4346B44EEECE4A3394F872C1356A8070BFEBB54669F375A63F2";
    String kx1Str = "0050162300000234208157701030503";

    String c2Str  = "09BA94341F1DD339766F71DF4BA1CC807C565AA0FC3BE835570104F5A0DF636D4F4F4AC7BFA481257";
    String kx2Str = "0050162300000234208157706021002";

    BigInteger c13 = new BigInteger (c1Str, 16).pow(3);
    BigInteger c23 = new BigInteger (c2Str, 16).pow(3);
    
    for (int n1 = 0; n1 < 16; n1++) {
      String tmp = "00004" + hex.charAt(n1) + "000" + kx1Str;
      BigInteger k1 = new BigInteger (tmp + tmp, 16);
      BigInteger a = c13.add(k1.negate());
      for (int n2 = 0; n2 < 16; n2++) {
        tmp = "00004" + hex.charAt(n2) + "000" + kx2Str;
        BigInteger k2 = new BigInteger (tmp + tmp, 16);
        BigInteger b = c23.add(k2.negate());
        
        BigInteger m = a.gcd(b);
        if (m.bitLength() > 318) {
          System.out.println ("Modulus:  0x" + m.toString(16));
          System.out.println ("          " + m.toString());
          return;
        }
      }
    }
    System.out.println ("Kein Modulus gefunden !?");
  }
  
  private static String hex = "0123456789ABCDEF";
}

Si on exécute ce calcul pour deux Postcard, on reçoit la valeur suivante pour modulo:

m = 100000000000000000000D62C6F32CD0EBF84B9339E3997B46570D236179E4675433DAA5FF513A6CF

Factorisation du modulo

Maintenant, on peut diviser modulo m par ses facteurs de nombres premiers p et q. En utilisant un programme comme qsieve qui permet en même temps de factoriser avec plusieurs ordinateurs, on reçoit le résultat souhaité après quelques heures:

p = 39D6E856F2DF3C90B6C973BF29DFB66E8E73DEA7
q = 46D111C85E2E5DD769207D4BAC02B0C039F9F6399

Calcul de l'exposant privé

Pour déterminer l'exposant privé nous pouvons utiliser l’équation e · d ? 1 mod r. La variable auxiliaire r a la valeur:

r = 800000000000000000006B16379966875FC25C977BA8C96bC7D5DC1767667EFDAC9973F6E3803248

Avec les valeurs calculées pour e et r, on obtient:

d = 2AAAAAAAAAAAAAAAAAAACE5CBD33222d1FEB74327E8D9879429C9EB277CCD4FF39887BFCF68010C3

Vérification des résultats

Avec le petit programme en Java, ci-dessous, nous pouvons examiner si tous nos résultats sont corrects. Tout essai devrait livrer « true » comme résultat. Celui qui possède une Postcard soi-même et un lecteur de cartes à puce, peut lire ses propres données et remplacer les valeurs c et k par les valeurs qu’il a déterminé lui-même:

import java.math.BigInteger;

public class Test {

  public static void main (String[] argv) {
    
    BigInteger c = new BigInteger ("0EC6DA0D247A69CD093B6D852118CA4346B44EEECE4A3394F872C1356A8070BFEBB54669F375A63F2", 16);
    BigInteger k = new BigInteger ("04400000501623000002342081577010305030000440000050162300000234208157701030503", 16);
     
    BigInteger m = new BigInteger ("100000000000000000000D62C6F32CD0EBF84B9339E3997B46570D236179E4675433DAA5FF513A6CF", 16);
    BigInteger p = new BigInteger ("39D6E856F2DF3C90B6C973BF29DFB66E8E73DEA7", 16);
    BigInteger q = new BigInteger ("46D111C85E2E5DD769207D4BAC02B0C039F9F6399", 16);
    BigInteger d = new BigInteger ("2AAAAAAAAAAAAAAAAAAACE5CBD33222d1FEB74327E8D9879429C9EB277CCD4FF39887BFCF68010C3", 16);
    BigInteger e = BigInteger.valueOf (3);

    check ("p * q == m", p.multiply (q), m);
    check ("c^d mod m == k", c.modPow (e, m), k);
    check ("k^d mod m == c", k.modPow (d, m), c);
  }

  private static void check (String msg, BigInteger a, BigInteger b) {
    boolean ok = a.equals(b);
    if (ok)
      System.out.println (msg + ": SUCCESS");
    else {
      System.out.println (msg + ": FAILED!");
      System.out.println ("     " + a.toString(16));
      System.out.println ("  <> " + b.toString(16));
    }
  }
}