Ces avantages ont pourtant un bémol dans la mesure où le code source peut être récupéré et signé par un autre certificat, mais on entre alors dans le domaine du piratage et si le certificat d'origine est déposé chez une autorité de certification, sa validité juridique est alors à envisager (cqfd : le bémol du bémol).
Lorsque l'on développe une application web avec Mozilla, on peut avoir besoin d'activer certaines fonctionnalités sur le navigateur. Il se peut que ces dernières soient protégées par une restriction de leur accès à des personnes reconnues de confiance.
Les fonctionnalités protégées sont les suivantes :
Pour être une personne reconnue de confiance, deux possibilités s'offrent à nous :
La première solution ne nous intéressee pas ici car nous désirons créer une application distante qui n'oblige pas l'utilisateur à de constantes mises à jour au fur et à mesure de l'évolution de notre application.
En revanche, la dernière solution est particulièrement attractive car elle permet au client d'avoir simplement un navigateur et d'utiliser notre logiciel de façon transparente par rapport aux changements de version et aux aléas des corrections de bogues et autres changements liés au travail de l'équipe de développement.
Le client, doit avant de commencer à utiliser notre application distante, installer un certificat de distribution.
Ce certificat, généré par une autorité de certification, garantit que le code exécuté a bien pour origine notre atelier de développement et que son contenu est bien dédié à ce qu'il attend de notre application. C'est la clé publique.
Sans ce certificat il ne pourra jamais exécuté les parties sensibles du code qui font appel aux fonctionalités protégées du navigateur. Il est donc une garantie :
Afin de rendre plus clair ce qui va suivre, voici le schéma de la hiérarchie des certificats :

Le principe de certification est le suivant. Tout certificat fait partie d'une chaîne de certification. Lorsqu'on lit un certificat, on connaît celui auquel il se réfère, il est certifié ainsi par son père. Chaque père a lui-même un père. Il existe cependant une exception à cette règle : le certificat racine qui est son propre père. Ce certificat racine est généré par une autorité de certification ou par l'organisation qui devient ainsi son propre certifieur.
Comme toute chaîne, celle-ci a deux extrémités. Le certificat racine en amont et le certificat de signature en aval. Le certificat de signature ne peut servir qu'à signer des applications. Les applications ainsi signées seront vérifiées par le père du certificat de signature, c'est à dire dans notre cas, par le certificat de distribution.
Cette hiérarchie permet de délivrer autant de certificat de signature à autant de personnes que l'on désire et chacune des applications signées par l'un de ces certificats sera authentifiée par un seul et unique certificat : le certificat de distribution.
L'intérêt est que l'utilisateur n'aura à installer qu'un seul certificat sans se soucier ni de l'évolution de l'application ni de l'organisation de son développement.
Nous allons juste décrire ici les quelques manipulations qui vont nous être utiles. Pour des informations plus poussées, veuillez vous reporter à la documentation de Mozilla (http://books.mozdev.org/html/mozilla-chp-12-sect-5.html).
La création des certificats va se dérouler en 6 étapes :
Pour ces étapes, nous utiliserons le paquetage NSS (Network Security Services) fourni par Mozilla. Celui-ci contient des outils de sécurité multi-plateformes pour les communications client-serveur. Nous n'en utiliserons que deux :
Pour mettre en oeuvre le système de certification, nous utiliserons un serveur de certificats ; la machine sur laquelle nous créerons les certificats et qui devra n'être accessible que par des utilisateurs physiquement identifiés. En effet, les certificats créés seront une garantie pour l'entreprise, il n'est donc pas question que n'importe qui puisse accéder à la génération de ceux-ci.
Enfin, pour la signature de l'application, le responsable du projet devra utiliser les certificats de signature exportés depuis le serveur pour signer le répertoire contenant le logiciel.
Pour manipuler nos certificats, nous aurons besoin d'une base de données qui regroupera les informations de ceux-ci (identifiant du certificat, organisation, clé privée...). Mozilla utilise déjà une base de données contenant des certificats provenant de certaines autorités de certification, mais il est préférable de créer une nouvelle base distincte pour nos propres certificats.
Nous allons ouvrir la ligne de commandes du serveur d'authentification et y taper la commande suivante :
Un mot de passe est alors demandé, puis sa confirmation. Ce mot de passe doit bien évidemment rester strictement confidentiel. Il ne devra être connu que des personnes autorisées à générer les certificats.
Cette commande a pour effet de créer une base de données dans le répertoire DBPATH. Ce répertoire aura préalablement été créé manuellement.
La base de données ainsi créée pourra contenir autant de certificats que l'on souhaite.
Si on liste le répertoire DBPATH après exécution de la commande, on peut y voir trois fichiers :
Nous ne devrons en aucun cas toucher à ces fichiers !
Nous allons maintenant pouvoir passer à l'étape 2.
Le certificat racine est un certificat que nous ne trouverons que sur le serveur de certification. Il est l'origine de tout certificat, y compris lui-même. Si plusieurs organisations utilisent le même serveur, un certificat racine devra être créé pour chacune d'elle.
Voici la commande de création :
Ici CERTNAME est le nom du certificat, il pourra être strictement équivalent à ORGNAME, le nom de l'organisation propriétaire du certificat. Il ne faut pas omettre de spécifier le répertoire de la base de données (DBPATH).
L'option -s permet de spécifier le nom du certificat que l'on crée (champ CN) et le nom de l'organisation à laquelle il se rattache (champ O).
L'option -n est le nom du certificat sur lequel se base le nouveau.
Lors de l'exécution de la commande, plusieurs questions seront posées, les voici accompagnées des réponses qui doivent être apportées :
Generating key. This may take a few moments...
0 - Digital Signature
1 - Non-repudiation
2 - Key encipherment
3 - Data encipherment
4 - Key agreement
5 - Cert signing key
6 - CRL signing key
Other to finish
5
0 - Digital Signature
1 - Non-repudiation
2 - Key encipherment
3 - Data encipherment
4 - Key agreement
5 - Cert signing key
6 - CRL signing key
Other to finish
9
Is this a critical extension [y/n]?
n
Is this a CA certificate [y/n]?
y
Enter the path length constraint, enter to skip [<0 for unlimited path]:
Is this a critical extension [y/n]?
n
0 - SSL Client
1 - SSL Server
2 - S/MIME
3 - Object Signing
4 - Reserved for futuer use
5 - SSL CA
6 - S/MIME CA
7 - Object Signing CA
Other to finish
7
0 - SSL Client
1 - SSL Server
2 - S/MIME
3 - Object Signing
4 - Reserved for futuer use
5 - SSL CA
6 - S/MIME CA
7 - Object Signing CA
Other to finish
9
Is this a critical extension [y/n]?
n
Le certificat de distribution va être créé depuis le certificat racine (-c) :
On retrouve la même construction qu'à l'étape précédente avec une option supplémentaire : -v. Cette dernière permet de spécifier le nombre de mois de validité du certificat. Il faut prendre en compte le fait que la durée minimum est de trois mois, ainsi, si l'on désire une validité de 12 mois, VALID devra valoir 9.
Il n'existe pas, à ma connaissance, de limite maximum.
Là aussi, des questions seront posées :
Generating key. This may take a few moments...
0 - Digital Signature
1 - Non-repudiation
2 - Key encipherment
3 - Data encipherment
4 - Key agreement
5 - Cert signning key
6 - CRL signning key
Other to finish
5
0 - Digital Signature
1 - Non-repudiation
2 - Key encipherment
3 - Data encipherment
4 - Key agreement
5 - Cert signning key
6 - CRL signning key
Other to finish
9
Is this a critical extension [y/n]?
n
Is this a CA certificate [y/n]?
y
Enter the path length constraint, enter to skip [<0 for unlimited path]:
Is this a critical extension [y/n]?
n
0 - SSL Client
1 - SSL Server
2 - S/MIME
3 - Object Signing
4 - Reserved for futuer use
5 - SSL CA
6 - S/MIME CA
7 - Object Signing CA
Other to finish
7
0 - SSL Client
1 - SSL Server
2 - S/MIME
3 - Object Signing
4 - Reserved for futuer use
5 - SSL CA
6 - S/MIME CA
7 - Object Signing CA
Other to finish
9
Is this a critical extension [y/n]?
n
Le certificat de distribution est la clé publique d'une application signée, par conséquent chaque utilisateur reconnaîtra la signature de l'application grâce à ce dernier. Nous aurons donc besoin de le distribuer aux utilisateurs, ce qui signifie que nous devons l'exporter.
Pour l'exportation, nous spécifions à certutil que nous voulons un fichier texte (-a) que nous placerons dans un fichier cacert, extension communément retenue pour les certificats.
L'option -o nous permet de donner le répertoire d'exportation (EXPORT) et le nom du fichier, composé ici du nom du certificat racine.
Une fois exporté, le certificat pourra être mis à disposition sur un site Internet ou bien envoyé par mél à l'utilisateur.
Administrateur du serveur web attention !! Sur le serveur Internet où sera mis à disposition le certificat, l'extension de fichier .cacert devra être associée au type MIME application/x-x509-ca-cert afin que le fichier soit reconnu par le navigateur et que l'assistant d'installation de certificats soit lancé.

Lorsque l'utilisateur voudra installer le certificat, à l'affichage de la fenêtre d'installation, il devra cocher les options "identify web site" et "identify software developpers". Cela sera la seule manipulation qui lui sera demandé.
Une fois le certificat de distribution créé, nous pouvons générer le certificat de signature. Idéalement, il en existe un par développeur pour pouvoir authentifier la partie de code de chacun, mais il est également possible de n'en créer qu'un seul, que le responsable du projet pourra utiliser pour signer l'application vérifiée par ses soins.
Voici la commande génératrice :
Comme on peut le remarquer, ce certificat se base sur le certificat de distribution (-c) d'où l'intérêt de créer ce dernier avant. Le nom du certificat de signature importe peu, ici nous avons utilisé un nom hiérarchique, à la manière des répertoires UNIX afin de marquer la parenté de notre certificat. Mais il serait possible de l'appeler certificat_du_developpeur_toto sans aucun soucis.
Voici l'interaction avec certutil lors de l'exécution de cette commande :
Generating key. This may take a few moments...
0 - Digital Signature
1 - Non-repudiation
2 - Key encipherment
3 - Data encipherment
4 - Key agreement
5 - Cert signning key
6 - CRL signning key
Other to finish
0
0 - Digital Signature
1 - Non-repudiation
2 - Key encipherment
3 - Data encipherment
4 - Key agreement
5 - Cert signning key
6 - CRL signning key
Other to finish
5
0 - Digital Signature
1 - Non-repudiation
2 - Key encipherment
3 - Data encipherment
4 - Key agreement
5 - Cert signning key
6 - CRL signning key
Other to finish
9
Is this a critical extension [y/n]?
n
Is this a CA certificate [y/n]?
n
Enter the path length constraint, enter to skip [<0 for unlimited path]:
Is this a critical extension [y/n]?
n
0 - SSL Client
1 - SSL Server
2 - S/MIME
3 - Object Signing
4 - Reserved for futuer use
5 - SSL CA
6 - S/MIME CA
7 - Object Signing CA
Other to finish
3
0 - SSL Client
1 - SSL Server
2 - S/MIME
3 - Object Signing
4 - Reserved for futuer use
5 - SSL CA
6 - S/MIME CA
7 - Object Signing CA
Other to finish
9
Is this a critical extension [y/n]?
n
Il ne nous reste plus qu'à exporter ce certificat pour le donner au développeur Toto ou bien au responsable du projet :
Rien de nouveau ici par rapport à l'exportation du certificat de distribution.
Nous nous plaçons maintenant du côté du développeur ou du responsable de projet qui désire signer son application.
Nous avons reçu notre certificat de signature, il convient donc de l'ajouter à notre base de données de certificats. Si nous n'en avons pas, il suffit de se reporter à l'étape 1 de la création de certificats.
La commande qui permet d'ajouter un certificat à une base de données est la suivante :
L'option -n spécifie le nom que nous donnons à notre certificat, il peut être tout à fait quelconque.
L'option -i donne l'emplacement du fichier .cacert à insérer dans la base.
Enfin, n'oublions pas de dire dans quelle base de données nous ajoutons le certificat avec l'option -d.
La signature d'une application est générée depuis une clé privée et le contenu des fichiers de ladite application. Le contenu va être lu bit à bit par le générateur qui construira alors la signature en utilisant le résultat de sa lecture combiné à la clé. Chaque signature est donc unique.
Maintenant, nous allons utiliser un autre outil de NSS : signtool.
Pour procéder, nous avons besoin de connaître le répertoire de l'application à signer. Il faut savoir que signtool parcourera ce répertoire fichier par fichier, l'accès en lecture est donc requis. Les signatures générées se situeront dans le sous-répertoire META-INF de notre application (pas besoin de créer ce dernier manuellement). Une fois les clés générées, signtool créera une archive JAR du contenu du répertoire si l'option -Z lui est spécifiée.
L'option -k indique le certificat à utiliser pour générer les clés.
L'option -p prend en paramètre le mot de passe de la base de données spécifiée par DBPATH.
Enfin ARCHIVEJAR est l'emplacement et le nom de l'archive à créer ou à remplacer et APPDIR, le répertoire de l'application à signer.
Une fois l'archive crée, il ne reste plus qu'à la mettre en ligne, accessible de tous les utilisateurs.
Voici une tentative d'explication du processus de vérification de la signature :

Malheureusement, les applications signées ont leurs limites qui ne les rendent pas toujours adaptées à tous les cas.
Comme dit précédemment, la signature d'applications distantes sert principalement à autoriser l'utilisation de fonctionnalités protégées de Mozilla. Ces fonctionnalités sont accessibles depuis des scripts JavaScript. Ces scripts peuvent être intégrées aux pages d'interface (XUL ou HTML), mais elles peuvent également se trouver dans des fichiers séparés, dans un but de modularité. Une page d'interface signée peut sans aucun problème faire appel à un fichier JavaScript signé. En revanche, si la page appelante n'est pas signée, que le fichier de scripts soit signé ou non, le comportement de Mozilla sera invariablement le même : il ne reconnaîtra pas le script comme étant signé.
La conséquence de ceci est que le script ne pourra pas appeler des fonctionnalités protégées, mais pourra tout de même exécuter des actions non sensibles à la sécurité.
Ce comportement, volontairement induit par les dévelopeurs de Mozilla, est destiné à éviter toute tentative d'usurpation de droits par une application frauduleuse qui pourrait ainsi utiliser les droits d'un script signé dans un but mal intentionné.
Ainsi, toute application sécurisée, devra intégralement être signée par le même certificat de signature. Il est bon de noter qu'il n'est pas nécessaire que les fichiers soient inclus dans la même archive JAR.
Le standard pour JavaScript prévoit la réservation des mots import et export.
Ces mots-clés pourraient être une future solution à ce problème car une fonction JavaScript signée exportée à la fin du même script pourrait être importée dans un script non signé.
Malheureusement, ces mot-clés ne semblent pas être implémentés dans les différents moteurs JavaScript de Mozilla. Il reste à savoir si cet oubli est volontaire pour respecter le principe de protection contre l'usurpation des droits, ou bien s'il s'agit effectivement d'un bogue.
Une autre conséquence du point précédent est que la possibilité de génération dynamique des pages de l'application par un serveur n'est pas envisageable.
En effet, la seule solution pour palier à ce problème serait de faire générer la signature de l'archive par le serveur à chaque modification de page. Cette opération fort coûteuse en ressources serait envisageable sur un serveur peu sollicité, mais est généralement à proscrire.
Pour mieux illustrer mes propos, nous terminerons cet article avec un petite application qui va tester l'activation de chaque fonctionnalité sécurisée (privilège dans les termes de Mozilla).
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
onload="init();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- insertion du script -->
<script src="exemple.js" type="application/x-javascript" />
<!-- Les témoins de l'activation de chaque privilège -->
<label id="browserread" value=""/>
<label id="browserwrite" value=""/>
<label id="xpconnect" value=""/>
<label id="preferencesread" value=""/>
<label id="preferenceswrite" value=""/>
<label id="preferencesaccess" value=""/>
<label id="fileread" value=""/>
</window>
/**
* Teste tous les privilèges de sécurité à l'initialisation de la page XUL.
* Appel : par l'événement onload de l'élément window.
*/
function init() {
try {
netscape.security.PrivilegeManager
.enablePrivilege('UniversalXPConnect');
document.getElementById('xpconnect')
.value = 'UniversalXPConnect Privileges enabled';
} catch (e) {
alert(e);
document.getElementById('xpconnect')
.value = 'UniversalXPConnect Privileges not allowed';
}
try {
netscape.security.PrivilegeManager
.enablePrivilege("UniversalBrowserRead");
document.getElementById('browserread')
.value = 'UniversalBrowserRead Privileges enabled';
} catch (e) {
alert(e);
document.getElementById('browserread')
.value = 'UniversalBrowserRead Privileges not allowed';
}
try {
netscape.security.PrivilegeManager
.enablePrivilege("UniversalBrowserWrite");
document.getElementById('browserwrite')
.value = 'UniversalBrowserWrite Privileges enabled';
} catch (e) {
alert(e);
document.getElementById('browserwrite')
.value = 'UniversalBrowserWrite Privileges no allowed';
}
try {
netscape.security.PrivilegeManager
.enablePrivilege("UniversalPreferencesRead");
document.getElementById('preferencesread')
.value = 'UniversalPreferencesRead Privileges enabled';
} catch (e) {
alert(e);
document.getElementById('preferencesread')
.value = 'UniversalPreferencesRead Privileges not allowed';
}
try {
netscape.security.PrivilegeManager
.enablePrivilege("UniversalPreferencesWrite");
document.getElementById('preferenceswrite')
.value = 'UniversalPreferencesWrite Privileges enabled';
} catch (e) {
alert(e);
document.getElementById('preferenceswrite')
.value = 'UniversalPreferencesWrite Privileges not allowed';
}
try {
netscape.security.PrivilegeManager
.enablePrivilege("CapabilityPreferencesAccess");
document.getElementById('preferencesaccess')
.value = 'CapabilityPreferencesAccess Privileges enabled';
} catch (e) {
alert(e);
document.getElementById('preferencesaccess')
.value = 'CapabilityPreferencesAccess Privileges not allowed';
}
try {
netscape.security.PrivilegeManager
.enablePrivilege("UniversalFileRead");
document.getElementById('fileread')
.value = 'UniversalFileRead Privileges enabled';
} catch (e) {
alert(e);
document.getElementById('fileread')
.value = 'UniversalFileRead Privileges not allowed';
}
}
On place ces deux fichiers dans un répertoire "exemple", puis on signe cette application :
L'archive JAR est placée ici dans le répertoire courant. Il ne reste plus qu'à la copier dans le répertoire de notre serveur web puis à créer une page de lancement afin d'éviter à l'utilisateur de saisir une URL complexe.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Exemple : lancement</title>
</head>
<body>
<h1>Exemple : lancement</h1>
<a href="jar:http://monsite.net/exemple.jar!/exemple.xul">Lancer l'exemple</a>
</body>
</html>
Lors de l'exécution, une fenêtre apparait à l'utilisateur.

Elle permet de lui demander l'autorisation d'activation du privilège en cours de test et s'affiche si Mozilla est configuré avec les préférences par défaut.
Avec notre script, voici ce qui se passe lorsqu'un privilège est refusé par l'utilisateur :

Il s'agit en fait de l'exception JavaScript retournée par le navigateur que nous affichons dans une boîte de dialogue.
Vous avez maintenant en main tous les éléments nécessaires pour créer une application distante signée. Merci de votre attention et n'hésitez pas à faire parvenir vos remarques et suggestions à <martial.braux@free.fr> et <dcourtin@systemplus.fr>.