Construire des applications en réseau sécurisées avec des certificats, partie 2

Pour construire des applications sécurisées, vous devez apprendre les outils du métier. Pour vous aider à vous familiariser avec ces concepts, je vous ai présenté la cryptographie à clé publique dans la partie 1 et expliqué comment elle évite les problèmes d'échange de clés qui accompagnent la cryptographie à clé secrète. J'ai également exploré la relation entre la confiance et l'évolutivité de la cryptographie à clé publique, et expliqué comment les certificats et une infrastructure à clé publique (PKI) permettent la confiance à une échelle plus large que la cryptographie à clé publique ne peut le faire seule. Enfin, j'ai décrit les certificats et les chaînes de certificats, et expliqué comment ils se rapportent aux CA (autorités de certification).

De nombreux types de certificats sont disponibles, notamment SDSI (infrastructure de sécurité distribuée simple), PGP (assez bonne confidentialité) et X.509. Ce mois-ci, pour élargir encore votre vocabulaire de sécurité, je décrirai le format de certificat qui mène le peloton et qui est un élément clé des normes PKI émergentes: le certificat X.509.

Vous pouvez lire toute la série sur les certificats:

  • Partie 1: Les certificats ajoutent de la valeur à la cryptographie à clé publique
  • Partie 2: Apprenez à utiliser les certificats X.509
  • Partie 3: Utilisez les classes Java CRL et X509CRL
  • Partie 4: authentifier les clients et les serveurs et vérifier les chaînes de certificats

Le format X.509 en détail

L'Union internationale des télécommunications (UIT) a développé et publié le format de certificat X.509, qui a été choisi par le groupe de travail Public Key Infrastructure X.509 (PKIX) de l'Internet Engineering Task Force (IETF). Si les acronymes indiquent la force, X.509 a clairement de puissants alliés.

En utilisant une notation appelée ASN.1 (Abstract Syntax Notation One), la norme X.509 définit le format d'un certificat. ASN.1 est un langage normalisé qui décrit des types de données abstraits d'une manière indépendante de la plate-forme.

Le document «Internet X.509 Public Key Infrastructure - Certificate and CRL Profile» (voir Ressources pour un lien) publié par le groupe de travail PKIX décrit un format de certificat X.509 en termes de notation ASN.1. C'est une lecture fascinante si vous êtes intéressé par ce genre de chose.

Un type de données - tel qu'un certificat - défini dans ASN.1 n'est pas utile tant qu'il ne peut pas définir sans ambiguïté comment représenter une instance d'un type de données sous la forme d'une série de bits. Pour donner au type de données cette fonctionnalité, ASN.1 utilise les règles de codage distinctives (DER), qui définissent comment coder de manière unique tout objet ASN.1.

Avec une copie de la définition ASN.1 d'un certificat X.509 et une connaissance du DER, vous pouvez écrire une application Java qui lira et écrira des certificats X.509 et interagira avec des applications similaires écrites dans d'autres langages de programmation. Heureusement, vous n'aurez probablement jamais à vous donner autant de mal car la plate-forme Java 2, Standard Edition (J2SE) est fournie avec une prise en charge intégrée des certificats X.509.

X.509 pour (presque) rien

Toutes les classes et interfaces liées aux certificats résident dans le package java.security.cert. Comme les autres membres de la famille d'API de sécurité de Sun, le package de certificat a été conçu autour du paradigme d'usine, dans lequel une ou plusieurs classes Java définissent une interface générique vers la fonctionnalité prévue d'un package. Les classes sont abstraites, les applications ne peuvent donc pas les instancier directement. Au lieu de cela, l'instance d'une classe d'usine crée et retourne des instances des sous-types particuliers des classes abstraites. Le paradigme d'usine contourne le typage fort de Java, mais en retour, permet au code de s'exécuter sans recompilation dans un plus large éventail d'environnements.

Les classes abstraites java.security.cert.Certificateet java.security.cert.CRLdéfinissent l'interface. Ils représentent respectivement des certificats et des listes de révocation de certificats (CRL). La CertificateFactoryclasse est leur usine.

Le java.security.certpaquet contient des implémentations concrètes des classes Certificateet CRLabstraites: les classes X509Certificateet X509CRL. Ces deux classes implémentent le certificat de base et la fonctionnalité CRL, puis l'étendent avec des fonctionnalités spécifiques à X.509. Lorsqu'une CertificateFactoryinstance renvoie une instance de l'une ou l'autre des classes, un programme peut soit l'utiliser telle quelle, soit la convertir explicitement au formulaire X.509.

Dans le java.security.certpackage, l'interface X509Extensiondéfinit une interface vers les extensions d'un certificat X.509. Les extensions sont des composants facultatifs qui fournissent un mécanisme permettant aux créateurs de certificats d'associer des informations supplémentaires à un certificat. Par exemple, un certificat peut utiliser l' KeyUsageextension pour indiquer qu'il peut être utilisé pour la signature de code.

Le java.security.certpackage comprend également une classe SPI (Service Provider Interface). Un fournisseur de services cryptographiques qui souhaite prendre en charge un type de certificat étend le SPI. Java 2 est fourni avec un SPI pour les certificats X.509.

Jetons un regard plus détaillé sur les classes et les interfaces du java.security.certpackage. Par souci de brièveté, je ne discuterai que des méthodes les plus utiles. Pour une couverture plus complète, je vous encourage à lire la documentation de Sun. (Voir Ressources.)

java.security.cert.CertificateFactory

L'histoire commence par java.security.cert.CertificateFactory. La CertificateFactoryclasse a des méthodes statiques qui créent une CertificateFactoryinstance pour un type spécifique de certificat et des méthodes qui créent à la fois des certificats et des CRL à partir des données fournies dans un flux d'entrée. Je décrirai brièvement les méthodes les plus importantes, puis expliquerai comment utiliser ces méthodes lors de la génération de certificats X.509 et de CRL. Plus loin dans l'article, je présenterai du code qui montre les méthodes en action.

  • public static CertificateFactory getInstance(String stringType)et public static CertificateFactory getInstance(String stringType, String stringProvider)instancier et renvoyer une instance d'une fabrique de certificats pour le type de certificat spécifié par le stringTypeparamètre. Par exemple, si la valeur de stringTypeest la chaîne "X.509", les deux méthodes renverront une instance de la CertificateFactoryclasse appropriée pour créer des instances des classes X509Certificateet X509CRL. La deuxième méthode accepte le nom d'un fournisseur de services cryptographiques spécifique comme argument et utilise ce fournisseur au lieu de la valeur par défaut.
  • public final Certificate generateCertificate(InputStream inputstream)instancie et renvoie un certificat à l'aide des données lues à partir de l' InputStreaminstance fournie . Si le flux contient plus d'un certificat et que le flux prend en charge les opérations mark()et reset(), la méthode lira un certificat et laissera le flux positionné avant le suivant.
  • public final Collection generateCertificates(InputStream inputstream)instancie et renvoie une collection de certificats à l'aide des données lues à partir de l' InputStreaminstance fournie . Si le flux donné ne prend pas en charge mark()et reset(), la méthode consommera tout le flux.
  • public final CRL generateCRL(InputStream inputstream)instancie et renvoie une CRL à l'aide des données lues à partir de l' InputStreaminstance fournie . Si le flux contient plus d'une CRL et prend en charge les opérations mark()et reset(), la méthode lira une CRL et laissera le flux positionné avant la suivante.
  • public final Collection generateCRLs(InputStream inputstream)instancie et renvoie une collection de CRL à l'aide de données lues à partir de l' InputStreaminstance fournie . Si le flux donné ne prend pas en charge mark()et reset(), public final Collection generateCRLs(InputStream inputstream)consommera l'intégralité du flux.

Il est important de comprendre comment ces quatre méthodes se comportent lors de la génération d'instances X.509 à partir d'un flux de données. Nous allons jeter un coup d'oeil.

Les méthodes generateCertificate()et generateCRL()s'attendent à ce que le contenu du flux d'entrée contienne des représentations codées en DER d'un certificat ou d'une CRL, respectivement.

Les méthodes generateCertificates()et generateCRLs()s'attendent à ce que le contenu du flux d'entrée contienne soit une séquence de représentations codées DER, soit un certificat conforme à PKCS # 7 (Public-Key Cryptography Standard # 7) ou un ensemble CRL. (Voir les ressources pour les liens.)

java.security.cert.Certificate

java.security.cert.Certificatedéfinit l'interface commune à tous les types de certificats: X.509, PGP et une petite poignée d'autres. Les méthodes les plus importantes de cette classe sont:

  • public abstract PublicKey getPublicKey() renvoie la clé publique liée à l'instance de certificat sur laquelle cette méthode est appelée.
  • public abstract byte [] getEncoded() renvoie la forme codée de ce certificat.
  • public abstract void verify(PublicKey publickey)et public abstract void verify(PublicKey publickey, String stringProvider)vérifier que la clé privée correspondant à la clé publique fournie a signé le certificat en question. Si les clés ne correspondent pas, les deux méthodes lancent un SignatureException.

java.security.cert.X509Certificate

La classe java.security.cert.X509Certificateétend la Certficateclasse décrite ci-dessus et ajoute des fonctionnalités spécifiques à X.509. Cette classe est importante car vous interagissez généralement avec des certificats à ce niveau, et non en tant que classe de base.

  • public abstract byte [] getEncoded()renvoie la forme codée de ce certificat, comme ci-dessus. La méthode utilise le codage DER pour le certificat.

La plupart des java.security.cert.X509Certificatefonctionnalités supplémentaires de se composent de méthodes de requête qui renvoient des informations sur le certificat. J'ai présenté la plupart de ces informations dans la partie 1. Voici les méthodes:

  • public abstract int getVersion() renvoie la version du certificat.
  • public abstract Principal getSubjectDN() renvoie des informations qui identifient le sujet du certificat.
  • public abstract Principal getIssuerDN() renvoie des informations qui identifient l'émetteur du certificat, qui est généralement l'autorité de certification, mais qui peuvent être le sujet si le certificat est auto-signé.
  • public abstract Date getNotBefore()et public abstract Date getNotAfter()renvoyer des valeurs qui restreignent la période pendant laquelle l'émetteur est prêt à se porter garant de la clé publique du sujet.
  • public abstract BigInteger getSerialNumber()renvoie le numéro de série du certificat. La combinaison du nom de l'émetteur et du numéro de série d'un certificat constitue son identification unique. Ce fait est crucial pour la révocation des certificats, dont je parlerai plus en détail le mois prochain.
  • public abstract String getSigAlgName()et public abstract String getSigAlgOID()renvoyer des informations sur l'algorithme utilisé pour signer le certificat.

Les méthodes suivantes renvoient des informations sur les extensions définies pour le certificat. N'oubliez pas que les extensions sont des mécanismes permettant d'associer des informations à un certificat; ils n'apparaissent que sur les certificats de la version 3.

  • public abstract int getBasicConstraints()renvoie la longueur du chemin des contraintes d'un certificat à partir de l' BasicConstraintsextension, si elle est définie. Le chemin des contraintes spécifie le nombre maximal de certificats d'autorité de certification pouvant suivre ce certificat dans un chemin de certification.
  • public abstract boolean [] getKeyUsage()renvoie le but du certificat tel que codé dans l' KeyUsageextension.
  • public Set getCriticalExtensionOIDs()et public Set getNonCriticalExtensionOIDs()renvoyer une collection d'identificateurs d'objets (OID) pour les extensions marquées respectivement critiques et non critiques. Un OID est une séquence d'entiers qui identifie universellement une ressource.

Je ne veux pas vous laisser sans code avec lequel jouer, donc plutôt que de fouiller dans les CRL, qui est un sujet complet en soi, je vais présenter le code et laisser les CRL pour la partie 3.

Le code

La classe suivante montre comment obtenir une fabrique de certificats, comment utiliser cette fabrique pour générer un certificat à partir de la représentation codée DER dans un fichier et comment extraire et afficher des informations sur le certificat. Vous remarquerez à quel point vous n'avez pas à vous soucier de l'encodage sous-jacent.