Java Cryptography Architecture : architecture de cryptographie de Java (JCA est aussi souvent utilisé pour désigner les connecteurs J2EE).

Besoin

Fonctionnalités cryptographiques pour les applications Java.

Analyse

Dans un esprit d'abstraction des couches propriétaires, la JCA distingue :

  • les concepts de sécurité (identité, clés, certificats...)
    • généraux
      • Condensés de messages
      • Génération de clés asymétriques (privées et publiques)
      • Signatures électroniques
      • Paramètres d'algorithmes
    • spécifiques à Java
  • les implémentations de ces concepts (RSA, DSA, SHA, MD5)

Il est ainsi possible d'utiliser les implémentations cryptographiques de différents fournisseurs tout en garantissant une interopérabilité entre ces différentes implémentations par le biais d'interfaces standardisées (les concepts).

Implémentation

La JCA est constituée des packages java.security.*.

Implémentation des concepts généraux

Identité

Les identités sont représentées par des classes implémentant java.security.Principal. depuis Java 2 (dans Java 1, la classe java.security.Identity implémentait cette interface et fournissait diverses méthodes permettant d'associer des clés et certificats à cette identité. Java 2 a rendu cette dernière classe obsolète avec l'apparition de la base de clés java.security.Keystore, qui permet d'associer clés et certificats à partir d'un alias unique -- alias qu'un objet de type java.security.Principal peut fournir via sa méthode getName(). La classe java.security.Identity est donc obsolète à partir de Java 2, ainsi que ses dérivées java.security.IdentityScope et java.security.Signer.)

Clés

Le concept de clé est représenté par l'interface java.security.Key, dont dérivent les interfaces java.security.PublicKey (clé publique) et java.security.PrivateKey (clé privée).

Clé publique et clé privée peuvent être associées dans un objet java.security.KeyPair. Ces paires de clés peuvent être générées à l'aide d'un générateur de paires de clés spécifique à un algorithme. Par exemple :


import java.security.*;
KeyPairGenerator myKeyPairGenerator = KeyPairGenerator.getInstance("DSA");
  

Toute génération de paire de clé s'effectue sur la base de deux critères :

  • la taille de la clé (un entier représentant le nombre de bits sur lesquels coder la clé, compatible avec l'algorithme du générateur de clés).
  • une source aléatoire, permettant de garantir l'unicité de la clé. Cette source est en fait pseudo-aléatoire (PRNG) et représentée par la classe java.security.SecureRandom, dérivable par divers fournisseurs.

Par exemple :


SecureRandom myPRNG = SecureRandom.getInstance("SHA1PRNG");
myKeyPairGenerator.initialize(1024, myPRNG);
  

La génération s'effectue enfin via la méthode genKeyPair(). Attention, cette opération peut prendre plusieurs secondes (notamment en fonction de la taille de la clé) :


KeyPair myKeyPair = myKeyPairGenerator.genKeyPair();
  

Ces clés sont opaques. L'accès transparent aux données d'une clé s'effectue via l'interface java.security.spec.KeySpec.

Condensé de message

Un condensé de message est initialisé à partir d'une fabrique, en fonction de son algorithme (SHA, MD5). Par exemple :


MessageDigest monCondense = MessageDigest.getInstance("SHA");
  

Reste ensuite à fournir les données à soumettre à la fonction de hachâge, sous forme de tableaux d'octets :


byte[] donnees1= { 1, 21, 3, 4 };
byte[] donnees2 = { 5, 2, 3, 7, 8, 12};

monCondense.update(donnees1);
monCondense.update(donnees2);

La génération des données du condensé se faisant via sa méthode digest().


byte[] donneesCondense = monCondense.digest();

Il est également possible de générer des condensés à partir de données d'autres condensés. Il suffit pour cela de cloner les condensés afin de ne pas agir sur les mêmes données mais sur des copies de ces données.

Par exemple :


monCondense.update(donnees1);
byte[] donneesCondense1 = monCondense.clone().digest();

monCondense.update (donnees2);
byte[] donneesCondense1et2 = monCondense.digest();

Signature

ACL/Permissions

Implémentation des concepts spécifiques à Java

Les concepts suivants ont été spécifiquement définis pour s'intégrer dans la plate-forme Java 2.

Objets gardés

Les objets gardés sont représentés par la classe java.security.GuardedObject.

Dans Java 2, tous les objets Permission sont des gardes, ce qui permet de retourner facilement un objet dont l'accès est soumis à une permission donnée (un ensemble de permissions ?) au travers d'un GuardedObject.

Politique de Sécurité

Java 2 permet de définir une politique de sécurité. Cette nouvelle possibilité offre les avantages suivants :

  • La définition d'une politique de sécurité offre une expression claire de ce qui est autorisé et interdit à l'acteur définissant cette politique, et éventuellement aux utilisateurs soumis à cette politique.
  • Les permissions peuvent être définies de manière plus fine que dans Java 1 (qui n'offrait qu'une permission "tout ou rien")
  • les politiques de sécurités peuvent être définies à plusieurs niveaux : pour la plate-forme et pour chaque utilisateur. Il est donc possible de définir des Politiques de Sécurité différentes pour les utilisateurs d'une même machine (on pourra par exemple définir des politiques différentes pour l'Administrateur de la machine, pour un compte destiné à tester les logiciels téléchargés potentiellement dangereux, etc.).

Concrètement, ces politiques sont exprimées sous la forme de séquences de permissions (comparables au principe des ACL).

Origine de code

Une origine de code (Code Source) représente une identification de l'émetteur d'un code applicatif.

Cette identification peut s'effectuer sur la base de :

  • l'URL de provenance du code
  • le(s) certificat(s) du code s'il est signé
  • les deux

Il s'agit donc d'une extension du concept de CODEBASE, utilisé dans Java 1.1 pour associer du code et un ClassLoader.

Lors de l'examen de l'origine d'un code, on procède donc à une comparaison de l'origine du code (télé)chargé et de l'origine de code déclarée comme valide, qui peut comporter des jokers.

On peut par exemple décréter qu'une origine de code acceptable (constituée uniquement d'une URL dans ce cas) est :

http://*.valtech.fr/classes/*

Ainsi que le code téléchargé à l'aide de l'URL http://javarome.valtech.fr/classes/security/Example.class sera considéré comme valide par exemple.

Cette possibilité peut être utilisée au travers de java.security.CodeSource.implies (CodeSource), qui renvoie si l'origine d'un code (URL et certficats) est compatible avec l'origine d'un autre code.

Chargeur de classe

Vérifieur de bytecode

Gestionnaire de Sécurité

Contrôleur d'accès

Le rôle du contrôleur d'accès est donc à terme de remplacer le Gestionnaire de Sécurité. Il s'agit d'une classe statique (non-instantiable), contrôlant les accès aux ressources en fonction du Contexte de Sécurité courant.

Exécution privilégiée

Il est possible pour une classe privilégiée (disposant d'une permission donnée) de transmettre momentanément son contexte de protection (les permissions qui lui sont accordées) à une autre classe non privilégiée.

Il convient cependant d'être particulièrement précautionneux quant aux opérations effectuées dans de telles zones temporairement privilégiées (attention à ce que l'on fait, ce que l'on permet de faire) et d'une manière générale d'y effectuer le minimum possible d'opérations.

Cliché du contexte

(AccessControlContext / thread) pour examen d'une permission dans un autre contexte : rapport avec le GuardedObject ?

Domaine de Protection

Un domaine de protection représente un modèle similaire à celui de la sanbox de Java 1.1, où l'identification par CODEBASE serait étendue au concept d'origine de code.

Un domaine de protection associe un ensemble de permissions à une origine de code donnée.

Il existe typiquement deux catégories de domaines de protection :

  • le domaine "système", capable d'accéder aux ressources de la machine
  • le domaine "applicatif", nécessitant parfois de recourir momentanément au fonctionnalités offertes au domaine système (de la même manière que sous Unix on passe momentanément "root" pour effectuer certaines opérations utilisateur) au travers d'une exécution privilégiée.

Contexte de Sécurité

L'exécution du code de plusieurs classes par un même thread implique donc la traversée de plusieurs domaines de protection. On définit le Contexte de Sécurité comme l'intersection des permissions accordées par les Domaines de Protection traversés.

Une classe autorisée à écrire des fichiers pourra par exemple appeler une classe non autorisée à écrire de tels fichiers. Dans ce cas, la règle de l'intersection des domaines de protection ne permettra pas au code de la classe non privilégiée de disposer des privilèges de la classe appelante (l'intersection correspond à la permission la plus faible des deux).

Il est possible à tout moment de demander au Contrôleur d'Accès quel est le Contexte de Sécurité courant (du thread courant), via la méthode statique AccessControler.getContext(). Cette méthode retourne un objet de type java.security.AccessControlContext ou l'un de ces descendants (il pourra en effet être intéressant de spécialiser le contexte de sécurité pour y inclure de nouveaux paramètres).

Blocs et actions privilégiés

Il peut arriver qu'une classe privilégiée souhaite transmettre temporairement des privilèges à une classe moins privilégiée. Cette opération s'effectue au travers de l'exécution d'un "bloc privilégié".

L'exécution d'un Bloc Privilégié consiste à demander l'exécution d'une Action Privilégiée. Une Action Privilégiée est une classe contenant le code devant disposer de privilèges. Une telle classe doit implémenter l'interface java.security.PrivilegedAction, qui impose de fournir le code d'une méthode run(), où doit se trouver le code privilégié.

Par exemple :

class PrivilegedUserNameAccess
implements java.security.PrivilegedAction
{
public Object run()
{
return System.getProperty ("user.name");
}
}

L'exécution de cette action privilégiée est alors demandée au Contrôleur d'Accès :

String userName = (String) AccessControler.doPrivileged (new PrivilegedUserNameAccess());

Dans cet exemple, le bloc permet de retourner la valeur d'une propriété qui ne serait pas accessible si la classe appelante ne disposais pas de la permission nécessaire (en l'occurence java.util.PropertyPermission ("user.name", "read")).

Cependant, il est possible de faire bénéficier une action privilégiée d'un contexte de sécurité différent de celui de la classe appelante (un contexte plus limité par exemple). Dans ce cas, il suffit de fournir à la méthode doPrivileged() le contexte adéquat :

String userName = (String) AccessControler.doPrivileged (new PrivilegedUserNameAccess(), someActionContext);

Base de clés

Une base de clés (Key Store) constitue un référentiel de clés et certificats.

Une entrée de la base de clés peut être :

Cette base est représentée par la classe java.security.KeyStore et son mode de persistance peut être fourni par divers fournisseurs implémentant une java.security.KeyStoreSpi (sur une carte à puce par exemple).

Objet Signé

Un objet signé est un objet dont l'état courant a été signé à l'aide d'un certificat.

Exemples

Notes