Se faire une carte à puce OpenPGP

Tout autant célèbre par sa puissance que par sa complexité d'emploi, OpenPGP est une référence en matière de chiffrement et d'authentification. S'il a tendance a être assez faiblement utilisé pour la sécurisation des emails, il reste cependant très populaire pour la signature de fichiers, en particulier des paquets pour différentes distributions GNU/Linux. Personnellement je l'utilise quotidiennement par l'intermédiaire de pass, un gestionnaire de mots de passe se basant dessus. Considérant donc ma clé privée OpenPGP comme un élément central de ma sécurité informatique, je me suis souvent demandé comment la conserver en sécurité tout en l'utilisant sur plusieurs terminaux dont parfois la confiance est relativement réduite (téléphone portable). C'est là que j'ai découvert les cartes OpenPGP…

OpenPGP card

Quésaco ?

Pour faire simple, il est possible de mettre sa clé privée OpenGPG sur une carte à puce. Cette opération est à sens unique, il est impossible de sortir une clé privée de la carte. Pour chiffrer ou signer un fichier, il faut s'authentifier auprès de la carte qui réalisera ensuite les opérations cryptographiques en interne et renverra le résultat. Étant donné que GnuPG implémente les spécifications OpenPGP card, l'utilisation de la carte est simple.

Bien entendu, il est absolument indispensable de gérer le cas du vol, de la perte ou de la détérioration de la carte. Il ne faudrait pas qu'un pickpoket dans le métro vous prive définitivement de la clé privée sur quoi reposait toute votre identité numérique. À ce titre, je recommande fortement de ne pas générer la clé directement sur la carte, mais de la générer sur un ordinateur puis de la transférer sur la carte et d'en conserver une copie hors-ligne de manière sécurisée.

Introduction aux cartes à puces

Avant toute chose, intéressons nous aux cartes à puce d'une manière plus générale. Face à la forte diversité, il est très important de porter une grande attention aux standards et autres spécifications. Le premier qui vient en tête est l'ISO/CEI 7810 qui définit les formats. Viennent ensuite l'ISO/IEC 7816 pour les cartes avec contact et l'ISO/IEC 14443 pour le sans-contact.

Lors de sa fabrication, une carte intègre un système (logiciel) qu'il sera par la suite impossible de modifier. S'il existe des cartes dont la fonction est intrinsèquement liée à ce système, par exemple des cartes de stockage de données, de nos jours il existe de véritables systèmes d'exploitations permettant, après l'étape de fabrication, de charger des applications. La fonction métier est ainsi décorellée de la fabrication pure, ce qui permet aux fabricants de produire à la chaîne et de revendre le même produit à différentes entités qui y appliqueront leur couche applicative personnalisée. C'est ce type de cartes, dites programmables, qui nous intéresse en ce moment.

Il existe différents systèmes d'exploitations pour cartes programmables dont voici un aperçu :

JavaCard est de loin l'OS plus populaire, ce système permet de développer des application en Java. S'il existe une version 3.0, la plupart des cartes sont encore sous 2.2. Développer une application pour JavaCard nécessite l'utilisation du SDK approprié, disponible sur le site d'Oracle. Ce serait parfait si Oracle ne s'amusait pas à pourrir la vie de utilisateurs de logiciels libres en ne publiant la dernière version de ce SDK que sous la forme d'un .msi spécifique à Microsoft Windows. Fort heureusement, ça reste utilisable sous GNU/Linux une fois le .msi extrait.

Vient ensuite MULTOS, sans doute l'OS le plus intéressant. Il est développé par un consortium ouvert mettant l'accent sur la sécurité. Le développement d'applications se fait en C ou en assembleur et, bien que je ne l'ai pas testé, il semble relativement simple d'accès.

On peut ensuite citer deux OS totalement propriétaires, peu présents sur le marché et donc marginaux : .NET et BasicCard. Il n'y a rien à dire dessus si ce n'est que ce n'est absolument pas libre et que parfois on vous fera signer un NDA pour vous dire comment développer vos applications en toute sécurité. Bref, à éviter.

Après l'OS, il nous reste une spécification à laquelle il faut faire attention lors de l'achat d'une carte programmable : la manière dont les applications vont être chargées sur la carte. Si certains OS intègrent des méthodes qui leur sont propre, il existe également Global Platform qui est indépendant de tout système. Ce dernier à l'avantage de disposer d'une implémentation libre, GlobalPlatformPro. On le retrouve principalement sur les JavaCard.

Choisir son matériel

Notre objectif est ici d'utiliser une smart card pour y mettre l'application SmartPGP. Il nous faut donc une carte compatible JavaCard 3.0 avec une mémoire non-volatile d'environ 40 kB (moins ça ne marchera pas, plus ça ne sert à rien). Les utilisateurs de téléphones mobiles compatibles apprécieront le sans-contact afin de pouvoir chiffrer et déchiffrer leurs emails. Nous porterons également une grande attention au module cryptographique de la carte qui sera un critère d'exclusion absolu : il est impensable d'utiliser une clé OpenPGP qui soit trop faible à cause des limitations matérielles de la carte. Le dernier critère de choix sera la disponibilité de la carte à la vente publique à l'unité. En effet, les fabricants ne font pas de vente directe et la plupart du temps les ventes se font en gros. Fort heureusement, des sites tels que Smartcard Focus ou Almex couvrent ce besoin (attention toutefois aux frais de port en fonction de votre localisation géographique).

Après de très longues recherches et comparatifs, j'ai finalement opté pour deux ACOSJ 40K dual. Niveau cryptographique, le support de RSA 2048 et des courbes elliptiques de 384 bits est suffisant. D'après la FAQ de GnuPG, RSA 3072 et 4096 n'offrent pas autant de sécurité supplémentaire par rapport RSA 2048 que le nombre le laisse suggérer. L'argument des cartes à puces est d'ailleurs indiqué dans cette FAQ comme une raison poussant GnuPG à laisser RSA 2048 par défaut. Bien tendu, si vous trouvez une carte qui supporte plus, par exemple une SLJ 52GDL080CL, n'hésitez pas (et communiquez moi votre fournisseur) !

Pour terminer, il faut bien entendu un lecteur de cartes. Personnellement, j'ai opté pour un IDBridge CT30, peu onéreux et qui fait le travail.

Petite parenthèse pour les débutants

Dans ce billet, beaucoup de nombres sont représentées sous leur représentation hexadécimale, c'est à dire en base 16 (chiffre de 0 à 9 et lettres de A à F). Traditionnellement, on ajoute le préfixe 0x à ces nombres afin correctement les repérer. Par exemple, 0x2A représente le nombre hexadécimal 2A, c'est à dire 42 dans le système décimal.

Dans cet article, le préfixe 0x est généralement omis car le contexte de lecture est suffisant et que certains outils ne l'acceptent pas. Également, parfois, des espaces ont été ajoutées afin de regrouper les caractères par 2. Ceci est fort pratique car 2 caractères hexadécimaux représentent exactement 1 byte. Par exemple, 0x1A206 peut être représenté sur 4 bytes sous la forme 00 01 A2 06.

En avant !

Une fois le lecteur de cartes installé (par exemple via un service pcscd), on utilisera pcsc-tools pour vérifier que tout fonctionne bien :

$ pcsc_scan
PC/SC device scanner
V 1.5.2 (c) 2001-2017, Ludovic Rousseau <ludovic.rousseau@free.fr>
Using reader plug'n play mechanism
Scanning present readers...
0: Gemalto PC Twin Reader (362E4E86) 00 00

Sun May  6 16:04:19 2018
 Reader 0: Gemalto PC Twin Reader (362E4E86) 00 00
  Card state: Card inserted,
  ATR: 3B 69 00 02 41 43 4F 53 4A 76 31 30 31

ATR: 3B 69 00 02 41 43 4F 53 4A 76 31 30 31
+ TS = 3B --> Direct Convention
+ T0 = 69, Y(1): 0110, K: 9 (historical bytes)
  TB(1) = 00 --> VPP is not electrically connected
  TC(1) = 02 --> Extra guard time: 2
+ Historical bytes: 41 43 4F 53 4A 76 31 30 31
  Category indicator byte: 41 (proprietary format)

Possibly identified card (using /home/rodolphe/.cache/smartcard_list.txt):
3B 69 00 02 41 43 4F 53 4A 76 31 30 31
        ACOSJ 40K Dual Interface (JavaCard)
 /

Comme on peut le constater, tout est conforme : pcsc_scan a correctement détecté le modèle du lecteur (Gemalto PC Twin Reader est l'ancien nom de l'IDBridge CT30) et de la carte (3.0.4 dans mon cas). Le matériel fonctionnant, il nous faut compiler l'application. Pour ceci, commençons par créer un répertoire ~/projects/javacard/javacard_sdk dans lequel (non montré ici) nous allons copier les fichiers du SDK JavaCard dans la version supportée par la carte. Oui, le .msi ou .jar exécutable pour Windows téléchargé depuis le site d'Oracle, que l'on aura pris soin d'extraire depuis Windows ou bien wine.

$ mkdir -p ~/projects/javacard/javacard_sdk

Ensuite, on clone le dépôt de l'application SmartPGP.

$ git clone https://github.com/ANSSI-FR/SmartPGP.git ~/projects/javacard/smartpgp

Il faut enfin éditer le fichier build.xml situé à al racine du dépôt afin d'y indiquer un AID correct (<applet […] aid="[…]"/>) ainsi que le chemin vers le SDK (<javacard jckit="../javacard_sdk">). Pour les petites productions de cartes OpenPGP 3.3, nous pouvons générer un AID à l'aide de la commande suivante. Si besoin, vous trouverez plus d'information au sujet de l'AID en fin de billet.

$ echo "D276000124010303FF$(openssl rand -hex 1 | cut -c 2-)0$(openssl rand -hex 4)0000" | tr '[:lower:]' '[:upper:]'
D276000124010303FFC09474C25F0000
<?xml version="1.0"?>
<project name="smartpgp" default="convert" basedir=".">
  <description>Ant build for SmartPGP applet</description>
  <get src="https://github.com/martinpaljak/ant-javacard/releases/download/18.06.25/ant-javacard.jar" dest="." skipexisting="true"/>
  <taskdef name="javacard" classname="pro.javacard.ant.JavaCard" classpath="ant-javacard.jar"/>
  <target name="convert">
    <javacard jckit="../javacard_sdk">
      <cap output="SmartPGPApplet.cap" sources="src" aid="D27600012401" version="1.0">
        <applet class="fr.anssi.smartpgp.SmartPGPApplet" aid="D276000124010303FFC09474C25F0000"/>
      </cap>
    </javacard>
  </target>
</project>

Nous utilisons alors Apache Ant pour compiler le projet :

$ cd ~/projects/javacard/smartpgp/
$ ant
[…]
BUILD SUCCESSFUL
Total time: 1 second

Nous pouvons maintenant envoyer l'application sur la carte à l'aide de GlobalPlatformPro :

$ gp-pro -install ~/projects/javacard/smartpgp/build/fr/anssi/smartpgp/javacard/smartpgp.cap -default
[…]
CAP loaded

Et maintenant le moment de vérité : gpg peut-il interagir avec votre carte ? Pour le savoir, lancez un gpg --card-status. Si gpg vous affiche les caractéristiques de votre carte, numéro de série compris, c'est gagné ! Mais avant d'aller plus loin, finalisons notre carte. Pour ceci, verrouillons la avec une clé que nous allons générer. Il est possible de générer des clés de 16, 24 ou 32 bytes, mais toutes les cartes n'acceptent pas forcément toutes les tailles. Nous utiliserons ici une clé de 16 bytes :

$ openssl rand -hex 16 | tr '[:lower:]' '[:upper:]'
4785607DF80B6D81948419891996D609
$ gp-pro --lock 4785607DF80B6D81948419891996D609
Card locked with: 4785607DF80B6D81948419891996D609
Write this down, DO NOT FORGET/LOSE IT!

Notre carte est à présent protégée par une clé (que vous ne devez surtout pas perdre), mais ce n'en est pas pour autant terminé. En effet, les spécification Global Platform nous indiquent qu'une carte suit un cycle de vie bien définit. Actuellement notre carte doit logiquement être dans le mode OP_READY et n'est donc pas prête à être véritablement utilisée. Nous allons donc la passer successivement en mode INITIALIZED puis SECURED :

$ gp-pro --key 4785607DF80B6D81948419891996D609 --initialize-card
$ gp-pro --key 4785607DF80B6D81948419891996D609 --secure-card

Cette fois c'est bon, vous pouvez personnaliser votre carte à l'aide de la commande gpg --card-edit. Pour plus d'informations, consultez l'excellent GNU/Linux magazine 168 (février 2014).

Bonus : au sujet de l'AID

Comme vu au début de ce billet, la norme ISO/IEC 7816-5 spécifie que chaque application de carte à puce dispose d'un identifiant unique. Cet identifiant, l'AID, est composé de deux parties: le RID et le PIX.

Le RID, ou « registered application provider identifier » est l'identifiant de l'autorité définissant des spécifications d'applications. Dans le cas des cartes OpenPGP, l'autorité est la FSF Europe et son RID est D2 76 00 01 24.

Le PIX, ou « proprietary application identifier extension » des cartes OpenPGP est définit par la FSFE comme suit :

  • 1 byte pour identification de l'application (01 : OpenPGP) ;
  • 2 bytes pour la version des spécifications (03 03 pour la version 3.3) ;
  • 2 bytes pour l'identifiant du fabriquant ;
  • 4 bytes pour le numéro de série de la carte ;
  • 2 bytes réservés pour un usage futur (00 00).

Afin de produire des cartes en grand nombre, il faut donc s'enregistrer auprès de la FSFE afin de se faire assigner un identifiant. Pour les productions ponctuelles (type usage personnel), les spécifications indiquent qu'il est possible d'utiliser un identifiant dont le numéro est entre FF 00 et FF FE.

Le numéro de série est définit par le fabriquant. Ce dernier doit commencer par le numéro 1 (00 00 00 01) et s'incrémenter pour chaque carte produite. Une exception : si le fabricant a un identifiant tiré du lot réservé vu précédemment, il faut alors définir un numéro de série aléatoire.

Si vous regardez en détail la commande indiquée plus haut pour générer un AID, vous vous apercevrez de la construction suivante :

  • D276000124 : RID de la FSFE ;
  • 01 : déclare une application OpenPGP ;
  • 0303 : utilisation de la version 3.3 ;
  • FF$(openssl rand -hex 1 | cut -c 2-)0 : déclare un identifiant de fabriquant pioché dans le lot réservé pour les numéros de série aléatoires ;
  • $(openssl rand -hex 4) : génération d'un numéro de série aléatoire ;
  • 0000 : les 2 bytes réservés.

Notez que le l'identifiant de fabriquant est à moitié choisi aléatoirement parmi le lot disponible. En effet, FF FF ne faisant pas partit de ce lot, j'ai préféré garder une partie fixe afin d'être certain de ne pas générer cet autre identifiant réservé par mégarde.