Mémo d'utilisation de JCA (Java Encryption Architecture)

Qu'est-ce que JCA

Abréviation de Java Cryptography Architecture. API, framework pour l'utilisation de la cryptographie en Java.

environnement

Java openjdk 11.0.2

OS Windows 10 (64bit)

Connaissances préalables

Pour utiliser JCA, vous avez besoin de connaissances de base sur la technologie cryptographique (de quel type de technologie vous disposez, de quel type de mécanisme il s'agit, etc.). Si vous ne le savez pas, vous risquez de ne pas comprendre la signification de la structure de classe et de son utilisation correcte. Dans le pire des cas, il peut être utilisé de manière problématique dans des situations d'utilisation réelles, et une faille de sécurité peut être intégrée.

Donc, tout d'abord, il est nécessaire d'étudier la technologie de cryptage en premier lieu.

Voir ici pour une description de la cryptographie.

Fonctions prédéfinies

Jshell est utilisé pour la vérification. Les processus fréquemment utilisés sont définis comme des fonctions à l'avance et utilisés sans aucune explication particulière.

Fonctions prédéfinies


import java.nio.*
import java.nio.file.*
import java.io.*

//Convertir un tableau d'octets en chaîne en notation hexadécimale
String toHexString(byte[] bytes) {
    ByteBuffer buffer = ByteBuffer.wrap(bytes);
    StringBuilder sb = new StringBuilder();
    for (int i=0; i<bytes.length; i+=4) {
        sb.append(String.format("%08x", buffer.getInt()));
    }
    return sb.toString();
}

//Sortie du tableau d'octets dans un fichier
void writeFile(String path, byte[] bytes) throws IOException {
    Files.write(Paths.get(path), bytes);
}

//Lire le fichier sous forme de tableau d'octets
byte[] readFile(String path) throws IOException {
    return Files.readAllBytes(Paths.get(path));
}

Remarques

Puisque la vérification d'opération est effectuée par JShell, le close () du flux d'entrée / sortie est omis pour simplifier la description. N'oubliez pas de faire close () lorsque vous l'utilisez réellement.

Mécanisme de base de JCA

Concept design

JCA est conçu avec la politique de base de réaliser ce qui suit.

  1. Indépendance de la mise en œuvre et interopérabilité
  2. Indépendance et extensibilité des algorithmes

Indépendance de la mise en œuvre

Les différentes technologies cryptographiques fournies par JCA (cryptage, fonctions de hachage, signatures numériques, etc ...) consistent en de multiples implémentations dues à diverses circonstances (comme la loi américaine et l'émergence de nouvelles technologies cryptographiques). Il y a. Ce composant qui fournit une implémentation de la cryptographie est appelé un ** fournisseur de services de chiffrement ** (ou simplement un fournisseur).

L'indépendance de la mise en œuvre consiste à vous faire ignorer l'existence de fournisseurs qui fournissent ces implémentations.

En d'autres termes, si vous déclarez simplement "Je veux utiliser le code" sans ** "Je veux utiliser le code, donc je le supporte ... Spécifiez le fournisseur" ** Après cela, JCA trouvera un fournisseur qui le prend en charge et résoudra la classe d'implémentation.

Interopérabilité de la mise en œuvre

L'interopérabilité de la mise en œuvre fait référence à la capacité de combiner plusieurs fournisseurs.

En d'autres termes, utilisez le fournisseur pour implémenter la fonction de hachage, utilisez le fournisseur pour générer des nombres aléatoires, utilisez le fournisseur *** pour la génération de clé et utilisez le fournisseur @@@ pour la signature numérique et le chiffrement. Tu peux le faire.

Indépendance de l'algorithme

Habituellement, il existe plusieurs algorithmes dans une seule technique cryptographique. Par exemple, pour les fonctions de hachage (technologie cryptographique), il existe plusieurs algorithmes tels que SHA-1, SHA-2, SHA-3 et MD5.

L'indépendance de l'algorithme signifie que la technologie cryptographique peut être utilisée sans dépendre d'un algorithme spécifique.

Par exemple, la fonction de hachage est abstraite par une classe appelée «MessageDigest». Cette classe définit les API qui ne dépendent pas d'un algorithme particulier. Il est nécessaire de spécifier l'algorithme lors de la création d'une instance de MessageDigest, mais le processus de hachage ultérieur peut être réalisé avec la même implémentation quel que soit l'algorithme.

Extensibilité de l'algorithme

L'extensibilité des algorithmes consiste à faciliter l'ajout et l'utilisation de nouveaux algorithmes cryptographiques lorsqu'ils sont inventés et mis en œuvre.

Fournisseur de services de chiffrement

Un composant qui fournit une implémentation d'une ou plusieurs techniques cryptographiques est appelé un ** fournisseur de services de chiffrement **. Lorsque nous disons simplement "fournisseur" dans le contexte de JCA, nous entendons un fournisseur de services de chiffrement.

Au moins un fournisseur est installé dans le JDK. Les fournisseurs peuvent être ajoutés de manière statique ou dynamique.

Les fournisseurs réels installés par défaut dans le JDK sont décrits ci-dessous (étant donné qu'il s'agit d'un document Oracle Java, pourrait-il être différent d'OpenJDK?). Document du fournisseur JDK

Rechercher un fournisseur

Lors de la création d'une instance d'une classe liée à une technique cryptographique spécifique, spécifiez un algorithme pour créer l'instance.

Par exemple, pour créer une instance de MessageDigest en spécifiant l'algorithme SHA-256, implémentez comme suit.

jshell


jshell> import java.security.*

jshell> var md = MessageDigest.getInstance("SHA-256")
md ==> SHA-256 Message Digest from SUN, <initialized>

JCA demande ensuite aux fournisseurs installés un par un, "SHA-256 prend-il en charge MessageDigest?" Il obtient ensuite l'implémentation concrète du premier fournisseur pris en charge trouvé.

Cela permet aux applications d'utiliser la technologie cryptographique sans connaître le fournisseur spécifique. Il est possible d'obtenir une instance en spécifiant le nom du fournisseur, mais ce n'est pas recommandé.

Vérifiez les fournisseurs installés dans l'environnement d'exécution

Une classe appelée java.security.Provider est préparée en tant que classe représentant le fournisseur. ing.

Tous les Providers installés dans l'environnement d'exécution actuel sont getProviders () de java.security.Security. Il peut être obtenu avec java.base / java / security / Security.html # getProviders ()).

jshell


jshell> import java.util.stream.*

jshell> Stream.of(Security.getProviders()).forEach(System.out::println)
SUN version 11
SunRsaSign version 11
SunEC version 11
SunJSSE version 11
SunJCE version 11
SunJGSS version 11
SunSASL version 11
XMLDSig version 11
SunPCSC version 11
JdkLDAP version 11
JdkSASL version 11
SunMSCAPI version 11
SunPKCS11 version 11

Si vous souhaitez y faire référence en tant que document, 4 JDK Provider Document Je pense que vous devriez regarder autour de vous.

Classe de moteur

Une classe qui fournit une technologie cryptographique spécifique est appelée une ** classe de moteur **.

Les classes de moteurs spécifiques incluent (seulement certaines):

classe Fonctions à fournir
SecureRandom Génération de nombres aléatoires imprévisibles pour la cryptographie
MessageDigest Fonction de hachage pour le cryptage
Signature Création et vérification de signature numérique
Cipher Cryptage et décryptage
Mac Code de vérification du message
KeyGenerator Génération de clé privée
KeyPairGenerator Génération de paires de clés
KeyStore Magasin de clés qui gère les clés

Obtenez une instance

jshell


jshell> var md = MessageDigest.getInstance("SHA-256")
md ==> SHA-256 Message Digest from SUN, <initialized>

La classe de moteur fournit une méthode de fabrique statique appelée getInstance (). En spécifiant le nom de l'algorithme dans l'argument de cette méthode de fabrique, vous pouvez obtenir l'instance qui implémente l'algorithme.

Notez que le nom de l'algorithme est ** insensible **.

Algorithmes pris en charge par spécification

Chaque classe de moteur a un algorithme qui doit être pris en charge par la spécification.

Les algorithmes qui doivent être pris en charge sont décrits dans la Javadoc pour chaque classe de moteur.

Par exemple, dans le cas de la classe MessageDigest, les trois suivants sont requis à partir de Java 11.

MessageDigest (Java SE 11 & JDK 11 )

Vérifiez les algorithmes pris en charge par le fournisseur de l'environnement d'exécution

jshell


jshell> var provider = Security.getProviders()[0]
provider ==> SUN version 11

jshell> var services = provider.getServices()
services ==> [SUN: SecureRandom.DRBG -> sun.security.provider. ... ImplementedIn=Software}
]

jshell> services.stream().map(s -> s.getType() + ": " + s.getAlgorithm()).forEach(System.out::println)
SecureRandom: DRBG
SecureRandom: SHA1PRNG
Signature: SHA1withDSA
Signature: NONEwithDSA
Signature: SHA224withDSA
Signature: SHA256withDSA
Signature: SHA1withDSAinP1363Format
Signature: NONEwithDSAinP1363Format
Signature: SHA224withDSAinP1363Format
Signature: SHA256withDSAinP1363Format
KeyPairGenerator: DSA
MessageDigest: MD2
MessageDigest: MD5
MessageDigest: SHA
MessageDigest: SHA-224
MessageDigest: SHA-256
MessageDigest: SHA-384
MessageDigest: SHA-512
MessageDigest: SHA-512/224
MessageDigest: SHA-512/256
MessageDigest: SHA3-224
MessageDigest: SHA3-256
MessageDigest: SHA3-384
MessageDigest: SHA3-512
AlgorithmParameterGenerator: DSA
AlgorithmParameters: DSA
KeyFactory: DSA
CertificateFactory: X.509
KeyStore: PKCS12
KeyStore: JKS
KeyStore: CaseExactJKS
KeyStore: DKS
Policy: JavaPolicy
Configuration: JavaLoginConfig
CertPathBuilder: PKIX
CertPathValidator: PKIX
CertStore: Collection
CertStore: com.sun.security.IndexedCollection

Java.security.Provider.Service est une classe qui représente les fonctions cryptographiques individuelles fournies par le fournisseur. Il existe une classe appelée security / Provider.Service.html). Ce «Service» peut être obtenu à partir des diverses méthodes Getter fournies par le «Fournisseur».

Service fournit une méthode pour obtenir des informations sur les techniques cryptographiques qu'il prend en charge. Par exemple, depuis getType (), " Vous pouvez obtenir le type de technologie cryptographique pris en charge par le service, comme MessageDigest " getAlgorithm () À partir de la méthode, " Vous pouvez obtenir le nom d'un algorithme spécifique, tel que SHA-256 ".

Si vous souhaitez y faire référence en tant que document, utilisez Nom de l'algorithme Java Security Standard. Je pense que vous devriez le voir.

Si vous souhaitez vérifier les algorithmes pris en charge pour chaque fournisseur spécifique, [JDK Provider Document](https://docs.oracle.com/javase/jp/11/security/oracle-providers.html#GUID- FE2D2E28-C991-4EF9-9DBE-2A4982726313) Je pense que vous devriez le regarder.

API indépendante de l'algorithme

La classe de moteur fournit l'API liée à la technologie cryptographique fournie par la classe d'une manière indépendante de l'algorithme.

En d'autres termes, SHA-256 et MD5 peuvent générer des valeurs de hachage avec la même implémentation en utilisant MessageDigest.

jshell


//Calculer la valeur de hachage avec MD5
jshell> var md5 = MessageDigest.getInstance("MD5")
md5 ==> MD5 Message Digest from SUN, <initialized>

jshell> md5.update("hoge".getBytes())

jshell> md5.digest()
$7 ==> byte[16] { -22, 112, 62, 122, -95, -17, -38, 0, 100, -22, -91, 7, -39, -24, -85, 126 }

// SHA-Calculer la valeur de hachage à 256
jshell> var sha256 = MessageDigest.getInstance("SHA-256")
sha256 ==> SHA-256 Message Digest from SUN, <initialized>

jshell> sha256.update("hoge".getBytes())

jshell> sha256.digest()
$10 ==> byte[32] { -20, -74, 102, -41, 120, 114, 94, -55, 115, 7, 4, 77, 100, 43, -12, -47, 96, -86, -69, 118, -11, 108, 0, 105, -57, 30, -94, 91, 30, -110, 104, 37 }

↑ est un exemple de jshell générant des valeurs de hachage avec MD5 et SHA-256. L'implémentation de la partie qui calcule la valeur de hachage est la même pour les deux algorithmes, seul l'algorithme spécifié lors de la création d'une instance de MessageDigest est différent.

Pourquoi le paquet est divisé en deux

Les classes fournies par JCA sont en grande partie le package javax.crypto. java.security Il est fourni séparément dans les packages.

Il y a une raison historique à cela. Il fut un temps où les États-Unis limitaient sévèrement l'exportation de la cryptographie.

[Restrictions d'exportation cryptographiques des États-Unis-Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%A1%E3%83%AA%E3%82%AB%E5 % 90% 88% E8% A1% 86% E5% 9B% BD% E3% 81% 8B% E3% 82% 89% E3% 81% AE% E6% 9A% 97% E5% 8F% B7% E3% 81 % AE% E8% BC% B8% E5% 87% BA% E8% A6% 8F% E5% 88% B6)

La structure du package JCA est conforme à ce règlement. Le package java.security contient des classes pour les technologies exportables ( MessageDigest et Signature), et le package javax.crypto contient des classes pour les technologies non exportables ( Cipher et KeyAgreement). Il est mis en place.

Il semble que les fournisseurs étaient également divisés en fonction de ceux-ci, [SUN Provider](https://docs.oracle.com/javase/jp/11/security/oracle-providers.html#GUID-3A80CC46-91E1-4E47- AC51-CB7B782CEA7D) fournit la fonctionnalité fournie par java.security dans SunJCE Provider -593C-4C38-A0D0-68FA7681E0A7) semble implémenter chacune des fonctions fournies par javax.crypto.

Lorsqu'il était strictement réglementé, SunJCE semblait être proposé en tant qu'extension, mais maintenant il est détendu et associé au JDK.

référence:Cryptage Java| 1.Sécurité générale|Guide du développeur de sécurité

Résumé du message (hachage)

Utilisez la classe java.security.MessageDigest pour utiliser la fonction de hachage Faire.

jshell


jshell> var md = MessageDigest.getInstance("SHA-256")
md ==> SHA-256 Message Digest from SUN, <initialized>

jshell> byte[] hash = md.digest("hello world".getBytes())
hash ==> byte[32] { -71, 77, 39, -71, -109, 77, 62, 8, -91 ...  -84, -30, -17, -51, -23 }

digest () `La valeur que vous souhaitez hacher dans la méthode Si vous passez un tableau d'octets, la valeur de hachage sera renvoyée sous forme de tableau d'octets.

Entrée fractionnée

jshell


jshell> md.update("hello".getBytes())

jshell> md.update(" world".getBytes())

jshell> var hash = md.digest()
hash ==> byte[32] { -71, 77, 39, -71, -109, 77, 62, 8, -91 ...  -84, -30, -17, -51, -23 }

update () Utilisez la méthode pour saisir Peut être divisé en plusieurs parties.

Si vous deviez saisir tous les octets en même temps, vous devrez mettre toutes les données dans un tableau d'octets. Il n'y a pas de problème si la taille des données de la cible de hachage est petite, mais si vous voulez hacher un gros fichier, etc., toutes les données seront développées sur la mémoire, donc ce sera strict. Dans un tel cas, en passant les données d'entrée à ʻupdate () `petit à petit, il n'est pas nécessaire de lire toutes les données dans la mémoire à la fois.

L'exécution de digest () initialise l'état de MessageDigest, de sorte que l'instance peut être réutilisée.

Convertir en chaîne hexadécimale

Le résultat de digest () est un tableau byte, il est donc difficile à comprendre tel quel. Une chaîne de caractères hexadécimaux est souvent utilisée comme représentation sous forme de chaîne de caractères de la valeur de hachage, essayez donc de la convertir.

jshell


// ※toHashString()Est une fonction prédéfinie (voir haut de page)
jshell> toHexString(hash)
$6 ==> "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"

Calculer la valeur de hachage du fichier

Si vous voulez calculer la valeur de hachage à partir d'un fichier, vous pouvez prendre les données de FileInputStream et les saisir dans MessageDigest par vous-même, mais une classe est fournie pour faciliter l'implémentation.

Calculons en fait la valeur de hachage SHA-256 du fichier zip de la version Windows d'OpenJDK 12.

jshell


jshell> var md = MessageDigest.getInstance("SHA-256")
md ==> SHA-256 Message Digest from SUN, <initialized>

//Générer InputStream du fichier
jshell> var in = new BufferedInputStream(new FileInputStream("openjdk-12_windows-x64_bin.zip"))
in ==> java.io.BufferedInputStream@10bbd20a

//Générer DigestInputStream
jshell> var dis = new DigestInputStream(in, md)
dis ==> [Digest Input Stream] SHA-256 Message Digest from SUN, <initialized>

//Lire toutes les informations depuis InputStream (nullOutputStream car aucun résultat de lecture n'est nécessaire()Désinvolte)
jshell> dis.transferTo(OutputStream.nullOutputStream())
$31 ==> 196405895

//Calculer la valeur de hachage
jshell> var hash = md.digest()
hash ==> byte[32] { 53, -88, -48, 24, -12, 32, -5, 5, -2,  ... -13, -119, 124, 78, -110 }

Chaque fois que DigestInputStream lit les données d'un flux, les données lues sont transmises à ʻupdate () ʻof MessageDigest`.

jshell


jshell> var md = MessageDigest.getInstance("SHA-256")
md ==> SHA-256 Message Digest from SUN, <initialized>

//Générer InputStream du fichier
jshell> var in = new BufferedInputStream(new FileInputStream("openjdk-12_windows-x64_bin.zip"))
in ==> java.io.BufferedInputStream@5a1c0542

//Générer DigestOutputStream (nullOutputStream car aucune information écrite n'est nécessaire()Désinvolte)
jshell> var dos = new DigestOutputStream(OutputStream.nullOutputStream(), md)
dos ==> [Digest Output Stream] SHA-256 Message Digest from SUN, <initialized>

//Lire toutes les informations d'InputStream et écrire dans DigestOutputStream
jshell> in.transferTo(dos)
$24 ==> 196405895

//Calculer la valeur de hachage
jshell> var hash = md.digest()
hash ==> byte[32] { 53, -88, -48, 24, -12, 32, -5, 5, -2,  ... -13, -119, 124, 78, -110 }

D'un autre côté, DigestOutputStream est passé à ʻupdate ()deMessageDigest` chaque fois que des données sont écrites dans le flux.

À propos, si vous convertissez la valeur de hachage calculée en une chaîne hexadécimale,

jshell


jshell> toHexString(hash)
$12 ==> "35a8d018f420fb05fe7c2aa9933122896ca50bd23dbd373e90d8e2f3897c4e92"

Valeur de hachage publiée sur le site OpenJDK est `35a8d018f420fb05fe7c2aa9933122896ca50bd23de7c2aa9933122896ca50bd23dbde373e9038d Peut être calculé.

clé

Il existe différentes "clés" en cryptographie (clé privée, clé publique, clé privée, etc ...). JCA définit également des types qui représentent différentes clés en conséquence (SecretKey, PublicKey, PrivateKey, etc ...).

Il existe différents types de clés, mais elles ont toutes une interface commune en tant que parent. Il s'agit de l'interface Key, qui est le type de niveau supérieur qui représente la "clé". Sera.

Dans Key, trois méthodes d'accès aux informations clés sont définies.

  1. getAlgorithm()
  1. getEncoded()
  1. getFormat()

Spécifications clés

Des informations telles que des algorithmes peuvent être obtenues à partir de "Key", mais les données spécifiques qui composent la clé ne peuvent pas être obtenues. L'incapacité d'accéder à des données clés spécifiques de cette manière est appelée ** expression opaque ** par JCA.

D'autre part, il existe également des types qui peuvent accéder aux données spécifiques qui composent la clé. Il s'agit de KeySpec (spécification de clé).

Puisque le but de «KeySpec» lui-même est d'exprimer que «ce type est une spécification de clé», l'API pour accéder réellement aux données de clé n'est pas définie. L'API réelle est définie dans la classe d'implémentation KeySpec.

Par exemple, DSAPrivateKeySpec affiche le nombre premier utilisé dans la génération de clé. Vous pouvez maintenant accéder aux informations sur $ p $ et la clé privée $ x $.

L'accès aux données spécifiques qui composent la clé de cette manière est appelé ** représentation transparente ** par JCA.

Les véritables Key et KeySpec et certaines hiérarchies de classes ressemblent à ceci:

jca.png

Générateur et usine

JCA propose deux méthodes pour créer une clé: Generator et Factory.

Generator offre la possibilité de créer une nouvelle clé. Par exemple, vous pouvez générer une nouvelle clé en spécifiant des paramètres tels que la longueur de clé.

D'autre part, «Factory» fournit principalement la fonction de conversion entre les clés et les spécifications de clé. Il semble que certaines clés peuvent générer la même clé à partir de deux spécifications de clé différentes.

Plus précisément, les classes suivantes existent.

Crypter décrypter

Le cryptage par clé commune et le processus de cryptage / décryptage de clé publique sont javax.crypto.Cipher. /crypto/Cipher.html) Utilisez la classe.

Spécification d'algorithme de chiffrement

jshell


jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher ==> Cipher.AES/CBC/PKCS5Padding, mode: not initialize ... orithm from: (no provider)

La chaîne de caractères passée à getInstance () de Cipher n'est pas un simple nom d'algorithme, mais le mode de chiffrement par bloc, etc. peut également être spécifié.

Le format spécifique est «algorithme / mode / remplissage».

ʻAlgorithmest le nom d'un algorithme de chiffrement tel que AES, DES ou RSA. modeest un mode de chiffrement par bloc qui spécifie ECB, CBC, etc. padding` spécifie comment remplir les données manquantes lorsque les données à chiffrer ne sont pas un multiple entier de la taille du bloc.

Il est également possible de spécifier uniquement «algorithme», tel que «Cipher.getInstance (« AES »)». Dans ce cas, les valeurs par défaut déterminées par le fournisseur sont utilisées pour le mode et le remplissage. Dans la plupart des cas, ** le mode par défaut sera ECB **, c'est donc toujours une bonne idée de spécifier le mode et le remplissage.

Initialisation

Pour commencer à utiliser Cipher, vous devez d'abord l'initialiser.

Pour l'initialisation, spécifiez "type de traitement à effectuer" et "paramètres pour cela". Par exemple, "cryptage" et "clé" seront transmis pour l'initialisation.

jshell


//Générer la clé
jshell> var keyGen = KeyGenerator.getInstance("AES")
keyGen ==> javax.crypto.KeyGenerator@365185bd

jshell> var key = keyGen.generateKey()
key ==> javax.crypto.spec.SecretKeySpec@fffe87d2

//Initialisation du chiffrement
jshell> cipher.init(Cipher.ENCRYPT_MODE, key)

//Obtenir les paramètres d'initialisation
jshell> var params = cipher.getParameters()
params ==>
    iv:
[0000: F1 D7 23 45 DA A6 7B 42   66 AF ...  46  ..#E...Bf.z..a.F
]

Utilisez la méthode ʻinit () `pour l'initialisation.

Spécifiez le type d'initialisation dans le premier argument. Le type d'initialisation est [Constantes définies dans Cipher](https://docs.oracle.com/javase/jp/11/docs/api/java.base/javax/crypto/Cipher.html#field. résumé) est utilisé. ʻENCRYPT_MODEest en mode de chiffrement etDECRYPT_MODE` est en mode de déchiffrement.

Les paramètres d'initialisation sont passés après le deuxième argument. En gros, spécifiez la clé utilisée pour le cryptage / décryptage.

Des paramètres supplémentaires peuvent être requis en fonction de l'algorithme de cryptage. Dans ce cas, recevez des paramètres supplémentaires tels que ʻAlgorithmParameterSpec` [init ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/javax/crypto/Cipher.html Utilisez la méthode #init (int, java.security.Key, java.security.spec.AlgorithmParameterSpec)). AlgorithmParameterSpec est une interface qui représente des paramètres spécifiques à l'algorithme. Il existe des classes d'implémentation spécifiques à divers algorithmes de chiffrement.

Même pour les algorithmes qui nécessitent des paramètres supplémentaires, le fournisseur peut définir des valeurs par défaut pour vous. Dans ce cas, il ne peut être initialisé que par la méthode ʻinit () `qui reçoit le mode de traitement et la clé. Les paramètres utilisés à l'heure actuelle sont getParameters () Il peut être obtenu par la méthode.

jshell


//Initialisation du chiffrement
jshell> cipher.init(Cipher.ENCRYPT_MODE, key)

//Obtenir les paramètres d'initialisation
jshell> var params = cipher.getParameters()
params ==>
    iv:
[0000: F1 D7 23 45 DA A6 7B 42   66 AF ...  46  ..#E...Bf.z..a.F
]

Ici, la clé pour AES est générée et «Cipher» est initialisé en mode crypté.

Etant donné que le mode au moment de la génération de "Cipher" était réglé sur CBC, le paramètre du vecteur d'initialisation (IV: ** I ** nitialization ** V ** ector) est à l'origine requis. Cependant, comme la spécification du vecteur d'initialisation a été omise dans ʻinit () `, le fournisseur a bien généré le vecteur d'initialisation.

Le vecteur d'initialisation est également requis pour l'initialisation du mode de décodage, il doit donc être obtenu avec getParameters (). Si l'algorithme de chiffrement utilisé ne nécessitait aucun paramètre supplémentaire, getParameters () renvoie null.

L'initialisation réinitialise tous les états internes de Cipher. En d'autres termes, une instance de «Cipher» qui était autrefois utilisée pour le chiffrement peut maintenant être réutilisée pour le déchiffrement. À l'inverse, si vous vous initialisez en mode de déchiffrement pendant le chiffrement, les informations définies pendant le processus de chiffrement seront perdues, alors soyez prudent.

Crypter décrypter

jshell


jshell> cipher.doFinal("Java Cryptography Architecture".getBytes())
$63 ==> byte[32] { -24, 54, 9, 79, -2, 118, 101, 69, -63, 30, -104, 77, -21, -24, -28, -4, 31, -56, -125, -121, 24, -115, 55, -92, -68, -35, -6, 90, -108, -122, -16, 21 }

doFinal () Chiffrer vers la méthode En passant la valeur que vous souhaitez convertir / déchiffrer (tableau byte), vous pouvez obtenir le texte chiffré / brut (tableau byte).

Similaire à MessageDigest, l'entrée est [update ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/javax/crypto/Cipher.html#update (byte) Il peut également être divisé en utilisant% 5B% 5D)). Cependant, contrairement au cas de MessageDigest, le résultat du chiffrement / déchiffrement est renvoyé à chaque fois ʻupdate ()`.

jshell


jshell> cipher.update("Java ".getBytes())
$59 ==> byte[0] {  }

jshell> cipher.update("Cryptography ".getBytes())
$60 ==> byte[16] { -24, 54, 9, 79, -2, 118, 101, 69, -63, 30, -104, 77, -21, -24, -28, -4 }

jshell> cipher.update("Architecture".getBytes())
$61 ==> byte[0] {  }

jshell> cipher.doFinal()
$62 ==> byte[16] { 31, -56, -125, -121, 24, -115, 55, -92, -68, -35, -6, 90, -108, -122, -16, 21 }

De plus, si vous utilisez le chiffrement par bloc, un tableau d'octets vide est renvoyé jusqu'à ce que la taille d'entrée corresponde à la taille du bloc. Pour AES, la taille du bloc est de 128 bits (16 octets), donc la valeur de retour de ʻupdate () `sera un tableau vide jusqu'à ce que l'entrée atteigne 16 octets.

Quand doFinal () est appelé, l'instance de Cipher revient à la dernière initialisation. Autrement dit, il peut être réutilisé pour crypter / décrypter une autre entrée telle quelle.

CipherInputStream/CipherOutputStream Semblable à DigestInputStream / DigestOutputStream, il existe une classe qui relie Cipher et ʻInputStream / ʻOutputStream.

L'image de chaque donnée et le flux de cryptage / décryptage sont illustrés dans la figure ci-dessous.

jca.jpg

Tout d'abord, lors de l'utilisation de CipherInputStream.

jshell


//Créer un InputStream à chiffrer
jshell> var is = new ByteArrayInputStream("Java Cryptography Architecture".getBytes())
is ==> java.io.ByteArrayInputStream@5fcd892a

//Créer CipherInputStream
jshell> var cis = new CipherInputStream(is, cipher)
cis ==> javax.crypto.CipherInputStream@b9afc07

//Créer OutputStream de la destination de sortie
jshell> var out = new ByteArrayOutputStream()
out ==>

//Extrait toutes les informations d'InputStream et écrit le résultat du chiffrement dans OutputStream
jshell> cis.transferTo(out)
$67 ==> 32

//Vérifiez le résultat écrit dans OutputStream
jshell> out.toByteArray()
$68 ==> byte[32] { -24, 54, 9, 79, -2, 118, 101, 69, -63, 30, -104, 77, -21, -24, -28, -4, 31, -56, -125, -121, 24, -115, 55, -92, -68, -35, -6, 90, -108, -122, -16, 21 }

Puis lors de l'utilisation de CipherOutputStream.

jshell


//Créer un InputStream à chiffrer
jshell> var is = new ByteArrayInputStream("Java Cryptography Architecture".getBytes())
is ==> java.io.ByteArrayInputStream@133e16fd

//Créer OutputStream pour sortir le chiffrement
jshell> var out = new ByteArrayOutputStream()
out ==>

//Créer CipherOutputStream
jshell> var cos = new CipherOutputStream(out, cipher)
cos ==> javax.crypto.CipherOutputStream@51b279c9

//Versez la cible de chiffrement dans CipherOutputStream
jshell> is.transferTo(cos)
$92 ==> 30

// doFinal()Fermez CipherOutputStream pour exécuter()
jshell> cos.close()

//Vérifiez le code
jshell> out.toByteArray()
$94 ==> byte[32] { -24, 54, 9, 79, -2, 118, 101, 69, -63, 30, -104, 77, -21, -24, -28, -4, 31, -56, -125, -121, 24, -115, 55, -92, -68, -35, -6, 90, -108, -122, -16, 21 }

Pour «CipherInputStream» et «CipherOutputStream», «doFinal ()» de «Cipher» est exécuté à «close ()». Donc, si vous n'oubliez pas d'exécuter close (), le résultat sera à mi-chemin [^ 1](Puisqu'il s'agit d'un flux, vous n'avez pas à vous inquiéter car il utilise un try-with-resources de base, etc. Je pense).

[^ 1]: CipherInputStream n'est pas à moitié fini même sans close (), mais l'implémentation semble êtredoFinal ()lorsque le flux d'entrée atteint la fin.

TransferTo () ne devrait-il pas être utilisé?

Le Javadoc de CipherInputStream dit: ..

Les programmeurs utilisant cette classe n'utiliseront jamais de méthodes qui ne sont pas définies dans cette classe ou qui n'ont pas été remplacées (telles que les nouvelles méthodes ou les constructeurs qui ont été ajoutés ultérieurement à l'une des superclasses). S'il vous plaît **. La conception et la mise en œuvre de ces méthodes peuvent ne pas prendre en compte les implications de sécurité de CipherInputStream.

D'autre part, pour ʻInputStream`, en Java 9, transferTo () a été ajoutée.

Ce transferTo () n'est pas écrasé par CipherInputStream, donc il remplit les conditions qui ne devraient pas être utilisées.

Cependant, ce transferTo () est implémenté dans OpenJDK 11 comme suit.

InputStream.java


    public long transferTo(OutputStream out) throws IOException {
        Objects.requireNonNull(out, "out");
        long transferred = 0;
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        int read;
        while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
            out.write(buffer, 0, read);
            transferred += read;
        }
        return transferred;
    }

Seuls read () de ʻInputStream et write () ʻ de ʻOutputStream` sont utilisés ici.

Si vous essayez d'écrire un processus sans utiliser transferTo (), vous finissez par écrire la même implémentation que ce transferTo (). Et la méthode utilisée à ce moment-là est read () remplacée par CipherInputStream ou write () ʻof CipherOutputStream`.

Ensuite, je pense personnellement qu'il n'y a aucun problème à utiliser transferTo () après tout.

Cependant, puisque cette implémentation est limitée à OpenJDK, il est possible que d'autres implémentations Java aient des problèmes de sécurité.

À utiliser à vos risques et périls.

AES Essayez d'implémenter le cryptage et le décryptage AES.

Génération de clés

jshell


//Créer KeyGenerator avec l'algorithme AES
jshell> var keyGen = KeyGenerator.getInstance("AES")
keyGen ==> javax.crypto.KeyGenerator@4d50efb8

//Génération de clés
jshell> var key = keyGen.generateKey()
key ==> javax.crypto.spec.SecretKeySpec@177c6

Utilisez KeyGenerator pour générer la clé. Pour les fournisseurs SunJCE, la longueur de clé par défaut est de 128 bits.

Spécifier la longueur de la clé

jshell


jshell> keyGen.init(256)

Vous pouvez spécifier la longueur de la clé avec la méthode ʻinit () `. Seules 128, 192 ou 256 longueurs de clé peuvent être spécifiées (spécification AES).

chiffrement

jshell


//Générer un chiffrement avec AES
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher ==> Cipher.AES/CBC/PKCS5Padding, mode: not initialize ... orithm from: (no provider)

//Initialiser en mode crypté
jshell> cipher.init(Cipher.ENCRYPT_MODE, key)

//Obtenir des informations sur le vecteur d'initialisation
jshell> var params = cipher.getParameters()
params ==>
    iv:
[0000: 88 8E 06 A0 89 80 0D 11   FF ED ...  45  ...........&.'.E
]

//chiffrement
jshell> var c = cipher.doFinal("Java Cryptography Architecture".getBytes())
c ==> byte[32] { -35, -80, -57, 91, -75, -46, -123, 49, ...  22, 90, -121, 124, -108 }

//Vérifiez s'il est crypté
jshell> new String(c)
$24 ==> "Religieuse[Ome?1ju Sehi "Li"\017}Première fois PW »\026Z?|?"

Puisque les informations de vecteur d'initialisation sont requises au moment du décodage, obtenez les informations de paramètre avec getParameters ().

Décryptage

jshell


//Initialiser en mode de décryptage
jshell> cipher.init(Cipher.DECRYPT_MODE, key, params)

//Décryptage
jshell> var p = cipher.doFinal(c)
p ==> byte[30] { 74, 97, 118, 97, 32, 67, 114, 121, 112 ... , 99, 116, 117, 114, 101 }

//Vérifiez s'il peut être déchiffré
jshell> new String(p)
$23 ==> "Java Cryptography Architecture"

Lorsque ʻinit () `, vous devez spécifier non seulement la clé mais aussi les paramètres utilisés pour initialiser le chiffrement.

Exporter la clé dans un fichier

jshell


//Clé de sortie vers fichier
jshell> writeFile("secret-key", key.getEncoded())

//Sortie des paramètres utilisés lors du cryptage dans un fichier
jshell> writeFile("aes-params", params.getEncoded())

//Génération cryptographique
jshell> var c = cipher.doFinal("Hello World!!".getBytes())
c ==> byte[16] { -77, 124, 124, -4, -92, 80, -89, 102,  ... 1, 114, 7, 117, -104, 11 }

//Code de sortie dans un fichier
jshell> writeFile("aes-cryptogram", c)

Les données encodées de la clé privée sont [getEncoded ()] de Key (https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/Key.html# Il peut être obtenu avec getEncoded ()). Pour plus d'informations sur les paramètres, consultez getEncoded () dans ʻAlgorithmParameters`. )) peut être obtenu.

Restaurer la clé à partir du fichier

jshell


//Lisez les informations de clé secrète du fichier et générez SecretKeySpec
jshell> Key key = new SecretKeySpec(readFile("secret-key"), "AES")
key ==> javax.crypto.spec.SecretKeySpec@178a8

//Obtenez une instance d'AlgorithmParameters et initialisez-la avec les informations lues dans le fichier
jshell> var params = AlgorithmParameters.getInstance("AES")
params ==>

jshell> params.init(readFile("aes-params"))

//Initialiser avec les informations générées et lues par Cipher
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher ==> javax.crypto.Cipher@df27fae

jshell> cipher.init(Cipher.DECRYPT_MODE, key, params)

//Lisez le code du fichier et décryptez-le
jshell> var m = cipher.doFinal(readFile("aes-cryptogram"))
m ==> byte[13] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 33 }

//Vérifiez le résultat
jshell> new String(m)
$34 ==> "Hello World!!"

La clé privée peut être restaurée à l'aide de SecretKeySpec (l'algorithme est ` Spécifiez "AES"). Bien que cette classe soit «KeySpec», elle implémente également «Key», donc elle peut être utilisée comme clé telle quelle.

Les paramètres peuvent être restaurés avec AlgorithmParameters. Tout d'abord, utilisez getInstance () pour Obtenez une instance en spécifiant "AES". Ensuite, avec init () Définissez les données de paramètres.

Cryptage par mot de passe (PBE)

jshell


//Générer KeySpec pour le chiffrement basé sur un mot de passe
jshell> char[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' }
password ==> char[8] { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' }

jshell> var keySpec = new PBEKeySpec(password)
keySpec ==> javax.crypto.spec.PBEKeySpec@5a8e6209

//Générer la clé à partir de KeySpec à l'aide de SecretKeyFactory
jshell> var keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128")
keyFac ==> javax.crypto.SecretKeyFactory@3234e239

jshell> var key = keyFac.generateSecret(keySpec)
key ==> com.sun.crypto.provider.PBEKey@855e49d8

PBE utilise une clé appelée PBEKey.

PBEKey génère PBEKeySpec en fonction du mot de passe. Ensuite, utilisez SecretKeyFactory pour convertir en PBEKey et l'obtenir. ..

Le mot de passe doit être spécifié dans un tableau de «char». La raison est décrite dans le Javadoc de PBEKeySpec, mais il semble qu'il ne puisse pas être effacé plus tard car la valeur est invariante avec String.

Dans l'acquisition d'instance de SecretKeyFactory, PBEWithHmacSHA256AndAES_128 est spécifié dans l'algorithme.

Le nom de l'algorithme est au format PBEWith <digest | prf> And <encryption>. Si digest | prf est MD5 ou SHA1, ce sera PBES1 dans RFC8018, et si c'est Hmac *, ce sera PBES2.

jshell


//Générer un chiffrement pour le chiffrement par mot de passe et initialiser en mode de chiffrement
jshell> var cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_128/CBC/PKCS5Padding")
cipher ==> javax.crypto.Cipher@5891e32e

Cipher obtient également une instance avec le même algorithme. (Le fournisseur SunJCE Cipher ne prend en charge que les modes et le remplissage CBC / PKCS5Padding dans PBE, donc je pense qu'il n'est pas nécessaire de le spécifier explicitement, mais juste au cas où)

jshell


//Créer un générateur de nombres pseudo aléatoires
jshell> var random = new SecureRandom()
random ==> Hash_DRBG,SHA-256,128,reseed_only

//Génération de sel
jshell> var salt = new byte[64];
salt ==> byte[64] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... , 0, 0, 0, 0, 0, 0, 0, 0 }

jshell> random.nextBytes(salt)

Génère du sel.

Salt utilise un générateur de nombres pseudo-aléatoires cryptographiques (SecureRandom) Produire.

Dans RFC8018, la taille du sel est définie sur au moins 64 bits, donc je l'ai générée avec 64 bits pour le moment.

jshell


//Déclaration du nombre d'itérations
jshell> int iterationCount = 1000
iterationCount ==> 1000

//Générer AlgorithmParameterSpec pour le chiffrement basé sur un mot de passe
jshell> var keyParamSpec = new PBEParameterSpec(salt, iterationCount)
keyParamSpec ==> javax.crypto.spec.PBEParameterSpec@eec5a4a

jshell> cipher.init(Cipher.ENCRYPT_MODE, key, keyParamSpec)

Générez PBEParameterSpec en utilisant le nombre d'itérations et la valeur de sel. Faire.

Ensuite, initialisez Cipher avec la clé que vous avez créée précédemment.

jshell


//Exécution du chiffrement
jshell> var c = cipher.doFinal("Hello World!!".getBytes())
c ==> byte[16] { 101, -52, -106, 26, 67, 118, -20, 22,  ...  42, -71, 115, 123, -122 }

//Vérifiez le résultat du cryptage
jshell> new String(c)
$18 ==> "efu?\032Cv?\026\031\000*S{?"

Après cela, le cryptage est effectué de la même manière que les autres algorithmes.

jshell


//Obtenez les paramètres utilisés lors du chiffrement
jshell> var params = cipher.getParameters()
params ==> PBEWithHmacSHA256AndAES_128

jshell> PBEParameterSpec pbeParamSpec = params.getParameterSpec(PBEParameterSpec.class)
pbeParamSpec ==> javax.crypto.spec.PBEParameterSpec@35a50a4c

//Obtenir IvParameterSpec
jshell> IvParameterSpec ivSpec = (IvParameterSpec)pbeParamSpec.getParameterSpec()
ivSpec ==> javax.crypto.spec.IvParameterSpec@281e3708

// IV (Vecteur d'initialisation)Vérifiez la valeur de
jshell> var iv = ivSpec.getIV()
iv ==> byte[16] { -67, 47, 111, -2, 34, -17, -89, 74, -7 ... 46, -124, 99, 79, 23, 88 }

Puisqu'il a été chiffré avec CBC, il est nécessaire d'obtenir le vecteur d'initialisation pour le déchiffrement. Les informations de vecteur d'initialisation (ʻIvParameterSpec) définies automatiquement au moment du chiffrement sont [PBEParameterSpec](https://docs.oracle.com/javase/jp/11/docs/api/java.base/javax/crypto). Il peut être obtenu par la méthode getParameterSpec ()` de /spec/PBEParameterSpec.html).

jshell


//Générer PBEParameterSpec pour le décryptage
jshell> var ivSpec = new IvParameterSpec(iv)
ivSpec ==> javax.crypto.spec.IvParameterSpec@dbd940d

jshell> PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, iterationCount, ivSpec)
pbeParamSpec ==> javax.crypto.spec.PBEParameterSpec@17695df3

//Initialiser en mode décryptage (* la génération de clé est identique au cryptage)
jshell> cipher.init(Cipher.DECRYPT_MODE, key, pbeParamSpec)

//Effectuer le décryptage
jshell> var m = cipher.doFinal(c)
m ==> byte[13] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 33 }

//Vérifiez le résultat du décryptage
jshell> new String(m)
$21 ==> "Hello World!!"

Au moment du déchiffrement, «Key» et «AlgorithmParameterSpec» sont générés à nouveau en utilisant les informations de «mot de passe», «sel» et «nombre d'itérations», et «Cipher» est initialisé. (Étant donné que les informations IV doivent être celles utilisées au moment du cryptage, la génération de PBEParameterSpec est légèrement différente.)

RSA Essayez d'utiliser RSA pour le chiffrement par clé publique.

Génération de clés

jshell


//Obtenez KeyPairGenerator pour générer une paire de clés
jshell> var keyPairGen = KeyPairGenerator.getInstance("RSA")
keyPairGen ==> java.security.KeyPairGenerator$Delegate@42dafa95

//Générer une paire de clés
jshell> var keyPair = keyPairGen.generateKeyPair()
keyPair ==> java.security.KeyPair@1dfe2924

Utilisez KeyPairGenerator pour générer une paire de clés pour le chiffrement de clé publique. Faire. Puisque RSA est utilisé, passez «« RSA »» comme nom d'algorithme.

Lorsque vous exécutez generateKeyPair (), KeyPair contenant l'ensemble de la clé publique et de la clé privée /KeyPair.html) peuvent être obtenus.

Spécifier la longueur de la clé

jshell


jshell> keyPairGen.initialize(4096)

jshell> keyPairGen.generateKeyPair().getPublic()
$9 ==> Sun RSA public key, 4096 bits
  params: null
  modulus: 58218231801097229661983999673649965349728454764714757172625245890343045...
  public exponent: 65537

Clé avec la méthode initialize () de KeyPairGenerator Vous pouvez spécifier la longueur.

chiffrement

jshell


//Obtenir un chiffrement pour RSA
jshell> var cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
cipher ==> javax.crypto.Cipher@35a50a4c

//Initialiser en mode crypté
jshell> cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic())

//Exécution du chiffrement
jshell> var c = cipher.doFinal("Hello World!!".getBytes())
c ==> byte[256] { 44, 48, -56, -42, -117, -48, 17, -76, ... , -15, 63, 46, -115, -42 }

//Vérifiez le résultat du cryptage
jshell> new String(c)
$14 ==> ",0 largeur\021\030?0\022:Li 斃 w?9 ァ,Homme| ノ_? lune rut m à Щa]\002)Ma\024L5XD?齡 訶({x?Shi\024,5, S@wM-?杣 woo g1\025 "02 鷦 Herato J Bonmi?\t ゥ  Rem F F 悊^?\"Miu\023 ゥ&Moho 輙 蓸 子.Mri\037 ku je?9m ED Care gg\023q 亥 X4\021pM3 廱 ヤ\017G\02,4 milliards\017 Donc?;Q\177 Hiver U5wgL B Peu profond>\006e?g?9 l:\t] 垉 R\020 Donc?nmi?!?&s 㽴?Ora\016`H\f6 mariné ?Les?4\nfU?.Sai"

Vous pouvez obtenir la clé publique avec getPublic () of KeyPair, donc cryptez-la. La procédure est la même que pour AES.

Décryptage

jshell


//Récupérez les paramètres générés lors du chiffrement
jshell> var params = cipher.getParameters()
params ==> MD: SHA-256
MGF: MGF1SHA-1
PSource: PSpecified

//Initialiser en mode de décryptage
jshell> cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate(), params)

//Exécution du décryptage
jshell> var m = cipher.doFinal(c)
m ==> byte[13] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 33 }

//Vérifiez le résultat du décryptage
jshell> new String(m)
$18 ==> "Hello World!!"

Vous pouvez obtenir la clé privée avec getPrivate () of KeyPair, alors utilisez-la pour le déchiffrement. Encore une fois, l'utilisation de «Cipher» est la même que pour AES.

Code de vérification de message (MAC)

jshell


//Générer une clé privée pour MAC
jshell> var keyGen = KeyGenerator.getInstance("HmacSHA256")
keyGen ==> javax.crypto.KeyGenerator@17579e0f

jshell> var key = keyGen.generateKey()
key ==> javax.crypto.spec.SecretKeySpec@588390c

//Créer une instance de MAC
jshell> var mac = Mac.getInstance("HmacSHA256")
mac ==> javax.crypto.Mac@7a765367

//Initialiser MAC
jshell> mac.init(key)

//Calculer la valeur MAC
jshell> var mv = mac.doFinal("Hello World!!".getBytes())
mv ==> byte[32] { 0, -103, -60, 37, -74, 97, 115, -50, 7 ... 80, 57, -84, -59, 90, 23 }

Utilisez la classe Mac pour calculer la valeur MAC.

Le MAC nécessite une clé privée, utilisez donc KeyGenerator pour générer la clé.

Après avoir créé une instance de Mac, initialisez-la d'abord avec la méthode ʻinit ()en utilisant la clé privée. Ensuite, passez la valeur (tableaubyte) pour laquelle vous voulez calculer la valeur MAC à doFinal ()`. La valeur MAC calculée est renvoyée sous forme de tableau "byte".

Divisez et calculez

jshell


jshell> mac.update("Hello ".getBytes())

jshell> mac.update("World!!".getBytes())

jshell> mac.doFinal()
$9 ==> byte[32] { 0, -103, -60, 37, -74, 97, 115, -50, 77, 6, 107, 27, 15, -41, -15, 111, -99, -50, -85, -68, 11, -66, 69, -54, 44, -92, 80, 57, -84, -59, 90, 23 }

Similaire à Cipher etc., vous pouvez utiliser ʻupdate ()` pour diviser et définir la valeur à calculer.

Signature numérique

Signature RSA

jshell


//Générer une paire de clés RSA
jshell> var keyPairGen = KeyPairGenerator.getInstance("RSA")
keyPairGen ==> java.security.KeyPairGenerator$Delegate@42dafa95

jshell> var keyPair = keyPairGen.generateKeyPair()
keyPair ==> java.security.KeyPair@1dfe2924

Tout d'abord, générez une paire de clés RSA comme dans le cas du cryptage RSA.

jshell


//Obtenir l'instance Signature
jshell> var signature = Signature.getInstance("SHA256WithRSA")
signature ==> Signature object: SHA256WithRSA<not initialized>

//Initialiser en mode signature
jshell> signature.initSign(keyPair.getPrivate())

//Enregistrer les données à signer
jshell> signature.update("Hello World!!".getBytes())

//Générer une signature numérique
jshell> var sign = signature.sign()
sign ==> byte[256] { 29, -91, 95, 8, -19, -56, 118, -29, 1 ... 7, -1, 53, 58, -99, -117 }

Pour créer une signature numérique, utilisez la classe Signature.

Signature a deux méthodes d'initialisation. L'un est initSign () , L'autre est initVerify () devenir.

Si vous voulez créer une signature, utilisez ʻinitSign () `pour l'initialiser. À ce stade, transmettez la clé de signature (clé privée) utilisée pour la signature en tant qu'argument.

Une fois l'initialisation terminée, l'étape suivante consiste à enregistrer les données à signer avec la méthode ʻupdate () `. Enfin, lorsque vous exécutez la méthode sign (), elle est numérique. La signature est renvoyée dans un tableau «byte».

jshell


//Initialiser la signature en mode vérification
jshell> signature.initVerify(keyPair.getPublic())

//Enregistrer les données à vérifier
jshell> signature.update("Hello World!!".getBytes())

//Effectuer la vérification
jshell> signature.verify(sign)
$12 ==> true

Pour la vérification, initialisez Signature avec ʻinitVerify ()`. À ce stade, une clé de vérification (clé publique) est passée en argument.

Ici aussi, enregistrez les données à vérifier avec ʻupdate () `. Et enfin verify () Effectuez la validation avec la méthode. À ce stade, passez un tableau «byte» de signatures numériques comme argument.

Si la validation réussit, «true» est renvoyé.

Signature DSA

jshell


//Générer une paire de clés pour DSA
jshell> var keyPairGen = KeyPairGenerator.getInstance("DSA")
keyPairGen ==> sun.security.provider.DSAKeyPairGenerator$Current@f6c48ac

jshell> var keyPair = keyPairGen.generateKeyPair()
keyPair ==> java.security.KeyPair@1f36e637

//Obtenir une instance Signature pour DSA
jshell> var signature = Signature.getInstance("SHA256WithDSA")
signature ==> Signature object: SHA256WithDSA<not initialized>

//Initialiser en mode signature
jshell> signature.initSign(keyPair.getPrivate())

//Enregistrer les données à signer
jshell> signature.update("Hello World!!".getBytes())

//Générer une signature numérique
jshell> var sign = signature.sign()
sign ==> byte[63] { 48, 61, 2, 29, 0, -72, 46, -94, 42, 12 ... 4, 59, -38, -5, -48, 127 }

//Initialiser en mode vérification
jshell> signature.initVerify(keyPair.getPublic())

//Enregistrer les données à vérifier
jshell> signature.update("Hello World!!".getBytes())

//Effectuer la vérification
jshell> signature.verify(sign)
$12 ==> true

Changez simplement le nom de l'algorithme, etc. pour DSA, et la méthode d'implémentation est la même que pour RSA.

keytool keytool | Java Platform, Standard Edition Tool Reference

Le JDK est livré avec un outil de ligne de commande appelé ** keytool **. Vous pouvez utiliser keytool pour générer et gérer des clés et des certificats.

aide keytool


$ keytool -h
Outils de gestion des clés et des certificats

commander:

 -certreq Générer une demande de certificat
 -changealias Modifier les alias des entrées
 -delete Supprime l'entrée
 -exportcert Exporter le certificat
 -genkeypair Générer une paire de clés
 -genseckey Génère une clé privée
 -gencert Générer un certificat à partir d'une demande de certificat
 -importcert Importer un certificat ou une chaîne de certificats
 -importpass Mot de passe d'importation
 -importkeystore Importer une ou toutes les entrées d'un autre keystore
 -modifier le mot de passe clé pour l'entrée keypasswd
 -list Répertorie les entrées du keystore
 -printcert Imprime le contenu du certificat
 -printcertreq Imprime le contenu de la demande de certificat
 -printcrl Imprime le contenu du fichier CRL
 -storepasswd Modifier le mot de passe du magasin de clés

Pour afficher ce message d'aide"keytool -?、-h ou--help"Utilisez le
command_Pour savoir comment utiliser le nom,"keytool -command_name --help"Utilisez le.
Pour spécifier un fichier d'options préconfiguré-conf <url>Utilisez l'option.

Format de commande

keytool <commander> <option>

keytool peut être utilisé en passant la commande à exécuter et ses options. Toutes les commandes et options sont sous la forme «-nom».

Vous pouvez vérifier la liste des commandes avec -h. De plus, l'aide pour chaque commande peut être trouvée en détail en passant -h à l'option pour cette commande.

-aide de la commande certreq


$ keytool -certreq -h
keytool -certreq [OPTION]...

Générer une demande de certificat

option:

 -alias <alias>Un autre nom pour l'entrée à traiter
 -sigalg <alg>Nom de l'algorithme de signature
 -file <file>Nom du fichier de sortie
 -keypass <arg>Mot de passe clé
 -keystore <keystore>Nom du magasin de clés
 -dname <name>Nom d'identification
 -ext <value>            X.Poste 509
 -storepass <arg>Mot de passe du fichier de clés
 -storetype <type>Type de fichier de clés
 -providername <name>Nom du fournisseur
 -addprovider <name>Ajouter un fournisseur de sécurité par nom(SunPK CS11 etc.)
   [-providerarg <arg>]    -Configurer les arguments addprovider
 -providerclass <class>Ajouter un fournisseur de sécurité avec un nom de classe complet
   [-providerarg <arg>]    -Configurer les arguments pour providerclass
 -providerpath <list>Chemin de classe du fournisseur
 -v Sortie détaillée
 -Mot de passe protégé avec mécanisme de protection

Pour afficher ce message d'aide"keytool -?、-h ou--help"Utilisez le

Magasin de clés

keytool stocke des informations telles que les clés et les certificats dans un fichier appelé keystore. Jusqu'à Java 8, les fichiers de clés étaient créés dans le propre format de fichier d'Oracle appelé JKS. Java 9 et versions ultérieures sont créés dans le format de fichier défini par la norme PKCS # 12 (RFC7292).

Chaque commande keytool reçoit désormais une option appelée -keystore. Cette option spécifie l'emplacement du fichier de clés. S'il n'est pas spécifié, home directory / .keystore est utilisé par défaut comme chemin du fichier keystore.

Comme vous pouvez le voir dans la liste des commandes, il n'y a pas de commande dédiée pour créer un nouveau keystore. Le fichier de clés est automatiquement créé s'il n'y a pas de fichier lorsque vous essayez pour la première fois de générer une clé ou similaire.

Le fichier de clés lui-même est chiffré avec un chiffrement basé sur un mot de passe et vous devez entrer le mot de passe pour accéder au contenu. Le mot de passe du fichier de clés vous sera demandé lorsque vous créerez le fichier de clés pour la première fois. La longueur du mot de passe doit comporter au moins 6 chiffres.

Les informations du keystore sont également accessibles à partir des programmes Java. La classe pour cela est KeyStore (les détails seront décrits plus tard).

entrée

Les clés et les certificats stockés dans le magasin de clés sont appelés collectivement ** entrées **.

Le magasin de clés est une sorte de magasin de valeurs de clé et chaque entrée est gérée en lui donnant un nom appelé ** alias **. Afin d'afficher / modifier les informations d'une entrée spécifique, l'entrée est spécifiée par un alias.

Type d'entrée

Il existe deux types d'entrées.

Une «entrée de clé» est une entrée qui contient une clé privée ou un ensemble de clés privées et leurs certificats de clé publique associés. Le certificat de clé publique peut être un certificat auto-signé ou il peut être enchaîné au certificat racine.

D'autre part, «l'entrée de certificat de confiance» contient un ** seul ** certificat de clé publique. Un seul certificat de clé publique est stocké dans cette entrée (non chaîné), même s'il est signé par une autre clé publique.

Vérifiez le contenu du magasin de clés

Afficher la liste

$ keytool -list
Type de fichier de clés: PKCS12
Fournisseur de keystore: SUN

Keystore contient 8 entrées

hogekey,2019/04/10, SecretKeyEntry,
pbekey,2019/04/10, SecretKeyEntry,
rsakey,2019/04/13, PrivateKeyEntry,
Empreinte digitale du certificat(SHA-256): CB:D1:0C:85:5F:1D:50:CC:62:C6:68:11:9F:7E:7D:4D:F7:1B:45:40:44:71:99:B3:41:2B:71:7A:E1:A6:02:FB
dsakey,2019/04/13, PrivateKeyEntry,
Empreinte digitale du certificat(SHA-256): E9:70:1D:B2:8C:8B:18:C2:7C:A4:A0:DC:32:A5:37:06:ED:1B:DF:30:65:32:77:B5:43:77:4D:9D:42:15:70:B2
qiita-cert,2019/04/15, trustedCertEntry,
Empreinte digitale du certificat(SHA-256): 57:28:2C:9D:D0:43:19:08:A4:CC:D3:52:CF:5F:32:16:EC:9D:DA:4A:4E:D1:5C:1F:3A:EB:39:3F:76:A3:91:D4

Vous pouvez vérifier la liste des entrées avec la commande -list.

L'entrée qui stocke la paire clé privée / clé publique est écrite comme «PrivateKeyEntry» (entrée de clé). L'entrée de la clé privée est écrite sous la forme «SecretKeyEntry» (c'est aussi l'entrée de clé). L'entrée d'un certificat de confiance est écrite sous la forme «trustedCertEntry».

Afficher en spécifiant une entrée

$ keytool -list -alias hogekey
hogekey,2019/04/10, SecretKeyEntry,

$ keytool -list -alias rsakey
rsakey,2019/04/13, PrivateKeyEntry,
Empreinte digitale du certificat(SHA-256): CB:D1:0C:85:5F:1D:50:CC:62:C6:68:11:9F:7E:7D:4D:F7:1B:45:40:44:71:99:B3:41:2B:71:7A:E1:A6:02:FB

Si vous spécifiez l'alias de l'entrée que vous souhaitez afficher individuellement avec l'option -alias, vous ne pouvez afficher que les informations de cet alias.

Des informations détaillées peuvent être sorties en ajoutant l'option -v.

$ keytool -list -alias hogekey -v
alias: hogekey
Date de création: 2019/04/10
Type d'entrée: SecretKeyEntry

$ keytool -list -alias rsakey -v
alias: rsakey
Date de création: 2019/04/13
Type d'entrée: PrivateKeyEntry
Longueur de la chaîne de certificat: 1
Certificat[1]:
propriétaire: CN=Alice, C=JP
Émetteur: CN=Alice, C=JP
Numéro de série: 6a59c918
Date de début de validité: Sat Apr 13 08:41:13 JST 2019 date de fin: Fri Jul 12 08:41:13 JST 2019
Empreinte digitale du certificat:
         SHA1: 1F:11:0E:8C:30:0B:DA:D8:2C:79:AD:C7:4B:2B:70:62:85:94:94:CA
         SHA256: CB:D1:0C:85:5F:1D:50:CC:62:C6:68:11:9F:7E:7D:4D:F7:1B:45:40:44:71:99:B3:41:2B:71:7A:E1:A6:02:FB
Nom de l'algorithme de signature: SHA256withRSA
Algorithme de clé publique du sujet:Clé RSA 2048 bits
version: 3

Expansion:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 92 68 47 49 D0 26 A7 1D   04 51 64 3E 96 5B B3 05  .hGI.&...Qd>.[..
0010: 40 7B 3B E2                                        @.;.
]
]

Changer le chemin du keystore

$ keytool -storepasswd
Nouveau mot de passe du keystore:
Veuillez saisir à nouveau votre nouveau mot de passe de keystore:

Vous pouvez changer le mot de passe du keystore avec la commande -storepasswd.

Supprimer l'entrée

$ keytool -delete -alias foo

Vous pouvez supprimer n'importe quelle entrée avec la commande -delete.

L'entrée à supprimer est spécifiée par l'option -alias.

Modifier l'alias d'entrée

$ keytool -changealias -alias from-alias -destalias to-alias

Vous pouvez changer l'alias de n'importe quelle entrée avec la commande -changealias.

-alias spécifie l'alias à modifier. -destalias spécifie le nom de l'alias modifié.

Vérifiez le fichier de clés intégré au JRE

Java conserve un keystore qui enregistre le certificat racine dans l'environnement d'exécution.

Le fichier spécifique est $ {JAVA_HOME} / lib / security / cacerts. Il s'agit également d'un fichier de clés, vous pouvez donc utiliser keytool pour vérifier et modifier le contenu.

Vérifiez le contenu des cacerts


$ keytool -list -cacerts
Veuillez saisir le mot de passe du fichier de clés:
Type de fichier de clés: JKS
Fournisseur de keystore: SUN

Keystore contient 93 entrées

verisignclass2g2ca [jdk],2018/06/13, trustedCertEntry,
Empreinte digitale du certificat(SHA-256): 3A:43:E2:20:FE:7F:3E:A9:65:3D:1E:21:74:2E:AC:2B:75:C2:0F:D8:98:03:05:BC:50:2C:AF:8C:2D:9B:41:A1
digicertassuredidg3 [jdk],2017/12/01, trustedCertEntry,
Empreinte digitale du certificat(SHA-256): 7E:37:CB:8B:4C:47:09:0C:AB:36:55:1B:A6:F4:5D:B8:40:68:0F:BA:16:6A:95:2D:B1:00:71:7F:43:05:3F:C2
verisignuniversalrootca [jdk],2017/12/01, trustedCertEntry,
Empreinte digitale du certificat(SHA-256): 23:99:56:11:27:A5:71:25:DE:8C:EF:EA:61:0D:DF:2F:A0:78:B5:C8:06:7F:4E:82:82:90:BF:B8:60:E8:4B:3C

...

Empreinte digitale du certificat(SHA-256): 43:48:A0:E9:44:4C:78:CB:26:5E:05:8D:5E:89:44:B4:D8:4F:96:62:BD:26:DB:25:7F:89:34:A4:43:C7:01:61
addtrustqualifiedca [jdk],2017/12/01, trustedCertEntry,
Empreinte digitale du certificat(SHA-256): 80:95:21:08:05:DB:4B:BC:35:5E:44:28:D8:FD:6E:C2:CD:E3:AB:5F:B9:7A:99:42:98:8E:B8:F4:DC:D0:60:16

Le mot de passe par défaut est «changeit». À propos, la documentation indique: «Les administrateurs système doivent modifier le mot de passe et les autorisations par défaut pour ce fichier après l'installation du SDK».

Si vous spécifiez l'option -cacerts, elle sera dans le même état que si vous aviez spécifié le fichier cacerts comme magasin de clés, vous pouvez donc y accéder sans spécifier le chemin du fichier avec -keystore.

Si vous implémentez la communication SSL, etc. en Java, la vérification du certificat est effectuée à l'aide de ce cacerts par défaut. Il peut également être mentionné lors de l'importation d'un certificat, qui sera décrit plus loin.

Générer une clé privée

python


$ keytool -genseckey -keyalg AES -keysize 256 -alias HogeKey
Veuillez saisir le mot de passe du fichier de clés:**********
Veuillez saisir à nouveau votre nouveau mot de passe:**********

Utilisez la commande -genseckey pour générer une clé privée pour le chiffrement de clé commune.

Vous serez invité à entrer un mot de passe, qui sera le mot de passe pour crypter / décrypter le fichier de clés lui-même. Surtout cette fois, c'était le premier accès (j'ai créé le magasin de clés pour la première fois), donc je suis également invité à saisir à nouveau le nouveau mot de passe. À partir de la prochaine fois, vous n'aurez à saisir le mot de passe qu'une seule fois.

AES est spécifié comme algorithme de clé dans -keyalg, et la longueur de clé est de 256.

-alias spécifie l'alias à attacher à la clé privée enregistrée. Il peut être omis, mais dans ce cas, ce sera la valeur «mykey».

Impossible de générer une clé basée sur un mot de passe?

J'ai essayé de créer une entrée en spécifiant l'algorithme de clé basé sur un mot de passe dans -keyalg.

$ keytool -genseckey -keyalg PBEWithHmacSHA256AndAES_128 -keysize 256 -alias pbeKey
Veuillez saisir le mot de passe du fichier de clés:*******
Veuillez saisir le mot de passe pour enregistrer:**************
retaper le mot de passe:**************

J'ai pu générer l'entrée sans aucune erreur, donc j'ai l'impression d'avoir réussi. Mais quand j'obtiens cette entrée d'un programme Java, cela ressemble à ceci:

jshell


(Omis)

jshell> var entry = keystore.getEntry("pbeKey", password)
entry ==> Secret key entry with algorithm PBEWithMD5AndDES

L'algorithme est «PBEWithMD5AndDES». .. .. Cela signifie-t-il qu'il n'y a pas de soutien officiel?

Générer une paire de clés de chiffrement à clé publique

$ keytool -genkeypair -keyalg RSA -keysize 2048 -dname CN=Alice,C=JP -alias rsakey

Pour générer une paire de clés pour le chiffrement à clé publique, utilisez la commande -genkeypair.

Comme pour le chiffrement de clé commun, vous pouvez spécifier l'algorithme de clé avec -keyalg et la longueur de clé (nombre de bits) avec -keysize. (Pour créer une paire de clés DSA, spécifiez DSA dans -keyalg)

Dans -genkeypair, la clé publique est générée sous la forme d'un certificat auto-signé (X.509). -dname spécifie le nom distinctif X.500 défini pour l'émetteur et le sujet du certificat. Si vous omettez la spécification de -dname, vous serez invité à entrer le nom distinctif X.500 à partir de la ligne de commande.

Nom distinctif X.500

Décrivez le nom distinctif X.500 sous la forme <nom d'attribut> = <valeur> séparé par des virgules, tel que CN = Amazon, OU = Server CA 1B, O = Amazon, C = US. Les valeurs suivantes peuvent être spécifiées pour le nom d'attribut.

Nom d'attribut sens
CN Nom commun
Nom du propriétaire, etc.
OU Nom du service (unité d'organisation)
Noms des départements et sections
O Nom de l'organisation
Nom de l'entreprise, etc.
L Nom de la localité
Nom de la ville, etc.
S Nom d'état
Nom de l'État, etc. (niveau préfectoral au Japon?)
C Code pays (pays)
Code à deux chiffres identifiant le pays (JP, USTel)

Quelques exemples réels.

Site confirmé valeur du sujet
Qiita
https://qiita.com/
CN=qiita.com
Oracle du Japon
https://www.oracle.com/jp/index.html
CN=www-cs-01.oracle.com
OU=Content Management Services IT
O=Oracle Corporation
L=Redwood Shores
S=California
C=US
Amazon
https://www.amazon.co.jp/
CN=www.amazon.co.jp
O=Amazon.com, Inc.
L=Seattle
S=Washington
C=US
Bureau du cabinet
https://www.cao.go.jp/
CN=*.cao.go.jp
O=Cabinet Office
L=Chiyoda-ku
S=Tokyo
C=JP
Panasonic
https://panasonic.jp/
CN=panasonic.jp
O=Panasonic Corporation
L=Kadoma
S=Osaka
C=JP

Il n'est pas nécessaire de spécifier tous les noms d'attribut. Cependant, il y a une restriction sur la commande, et la commande doit être «CN», «OU», «O», «L», «S», «C».

Lorsqu'elle est spécifiée avec l'option -dname, la valeur de l'option doit être placée entre guillemets ( ") si vous souhaitez inclure des espaces vides.

-Exemple d'inclusion de dname entre guillemets


$ keytool ... -dname "CN=Alice, S=Osaka Fu, C=JP"

De plus, les virgules demi-largeur ont une signification particulière pour séparer les attributs, donc si vous voulez inclure la demi-largeur dans la valeur d'attribut, vous devez échapper avec une barre oblique inverse.

Utilisez des virgules demi-largeur pour les valeurs d'attribut


$ keytool ... -dname CN=Hoge\,Fuga

Période de validité du certificat

La période de validité du certificat peut être contrôlée par les deux options suivantes.

-startdate est la date et l'heure de début de la période de validité du certificat. -validity spécifie la période de validité du certificat en jours.

En d'autres termes, la période de validité du certificat se situe entre la date et l'heure spécifiées par «-startdate» et le nombre de jours spécifié par «-validity».

La valeur par défaut de «-startdate» est la date et l'heure d'exécution de la création de la clé, et la valeur par défaut de «-validité» est «90». Par conséquent, la période de validité par défaut d'un certificat est de 90 jours à compter de la date et de l'heure de création de la paire de clés.

-startdate peut être spécifié de manière relative ou absolue.

Dans la méthode relative, la date et l'heure de début sont spécifiées par l'heure relative à la date et à l'heure d'exécution. Plus précisément, il est décrit comme suit.

Spécification d'une date et d'une heure de début relatives


$ keytool ... -startdate +10y-1m+5d+8H

Cette désignation est «+ 10y» pour «plus 10 ans», «-1m» pour «moins 1 mois», «+ 5d» pour «plus 5 jours» et «+ 8H» pour «plus 8 heures». Il y a.

Le format de spécification relative est ([+ -] nnn [ymdHMS]) +. En d'autres termes, spécifiez dans quelle mesure l'année, le mois, le jour, l'heure, les minutes et les secondes sont décalés de plus / moins.

D'autre part, la spécification absolue est spécifiée comme suit.

Spécification de la date et de l'heure de début absolues


$ keytool ... -startdate "2019/12/01 12:00:00"

C'est celui que j'ai vu.

Le format est «[aaaa / mm / jj] [HH: MM: SS]». En cas d'omission, la date et l'heure d'exécution seront utilisées.

En d'autres termes, si vous spécifiez uniquement la date, l'heure, les minutes et les secondes seront l'heure d'exécution. Si seules les heures, minutes et secondes sont spécifiées, la date sera la date d'exécution.

Certificat de sortie

$ keytool -exportcert -alias alice-key -file alice-cert
Le certificat est un fichier<alice-cert>Enregistré dans

Vous pouvez utiliser la commande -exportcert pour sortir le certificat dans l'entrée spécifiée au format X.509. (L'entrée de clé privée ne peut pas être spécifiée car il n'y a pas de certificat)

La valeur par défaut est la sortie vers la sortie standard. Spécifiez l'option -file pour la sortie dans un fichier.

Le format de sortie par défaut est le format binaire. Pour le format PEM, spécifiez l'option -rfc.

Exemple de sortie au format PEM


$ keytool -exportcert -alias alice-key -rfc
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIEFj7+/DANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJj
...Abréviation...
7ticcWecmUspMZ2dx/lO
-----END CERTIFICATE-----

Afficher le contenu du fichier de certificat

$ keytool -printcert -file alice-cert
propriétaire: CN=alice
Émetteur: CN=ca
Numéro de série: 163efefc
Date de début de validité: Sat Apr 13 11:45:48 JST 2019 date de fin: Fri Jul 12 11:45:48 JST 2019
Empreinte digitale du certificat:
         SHA1: 07:E5:76:25:83:D1:C2:C5:66:C3:87:9E:E9:7A:B0:E8:1C:07:63:83
         SHA256: 55:ED:F8:4B:CC:3F:C9:A9:3F:5D:D8:B3:CC:51:33:58:3E:36:4A:10:06:1F:E7:19:94:25:73:66:30:E7:85:EF
Nom de l'algorithme de signature: SHA256withRSA
Algorithme de clé publique du sujet:Clé RSA 2048 bits
version: 3

Expansion:
...

Vous pouvez vérifier le contenu du fichier de certificat spécifié en exécutant la commande -printcert avec l'option -file. Si vous ne spécifiez pas -file, les informations sont lues à partir de l'entrée standard par défaut.

La valeur par défaut est sortie dans un format lisible par l'homme, comme décrit ci-dessus. Spécifiez l'option -rfc pour la sortie au format PEM.

Lors de la sortie au format PEM


$ keytool -printcert -file alice-cert -rfc
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIEFj7+/DANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJj
...Abréviation...
7ticcWecmUspMZ2dx/lO
-----END CERTIFICATE-----

La commande -printcert n'est pas liée au fichier de clés, elle peut donc être exécutée sans spécifier -keystore.

Afficher le certificat de n'importe quel serveur SSL

$ keytool -printcert -sslserver qiita.com
Certificate #0
====================================
propriétaire: CN=qiita.com
Émetteur: CN=Amazon, OU=Server CA 1B, O=Amazon, C=US
Numéro de série: 793de44ec0e815de65a3fbb3e35a1e2
Date de début de validité: Sun Mar 31 09:00:00 JST 2019 date de fin: Thu Apr 30 21:00:00 JST 2020
Empreinte digitale du certificat:
         SHA1: C0:D8:EE:56:3B:9F:68:22:0B:36:F3:9E:2A:D3:69:4E:3C:1D:61:44
         SHA256: 57:28:2C:9D:D0:43:19:08:A4:CC:D3:52:CF:5F:32:16:EC:9D:DA:4A:4E:D1:5C:1F:3A:EB:39:3F:76:A3:91:D4
Nom de l'algorithme de signature: SHA256withRSA
Algorithme de clé publique du sujet:Clé RSA 2048 bits
version: 3

...

Certificate #3
====================================
propriétaire: CN=Starfield Services Root Certificate Authority - G2, O="Starfield Technologies, Inc.", L=Scottsdale, ST=Arizona, C=US
Émetteur: OU=Starfield Class 2 Certification Authority, O="Starfield Technologies, Inc.", C=US
Numéro de série: a70e4a4c3482b77f
...

Vous pouvez imprimer le certificat du serveur SSL spécifié en exécutant la commande -printcert avec l'option -sslserver.

L'hôte et le port peuvent être spécifiés dans -sslserver sous la forme hôte: port. Le numéro de port est facultatif et vaut par défaut 443.

Lors de l'exécution dans un environnement proxy, spécifiez les informations du proxy avec les options -J-Dhttps.proxyHost et -J-Dhttps.proxyPort (par exemple, -J-Dhttps.proxyHost = proxy.host.name) -J-Dhttps.proxyPort = 8080).

Cela prend également par défaut un format lisible par l'homme. Pour le format PEM, spécifiez l'option -rfc.

Si vous souhaitez l'enregistrer localement en tant que fichier de certificat, vous pouvez rediriger le résultat de sortie vers un fichier avec l'option -rfc ( -file est une option de fichier d'entrée, donc même si vous spécifiez cela, il sera enregistré dans le fichier. N'est pas enregistré).

Créer une demande de signature de certificat

$ keytool -certreq -alias alice-key
-----BEGIN NEW CERTIFICATE REQUEST-----
MIICkDCCAXgCAQAwGzELMAkGA1UEBhMCSlAxDDAKBgNVBAMTA1RvbTCCASIwDQYJ
...Abréviation...
wX7FTUBXCD8CjXFxosmZ97YJf7Xy89cmdArgVNE9T9pJJpht
-----END NEW CERTIFICATE REQUEST-----

Vous pouvez utiliser la commande -certreq pour créer une demande de signature de certificat.

Dans l'option -alias, spécifiez l'alias de l'entrée de clé publique cible.

La valeur par défaut est la sortie vers la sortie standard. Si vous voulez sortir dans un fichier, redirigez ou spécifiez le fichier de destination de sortie avec l'option -file.

Exporter la demande de signature de certificat dans un fichier


$ keytool -certreq -alias alice-key -file alice-certreq

Générer un certificat à partir d'une demande de signature de certificat

#Vérifiez le contenu du magasin de clés
$ keytool -list
...

Keystore contient 2 entrées

ca-key,2019/04/13, PrivateKeyEntry,
...
alice-key,2019/04/13, PrivateKeyEntry,
...

#Générer un certificat à partir d'un fichier de demande de signature de certificat
$ keytool -gencert -infile alice-certreq -alias ca-key -outfile alice-cert

Vous pouvez utiliser la commande -gencert pour générer un certificat (au format X.509) à partir d'un fichier de demande de signature de certificat.

L'exemple ci-dessus génère un certificat pour le fichier de demande de signature de certificat «alice-certreq» en utilisant l'entrée de paire de clés «ca-key» qui existe dans le fichier de clés.

-infile spécifie le fichier de demande de signature de certificat à traiter. Si non spécifié, entrez à partir de l'entrée standard.

-alias spécifie l'entrée de clé utilisée pour signer le certificat.

-outfile spécifie le fichier pour sortir le certificat résultant. S'il n'est pas spécifié, il est émis vers la sortie standard.

Par défaut, les certificats sont imprimés au format binaire. Vous pouvez sortir au format PEM en spécifiant l'option -rfc.

-Lorsque l'option rfc est spécifiée


$ keytool -gencert -infile alice-certreq -alias ca-key -rfc
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIEF+xeBDANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJj
...Abréviation...
es0hcACS0jLw0VoJYjSB
-----END CERTIFICATE-----

Importez le certificat

Utilisez la commande -importcert pour importer le certificat.

Le format des données pouvant être importées peut être une chaîne de certificats au format PKCS # 7 ou un seul ou une séquence de certificats X.509.

Cette commande a deux fonctions.

  1. Importer la réponse du certificat
  2. Importez un certificat de confiance

La commande -importcert exécute l'une de ces fonctions, selon les conditions.

Il fonctionne comme une importation de réponse de certificat si les conditions suivantes sont remplies.

--Une entrée avec le nom spécifié par -alias existe déjà dans le fichier de clés

En revanche, si les conditions suivantes sont remplies, il fonctionne comme une importation de certificat de confiance.

--L'entrée avec le nom spécifié par -alias n'existe pas dans le fichier de clés

Importer la réponse du certificat

Si une entrée avec le nom spécifié par -alias existe déjà dans le magasin de clés en tant qu'entrée de clé privée, le certificat que vous essayez d'importer est considéré comme une réponse de certificat. En d'autres termes, il est traité comme une tentative d'importer la réponse de certificat signée et renvoyée par l'autorité de certification en réponse à la demande de signature de certificat générée à partir du certificat de clé publique dans l'entrée de clé.

Par conséquent, keytool fonctionne pour remplacer le certificat de clé publique contenu dans l'entrée de clé par la réponse de certificat spécifiée pour l'importation.

Vérification de la réponse du certificat

Avant que l'importation ne soit réellement effectuée, la réponse du certificat est validée.

La vérification est approximativement effectuée des deux manières suivantes.

  1. Si la réponse du certificat correspond à l'entrée de clé spécifiée par -alias
  2. Si la réponse du certificat est fiable

Si la réponse de certificat correspond à l'entrée de clé spécifiée par «-alias» est déterminée par le fait que la réponse de certificat contient la même clé publique dans l'entrée de clé. Si le certificat de clé publique dans l'entrée de clé n'existe pas dans la réponse de certificat, la réponse de certificat est totalement inutile et une erreur se produit.

jca.jpg

La méthode pour déterminer si la réponse de certificat est fiable ou non est approximativement divisée en deux en fonction du nombre de certificats existant dans la réponse de certificat.

Lorsque plusieurs certificats sont inclus dans la réponse de certificat

Si la réponse de certificat contient plusieurs certificats, elle sera testée pour voir si une chaîne de certificats valide peut être créée avec ces certificats.

Tout d'abord, recherchez le certificat avec la même clé publique que l'entrée de clé de destination d'importation dans la réponse de certificat. Ensuite, avec ce certificat en haut, les certificats dont la vérification de signature est OK sont connectés les uns après les autres.

jca.jpg

Si le certificat de fin est un certificat auto-signé, il sera vérifié pour voir si le même certificat existe dans le keystore. Si le même certificat auto-signé existe dans le magasin de clés, la chaîne de certificats est considérée comme légitime et la chaîne de certificats construite est importée dans l'entrée de clé. Si le même certificat auto-signé n'existe pas, keytool vous demandera si vous souhaitez poursuivre l'importation.

En revanche, si le certificat à la fin n'est pas un certificat auto-signé, il est vérifié s'il existe un certificat dans le keystore qui peut vérifier la signature. Si le certificat correspondant est trouvé dans le magasin de clés, le certificat trouvé dans le magasin de clés est ajouté à la fin de la chaîne de certificats et la chaîne de certificats est importée dans l'entrée de clé. Si le certificat n'est pas trouvé, keytool vous demandera si vous souhaitez poursuivre l'importation.

jca.jpg

Si la réponse du certificat est un certificat unique

S'il n'y a qu'un seul certificat dans la réponse de certificat, le comportement de validation changera légèrement.

Dans ce cas, keytool tente de reproduire la chaîne de certificats à l'aide du certificat qui existe dans le fichier de clés de destination.

jca.jpg

Si la chaîne de certificats peut être finalement reproduite jusqu'à la racine, la chaîne reproduite comme une chose fiable est enregistrée dans l'entrée de clé. Si la chaîne ne peut pas être reproduite (si vous ne trouvez pas de clé publique vérifiable au milieu), keytool vous demandera si vous souhaitez poursuivre l'importation.

Importer un certificat de confiance

Si l'entrée spécifiée par -alias n'existe pas dans le keystore, elle sera importée en tant que certificat de confiance.

Dans ce cas, même s'il y a plusieurs certificats dans les données à importer, seul le premier est importé.

À ce stade également, une vérification est effectuée pour voir si le certificat est valide. Cette validation se comporte de la même manière que si la réponse de certificat était un certificat unique.

En d'autres termes, la validité ou non du certificat est déterminée par le fait que la chaîne de certificats peut ou non être reproduite à l'aide du certificat dans le magasin de clés.

Si la chaîne peut être reproduite, l'importation sera terminée. S'il ne peut pas être reproduit, keytool vous demandera si vous souhaitez poursuivre l'importation.

Utilisez également les certificats cacerts pour la validation

Si vous spécifiez -trustcacerts dans l'option de la commande -importcert, le certificat racine dans cacerts sera également utilisé lors de la validation. (Par défaut, seul le certificat du keystore cible est utilisé)

Essayez en fait

#Vérifiez le keystore d'Alice
$ keytool -keystore alice.keystore -list
...
Keystore contient 1 entrée

alice-key,2019/04/18, PrivateKeyEntry,
Empreinte digitale du certificat(SHA-256): C7:55:BB:57:C4:3F:57:02:BE:2E:57:1E:7C:6F:F4:A9:55:E5:90:92:6B:7D:DF:5F:14:FF:F2:65:16:81:E1:56

#Vérifiez le fichier de clés CA
$ keytool -keystore ca.keystore -list
...
Keystore contient 2 entrées

middle-ca-key,2019/04/18, PrivateKeyEntry,
Empreinte digitale du certificat(SHA-256): CE:79:D5:32:D0:1E:5E:3A:55:BB:3E:CD:CE:5E:5F:9F:4D:79:97:2E:E7:EF:F9:60:62:C5:D0:4E:AD:28:B4:60
root-ca-key,2019/04/18, PrivateKeyEntry,
Empreinte digitale du certificat(SHA-256): C0:06:6C:5C:2C:F5:3D:8B:DB:D4:EC:19:C5:30:A0:90:71:00:CC:17:40:E8:EB:38:64:5B:8F:40:3A:D4:3F:7C

Deux magasins clés ont été préparés pour vérification.

Le premier est le keystore d'Alice, qui n'a actuellement qu'une seule entrée pour une paire de clés publiques de certificats auto-signés (ʻalice-key`).

L'autre est le keystore de l'autorité de certification (CA), qui a une paire de clés publiques pour l'autorité d'authentification intermédiaire («middle-ca-key») et une paire de clés publiques pour l'autorité d'authentification racine («root-ca-key»). Il y a deux entrées. (Au fait, middle-ca-key est signé avec root-ca-key)

À partir de là, générez une demande de signature de certificat à partir de la paire de clés publiques d'Alice, signez-la avec la clé de l'autorité de certification et importez-la dans le magasin de clés d'Alice.

#Générer une demande de signature de certificat à partir de la clé d'Alice
$ keytool -keystore alice.keystore -certreq -alias alice-key -file alice.csr

#Signé avec la clé de l'autorité de certification intermédiaire
$ keytool -keystore ca.keystore -gencert -alias middle-ca-key -infile alice.csr -outfile alice.cer

#Vérifiez le contenu du certificat généré
$ keytool -printcert -file alice.cer
Certificat[1]:
propriétaire: CN=alice
Émetteur: CN=middle-ca
Numéro de série: 25fc2e01
...

Certificat[2]:
propriétaire: CN=middle-ca
Émetteur: CN=root-ca
Numéro de série: 248f2111
...

Généré une demande de signature de certificat à partir de la paire de clés d'Alice et généré un certificat de clé publique signé avec une clé d'autorité de certification intermédiaire.

En regardant le contenu, vous pouvez voir que les certificats sont enchaînés dans l'ordre Alice → Autorité de certification intermédiaire (le certificat de l'autorité de certification racine n'est pas inclus).

Importez-le dans l'entrée de clé d'origine d'Alice (c'est-à-dire importez-le en tant que réponse de certificat).

$ keytool -keystore alice.keystore -importcert -file alice.cer -alias alice-key

Certificat de réponse de niveau supérieur:

propriétaire: CN=middle-ca
Émetteur: CN=root-ca
Numéro de série: 248f2111
...

...N'est pas digne de confiance. Voulez-vous installer la réponse?[Non]:

Le certificat de niveau supérieur a été déterminé comme non approuvé et il a été demandé de confirmer qu'il pouvait être importé. Puisque le certificat root-ca n'existe pas dans le magasin de clés d'Alice, la réponse de certificat que vous avez tenté d'importer est considérée comme non fiable.

Ici, l'importation est temporairement suspendue et le certificat d'autorité d'authentification racine est d'abord importé dans le magasin de clés d'Alice.

#Exporter le certificat de clé publique de l'autorité de certification racine
$ keytool -keystore ca.keystore -exportcert -alias root-ca-key -file root-ca.cer
Le certificat est un fichier<root-ca.cer>Enregistré dans

#Importer le certificat de l'autorité de certification racine dans le magasin de clés d'Alice
$ keytool -keystore alice.keystore -importcert -file root-ca.cer -alias root-ca-cert
propriétaire: CN=root-ca
Émetteur: CN=root-ca
Numéro de série: 13b64cb6
...
Empreinte digitale du certificat:
         SHA1: E9:D2:EF:D2:1D:86:8F:04:50:0C:ED:DD:5B:C1:C5:DD:FE:64:77:3D
         SHA256: C0:06:6C:5C:2C:F5:3D:8B:DB:D4:EC:19:C5:30:A0:90:71:00:CC:17:40:E8:EB:38:64:5B:8F:40:3A:D4:3F:7C
...

Faites-vous confiance à ce certificat?[Non]:  y
Certificat ajouté au keystore

L'entrée spécifiée pour -alias n'existe pas, donc la commande -importcert se comporte comme" importer un certificat de confiance ".

Étant donné que ce certificat est un certificat auto-signé, l'importateur (administrateur du magasin de clés) n'a d'autre choix que de décider de lui faire confiance. Je lui fais confiance ici, j'ai donc terminé l'importation avec y.

Si vous importez réellement un certificat de confiance, vous devez examiner attentivement les informations telles que les empreintes digitales affichées à l'écran pour vous assurer que vous importez le certificat souhaité.

Essayez à nouveau d'importer la réponse de certificat d'Alice.

#Importer la réponse de certificat d'Alice
$ keytool -keystore alice.keystore -importcert -file alice.cer -alias alice-key
Réponse de certificat installée dans le magasin de clés

#Vérifiez l'entrée de la clé d'Alice après l'importation
$ keytool -keystore alice.keystore -storepass password -list -v -alias alice-key
alias: alice-key
Date de création: 2019/04/18
Type d'entrée: PrivateKeyEntry
Longueur de la chaîne de certificat: 3
Certificat[1]:
propriétaire: CN=alice
Émetteur: CN=middle-ca
Numéro de série: 25fc2e01
...

Certificat[2]:
propriétaire: CN=middle-ca
Émetteur: CN=root-ca
Numéro de série: 248f2111
...

Certificat[3]:
propriétaire: CN=root-ca
Émetteur: CN=root-ca
Numéro de série: 13b64cb6
...

Cette fois, le certificat de l'autorité de certification racine était déjà installé, de sorte que la vérification de la chaîne de certificats a réussi et l'installation s'est terminée telle quelle.

En outre, après l'importation, vous pouvez voir que la chaîne de certificats est stockée dans le certificat de clé publique dans l'entrée de clé d'Alice.

KeyStore Le fichier de keystore généré et géré par la commande keytool possède une API afin de pouvoir y accéder à partir des programmes Java.

Obtenir une instance KeyStore

jshell


jshell> var keystore = KeyStore.getInstance("PKCS12")
keystore ==> java.security.KeyStore@3fd7a715

Pour accéder au fichier de clés, utilisez la classe KeyStore.

getInstance () spécifie le type de fichier de clés. Dans Java 9 et supérieur, le type de fichier de clés est PKCS12 par défaut, vous instanciez donc avec «« PKCS12 »». (S'il s'agit d'un ancien magasin de clés, il peut s'agir de JKS, spécifiez donc " JKS " dans ce cas)

Lire le fichier de keystore

jshell


jshell> char[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' }
password ==> char[8] { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' }

jshell> keystore.load(new FileInputStream("alice.keystore"), password)

Pour charger le fichier keystore, [load ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#load(java.) io.InputStream, char% 5B% 5D))) Utilisez la méthode. Spécifiez ʻInputStreamdu fichier keystore dans le premier argument, et spécifiez le mot de passe du fichier keystore dans le tableauchar` dans le second argument.

Lire en même temps que l'acquisition de l'instance

jshell


jshell> var keystore = KeyStore.getInstance(new File("alice.keystore"), password)
keystore ==> java.security.KeyStore@525b461a

[getInstance (File, char [])](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#getInstance(java.io.File) , char% 5B% 5D)), vous pouvez tout faire, de la création d'une instance KeyStore au chargement d'un fichier keystore en une seule fois.

Le type de fichier de clés est déterminé automatiquement.

Obtenir les noms d'alias pour toutes les entrées

jshell


jshell> keystore.aliases().asIterator().forEachRemaining(System.out::println)
alice-key
aes-key
root-ca-cert

aliases () Dans la méthode, alias des noms de toutes les entrées Peut être obtenu.

Vérifiez le type d'entrée à partir du nom d'alias

jshell


jshell> keystore.isKeyEntry("alice-key")
$12 ==> true

jshell> keystore.isCertificateEntry("alice-key")
$13 ==> false

jshell> keystore.isKeyEntry("root-ca-cert")
$14 ==> false

jshell> keystore.isCertificateEntry("root-ca-cert")
$15 ==> true

Si l'entrée pointée par l'alias est une entrée de clé est isKeyEntry ().

S'il s'agit d'une entrée de certificat de confiance, c'est [isCertificateEntry ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#isCertificateEntry (java) Vous pouvez le vérifier avec .lang.String)).

Obtenez la clé privée

jshell


jshell> var seckey = keystore.getKey("seckey", password)
seckey ==> javax.crypto.spec.SecretKeySpec@fffe8f5b

jshell> seckey.getAlgorithm()
$29 ==> "AES"

[getKey ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#getKey(java.lang.String, char% 5B%) Avec 5D)), vous pouvez obtenir la clé de l'alias spécifié comme clé privée.

Le premier argument est le nom d'alias et le deuxième argument est le mot de passe qui protège la clé (généralement le même que le mot de passe du keystore).

Obtenez une clé privée

jshell


jshell> var alicePrivateKey = keystore.getKey("alice-key", password)
alicePrivateKey ==> SunRsaSign RSA private CRT key, 2048 bits
  param ... 96220784207006016968977089

jshell> alicePrivateKey.getClass()
$38 ==> class sun.security.rsa.RSAPrivateCrtKeyImpl

Vous pouvez obtenir la clé privée en utilisant getKey () sur l'entrée de clé de la paire de clés publiques.

Obtenir une clé publique (certificat)

jshell


//Obtenir un certificat
jshell> Certificate aliceCert = keystore.getCertificate("alice-key")
aliceCert ==> [
[
  Version: V3
  Subject: CN=alice
  Signature ... 3 D5  .........%$.m...

]

//Obtenez la clé publique du certificat
jshell> PublicKey alicePublicKey = aliceCert.getPublicKey()
alicePublicKey ==> Sun RSA public key, 2048 bits
  params: null
  mo ... 9
  public exponent: 65537

[GetCertificate ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#getCertificate(java) pour l'entrée de clé de la paire de clés publiques Avec .lang.String)), le certificat de clé publique (java.security.cert.Certificate /security/cert/Certificate.html))) peuvent être obtenus.

Obtenez une chaîne de certificats

jshell


jshell> Certificate[] chain = keystore.getCertificateChain("alice-key")
chain ==> Certificate[3] { [
[
  Version: V3
  Subject: CN= ... C0  .;;.O...&.....N.

] }

Si vous utilisez getCertificateChain () , Vous pouvez obtenir la chaîne de certificats qui existe dans l'entrée spécifiée sous la forme d'un tableau de Certificat.

Ajouter une entrée de clé privée

jshell


//Générer la clé privée
jshell> var secKey = KeyGenerator.getInstance("AES").generateKey()
secKey ==> javax.crypto.spec.SecretKeySpec@fffe806b

//Générer une entrée de clé privée
jshell> var secKeyEntry = new KeyStore.SecretKeyEntry(secKey)
secKeyEntry ==> Secret key entry with algorithm AES

//Enregistrer une entrée de clé privée dans le magasin de clés
jshell> var protection = new KeyStore.PasswordProtection(password)
protection ==> java.security.KeyStore$PasswordProtection@27d415d9

jshell> keystore.setEntry("sec-key", secKeyEntry, protection)

//Extraire et confirmer l'entrée de clé privée enregistrée
jshell> keystore.getKey("sec-key", password)
$10 ==> javax.crypto.spec.SecretKeySpec@fffe806b

Pour ajouter une entrée au keystore, setEntry ().

Le premier argument est l'alias d'entrée. Le deuxième argument est l'entrée à enregistrer. Le troisième argument transmet les paramètres liés à la protection d'entrée.

Créez une entrée de clé privée avec KeyStore.SecretKeyEntry.

Le paramètre transmettra le mot de passe à utiliser lors du cryptage de l'entrée. Il est possible de spécifier un mot de passe différent de celui du magasin de clés, mais est-il prudent de le conserver? (Keytool utilise le même mot de passe que le keystore si le type de keystore est PKCS12)

Sortie dans un fichier

jshell


jshell> keystore.store(new FileOutputStream("alice.keystore"), password)

[store ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#store (java.io.OutputStream, char% 5B%) Avec 5D)), vous pouvez sortir le contenu de KeyStore dans le flux de sortie spécifié.

Certificat

Y a-t-il une nouvelle API à créer?

[Java.security.cert.Certificate] en tant que classe représentant un certificat (https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/cert/Certificate.html) Il existe une classe appelée java.security.cert.CertificateFactory comme classe pour construire cette instance. Il existe une classe appelée /java/security/cert/CertificateFactory.html).

Cependant, «CertificateFactory» lit un certificat existant et construit un «certificat», pas un certificat à partir de zéro.

En gros java.security.cert Même si vous regardez le package , Je ne trouve pas de classe qui génère un certificat.

J'ai regardé l'implémentation de la commande -gencert de Keytool, mais la classe du package sun.security.x509 (API interne) a été utilisée.

Il n'existe aucun moyen de générer un certificat en utilisant uniquement l'API standard. Il semble qu'il n'y ait pas d'autre choix que de le générer à l'aide de keytool.

Lire le certificat

Type de certificat spécifiable

Utilisez CertificateFactory pour lire le certificat codé Faire.

Le type de certificat qui peut être spécifié avec getInstance () est uniquement X.509 comme fonction standard, et Javadoc etc. est essentiellement expliqué principalement par l'explication de X.509.

Type de CertificateFactory | Nom de l'algorithme standard de sécurité Java

Par conséquent, l'explication ici est également basée sur X.509.

Lisez les certificats un par un

jshell


//Générer CertificateFactory
jshell> var factory = CertificateFactory.getInstance("X.509")
factory ==> java.security.cert.CertificateFactory@3abbfa04

//Fichier de certificat à lire (Alice.Générer un flux d'entrée de cer)
jshell> var in = new FileInputStream("alice.cer")
in ==> java.io.FileInputStream@31ef45e3

//Lire les informations de certificat à partir du flux d'entrée
jshell> factory.generateCertificate(in)
$5 ==> [
[
  Version: V3
  Subject: CN=alice
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
...
]

jshell> factory.generateCertificate(in)
$6 ==> [
[
  Version: V3
  Subject: CN=middle-ca
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
...
]

jshell> factory.generateCertificate(in)
|Exception java.security.cert.CertificateException: Could not parse certificate: java.io.IOException: Empty input
|        at X509Factory.engineGenerateCertificate (X509Factory.java:110)
|        at CertificateFactory.generateCertificate (CertificateFactory.java:355)
|        at (#7:1)
|Cause: java.io.IOException: Empty input
|        at X509Factory.engineGenerateCertificate (X509Factory.java:106)
|        ...

Pour lire le certificat X.509 encodé, generateCertificate ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/cert/CertificateFactory Utilisez .html # generateCertificate (java.io.InputStream)).

Les données cibles doivent être des données au format binaire codées DER ou des données au format PEM.

Si les données cibles contiennent des données de plusieurs certificats, un certificat peut être lu depuis le début à chaque fois que generateCertificate () est appelé. (Si le certificat n'existe plus et que vous essayez de le lire plus loin, une exception sera levée)

Lire plusieurs certificats à la fois

jshell


jshell> Collection<? extends Certificate> certs = factory.generateCertificates(new FileInputStream("alice.cer"))
certs ==> [[
[
  Version: V3
  Subject: CN=alice
  Signatur ...  70  ....$..&...2.n.p

]]

jshell> certs.size()
$12 ==> 2

Si generateCertificates () , Tous les certificats des données cibles peuvent être lus en même temps.

Vérifier la signature du certificat

jshell


//Extraire les certificats un par un de la collection de certificats lus dans ↑
jshell> var iterator = certs.iterator()
iterator ==> java.util.ArrayList$Itr@27808f31

//Obtenez le certificat d'Alice
jshell> var aliceCert = iterator.next()
aliceCert ==> [
[
  Version: V3
  Subject: CN=alice
  Signature ... 3 D5  .........%$.m...

]

//Obtenir le certificat de l'autorité de certification intermédiaire
jshell> var middleCaCert = iterator.next()
middleCaCert ==> [
[
  Version: V3
  Subject: CN=middle-ca
  Signa ... 0 70  ....$..&...2.n.p

]

//Vérifier le certificat d'Alice avec la clé publique de l'autorité de certification intermédiaire (vérification OK)
jshell> aliceCert.verify(middleCaCert.getPublicKey())

//Vérifier le certificat d'Alice avec la propre clé publique d'Alice (vérification NG)
jshell> aliceCert.verify(aliceCert.getPublicKey())
|Exception java.security.SignatureException: Signature does not match.
|        at X509CertImpl.verify (X509CertImpl.java:459)
|        at X509CertImpl.verify (X509CertImpl.java:391)
|        at (#18:1)

Pour vérifier la signature du certificat verify () est utilisé.

Passez la clé publique utilisée pour la vérification comme argument.

Si la validation est OK, rien ne se passe et si elle est NG, une exception est levée.

nombre aléatoire

jshell


//Créer une instance SecureRandom
jshell> var random = new SecureRandom()
random ==> Hash_DRBG,SHA-256,128,reseed_only

//Obtenez des nombres aléatoires inférieurs à 100
jshell> random.nextInt(100)
$3 ==> 91

//Générer un tableau d'octets aléatoires de 512 bits
jshell> byte[] bytes = new byte[64]
bytes ==> byte[64] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... , 0, 0, 0, 0, 0, 0, 0, 0 }

jshell> random.nextBytes(bytes)

jshell> bytes
bytes ==> byte[64] { 97, 86, -17, 9, 81, 39, -18, -43, 40, -43, -21, 93, 112, -78, -1, 59, -5, 110, 115, 13, -86, 34, -81, -90, -107, -82, -102, -72, 19, 87, 9, -38, -91, -7, 47, -119, 103, -24, 52, -40, -3, 36, -9, -62, 10, -32, 36, 27, -74, -75, 58, -54, -124, -49, -6, 77, 78, 32, 96, 66, 67, -86, -39, -98 }

Utilisez SecureRandom pour générer des nombres aléatoires pour le chiffrement.

Java a également une classe Random pour générer des nombres aléatoires. Faire. Cependant, cela n'est pas imprévisible et ne doit pas être utilisé pour la cryptographie.

SecureRandom est différent des autres classes de moteur dans [getInstace ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/SecureRandom.html# En plus de getInstance (java.lang.String)), [Constructor](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/SecureRandom.html#%3Cinit Vous pouvez créer une instance avec% 3E ()).

Lorsqu'une instance est créée par le constructeur, l'algorithme est sélectionné parmi les fournisseurs ayant la priorité la plus élevée.

L'algorithme sélectionné pour chaque environnement est [SecureRandom Implementation | 4 JDK Provider Document](https://docs.oracle.com/javase/jp/11/security/oracle-providers.html#GUID-9DC4ADD5 Il peut être confirmé par -6D01-4B2E-9E85-B88E3BEE7453).

Dois-je utiliser getInstance () ou un constructeur?

Une instance de SecureRandom peut être obtenue avec getInstance () comme les autres classes de moteur, ou avec un constructeur. Dans le premier, il est nécessaire de spécifier l'algorithme, mais dans le second, l'algorithme est déterminé par une priorité prédéterminée.

Lequel doit être utilisé pour créer une instance de SecureRandom?

En regardant la documentation officielle Javadoc et JCA, je n'ai rien trouvé qui dise clairement "utiliser ceci".

La conclusion personnelle que j'ai tirée après diverses enquêtes était "utiliser le constructeur à moins qu'il n'y ait une raison particulière" (cela peut être faux car c'est une conclusion personnelle).

La raison en est que l'utilisation d'un constructeur vous permet de maintenir la portabilité tout en sélectionnant l'algorithme optimal pour chaque environnement.

Dans les environnements de type Unix, l'algorithme par défaut est NativePRNG. Cependant, cet algorithme ne peut pas être utilisé dans un environnement Windows. En d'autres termes, si l'algorithme est spécifié comme «NativePRNG», le programme ne peut pas être porté sur Windows. (Inversement, Windows-PRNG ne peut pas être utilisé dans les environnements basés sur Unix)

De plus, dans l'environnement Windows, l'algorithme par défaut jusqu'à Java 8 était SHA1PRNG. Cependant, avec l'ajout de DRBG dans Java 9, la priorité absolue a été remplacée par DRBG (JEP 273: DRBG-Based SecureRandom Implementations). ).

«DRBG» est une méthode de génération de nombres pseudo-aléatoires décrite dans la Publication spéciale NIST 800-90A Révision 1. Il semble être plus fort que «SHA1PRNG».

Je pense qu'il est préférable d'utiliser un constructeur car il est facile de gérer les cas où l'algorithme est remplacé par un nouveau. (C'est juste une conclusion personnelle)

getInstanceStrong() Java 8 a ajouté une méthode appelée getInstanceStrong () ing.

Un algorithme qui génère des nombres aléatoires forts est sélectionné, comme son nom l'indique «Strong». Il semble être utilisé lors de la génération de valeurs importantes telles que la génération de paires de clés pour le chiffrement à clé publique.

Par exemple, lorsqu'il est utilisé dans un environnement basé sur Unix, l'algorithme spécifique est «NativePRNGBlocking».

Ce qui est différent du NativePRNG par défaut est [nextBytes ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/ security / SecureRandom.html # nextBytes (octet% 5B% 5D)) et [generateSeed ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/ SecureRandom.html # generateSeed (int)) est différent selon qu'il utilise / dev / random ou / dev / urandom, respectivement.

algorithme nextBytes() generateSeed()
NativePRNG /dev/urandom /dev/random
NativePRNGBlocking /dev/random /dev/random
NativePRNGNonBlocking /dev/urandom /dev/urandom

SecureRandom | Tableau 4-4 Algorithmes du fournisseur SUN

Ces / dev / random et / dev / urandom sont des pseudo-périphériques pour générer des nombres aléatoires et peuvent être utilisés sur les systèmes d'exploitation basés sur Unix.

/ dev / random recueille le bruit ambiant et génère des nombres aléatoires, de sorte que de vrais nombres aléatoires peuvent être obtenus. Cependant, si une tentative est faite pour obtenir un nombre aléatoire alors que des informations suffisantes ne sont pas collectées, le processus sera bloqué (en attente).

D'un autre côté, / dev / urandom peut générer des nombres aléatoires sans bloquer le traitement en réutilisant les informations collectées. Cependant, puisque les informations sont réutilisées, la sécurité en tant que nombre aléatoire est inférieure à «/ dev / random».

référence

/ dev / random est hautement sécurisé, mais au prix d'une dégradation des performances du programme. / dev / urandom est moins sécurisé, mais au prix de ne pas compromettre les performances du programme.

La sélection par défaut, NativePRNG, utilise / dev / random pour générer de vrais nombres aléatoires pour la génération de semences, mais utilise / dev / urandom pour la génération normale de nombres aléatoires. En d'autres termes, «NativePRNG» est capable de garantir des performances tout en réduisant légèrement la sécurité.

D'un autre côté, NativePRNGBlocking obtient toujours un nombre aléatoire de / dev / random. En d'autres termes, la performance est abandonnée et la sécurité est primordiale.

Je pense que vous devriez utiliser getInstanceStrong () lorsque la sécurité doit avoir la priorité sur les performances du programme, et utiliser l'implémentation par défaut dans le constructeur lorsque les deux doivent être compatibles (personnels). opinion).

Échange de clés Diffie-Hellman

KeyAgreement est une classe pour réaliser l'échange de clés Diffie-Hellman. Il est préparé.

Le flux d'échange de clés à l'aide de "KeyAgreement" est le suivant.

jca.png

Ci-dessous, lancez jshell pour Alice et jshell pour Bob, et essayez d'échanger les clés (les deux répertoires actuels sont au même endroit).

Alice


//Générer une paire de clés
jshell> var aliceKeyPairGen = KeyPairGenerator.getInstance("DH")
aliceKeyPairGen ==> java.security.KeyPairGenerator$Delegate@6d7b4f4c

//Générer une paire de clés
jshell> var aliceKeyPair = aliceKeyPairGen.generateKeyPair()
aliceKeyPair ==> java.security.KeyPair@1786dec2

//Générer l'accord de clé d'Alice
jshell> var aliceKeyAgree = KeyAgreement.getInstance("DH")
aliceKeyAgree ==> javax.crypto.KeyAgreement@2c039ac6

//Initialisé avec la clé privée d'Alice
jshell> aliceKeyAgree.init(aliceKeyPair.getPrivate())

//Sortie de la clé publique d'Alice dans un fichier
jshell> writeFile("alice-dh-public-key", aliceKeyPair.getPublic().getEncoded())

//Le format de la clé publique est X.509
jshell> aliceKeyPair.getPublic().getFormat()
$32 ==> "X.509"

Pour échanger des clés, utilisez d'abord KeyPairGenerator pour générer une paire de clés. Spécifiez «DiffieHellman» ou l'abréviation «DH» pour le nom de l'algorithme.

La clé publique de cette paire de clés contient le premier $ p $, la primitive $ g $ et la valeur $ y = g ^ {a} \ bmod p $ calculée à partir du nombre aléatoire choisi de manière appropriée $ a $. Il y a.

Pour KeyAgreement, récupérez une instance avec DiffieHellman (ou DH) et initialisez-la avec la clé privée.

Bob


//Lire la clé publique d'Alice
jshell> var encodedAlicePublicKey = readFile("alice-dh-public-key")
encodedAlicePublicKey ==> byte[556] { 48, -126, 2, 40, 48, -126, 1, 27, 6,  ...  70, 34, 8, -3, -119, 14 }

//Restaurer la clé publique codée d'Alice
jshell> var bobKeyFactory = KeyFactory.getInstance("DH")
bobKeyFactory ==> java.security.KeyFactory@7a9273a8

jshell> var alicePublicKeySpec = new X509EncodedKeySpec(encodedAlicePublicKey)
alicePublicKeySpec ==> java.security.spec.X509EncodedKeySpec@e874448

jshell> var alicePublicKey = bobKeyFactory.generatePublic(alicePublicKeySpec)
alicePublicKey ==> SunJCE Diffie-Hellman Public Key:
y:
    17cc14 ...
g:
    02
l:
    1024

La clé publique reçue d'Alice est au format X.509, donc X509EncodedKeySpec Peut être utilisé pour restaurer.

Puisque c'est le rôle de KeyFactory de générer Key à partir de KeySpec, restaurez en utilisant KeyFactory. Pour le nom de l'algorithme, spécifiez «DiffieHellman» ou «DH».

Bob


//Générer la paire de clés de Bob
jshell> var bobKeyPairGen = KeyPairGenerator.getInstance("DH")
bobKeyPairGen ==> java.security.KeyPairGenerator$Delegate@42d8062c

jshell> var dhParams = ((DHPublicKey)alicePublicKey).getParams()
dhParams ==> javax.crypto.spec.DHParameterSpec@cb51256

jshell> bobKeyPairGen.initialize(dhParams)

jshell> var bobKeyPair = bobKeyPairGen.generateKeyPair()
bobKeyPair ==> java.security.KeyPair@5bcea91b

La clé publique d'Alice restaurée est de type DHPublicKey Il est devenu. En utilisant cette méthode getPrams (), Obtenez les paramètres spécifiques de l'échange de clés généré du côté d'Alice.

Une fois les paramètres d'échange de clés obtenus, une paire de clés côté Bob est générée à partir des valeurs.

Bob


//Générer le KeyAgreement de Bob et initialiser avec la clé privée de Bob
jshell> var bobKeyAgree = KeyAgreement.getInstance("DH")
bobKeyAgree ==> javax.crypto.KeyAgreement@27f723

jshell> bobKeyAgree.init(bobKeyPair.getPrivate())

//Sortie de la clé publique de Bob dans un fichier
jshell> writeFile("bob-dh-public-key", bobKeyPair.getPublic().getEncoded())

Initialisez KeyAgreement du côté de Bob avec la clé privée de Bob.

Envoyez ensuite la clé publique de Bob à Alice.

Alice


//Lire la clé publique de Bob
jshell> var encodedBobPublicKey = readFile("bob-dh-public-key")
encodedBobPublicKey ==> byte[557] { 48, -126, 2, 41, 48, -126, 1, 27, 6,  ... 5, -20, 52, -85, -95, 51 }

//Restaurer la clé publique de Bob du côté d'Alice
jshell> var aliceKeyFactory = KeyFactory.getInstance("DH")
aliceKeyFactory ==> java.security.KeyFactory@7b69c6ba

jshell> var bobPublicKeySpec = new X509EncodedKeySpec(encodedBobPublicKey)
bobPublicKeySpec ==> java.security.spec.X509EncodedKeySpec@12f41634

jshell> var bobPublicKey = aliceKeyFactory.generatePublic(bobPublicKeySpec)
bobPublicKey ==> SunJCE Diffie-Hellman Public Key:
y:
    f82361 ...
g:
    02
l:
    1024

Alice récupère la clé publique reçue de Bob. La procédure est la même que lorsque Bob a restauré la clé publique d'Alice.

Alice


//Générer une clé privée basée sur les informations de clé publique de Bob
jshell> aliceKeyAgree.doPhase(bobPublicKey, true)
$20 ==> null

jshell> var secret = aliceKeyAgree.generateSecret()
secret ==> byte[256] { 102, 50, -83, 94, 119, -90, -46, -27, ... 66, -94, -121, -19, -119 }

Bob


//Générer une clé privée basée sur les informations de clé publique d'Alice
jshell> bobKeyAgree.doPhase(alicePublicKey, true)
$25 ==> null

jshell> var secret = bobKeyAgree.generateSecret()
secret ==> byte[256] { 102, 50, -83, 94, 119, -90, -46, -27, ... 66, -94, -121, -19, -119 }

Enfin, chacun des KeyAgreement doPhase (), la préparation de la génération de clé privée est terminée.

Utilisez generateSecret () pour générer la clé privée. ..

Ci-dessous, nous avons confirmé que le cryptage et le décryptage peuvent être effectués à l'aide de la clé privée générée.

Alice


//Générer la clé privée AES en utilisant les 128 premiers bits de la clé générée
jshell> var secretKey = new SecretKeySpec(secret, 0, 16, "AES")
secretKey ==> javax.crypto.spec.SecretKeySpec@fffe86a4

//Chiffré avec AES
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5PAdding")
cipher ==> javax.crypto.Cipher@12cdcf4

jshell> cipher.init(Cipher.ENCRYPT_MODE, secretKey)

//Exporter le code
jshell> writeFile("cryptograph", cipher.doFinal("Hello Diffie-Hellman!!".getBytes()))

//Exporter le vecteur d'initialisation
jshell> writeFile("iv", cipher.getParameters().getEncoded())

Bob


//Générer une clé privée pour AES ainsi que pour Alice
jshell> var secretKey = new SecretKeySpec(secret, 0, 16, "AES")
secretKey ==> javax.crypto.spec.SecretKeySpec@fffe86a4

//Restaurer le vecteur d'initialisation à partir du fichier
jshell> var params = AlgorithmParameters.getInstance("AES")
params ==>

jshell> params.init(readFile("iv"))

//Préparer le chiffrement en mode de déchiffrement
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher ==> javax.crypto.Cipher@cb51256

jshell> cipher.init(Cipher.DECRYPT_MODE, secretKey, params)

//Décrypter le code
jshell> var message = cipher.doFinal(readFile("cryptograph"))
message ==> byte[22] { 72, 101, 108, 108, 111, 32, 68, 105, 1 ... 08, 109, 97, 110, 33, 33 }

jshell> new String(message)
$31 ==> "Hello Diffie-Hellman!!"

référence

Recommended Posts

Mémo d'utilisation de JCA (Java Encryption Architecture)
Mémo Java
java quoi que ce soit mémo
Mémo Java Silver
java, mémo maven
Notes d'utilisation de JavaParser
Remarque sur Java SE 7
Notes d'utilisation de WatchService
Mémo d'utilisation PlantUML
java n'importe quoi mémo 2
Mémo de spécification Java
Notes d'utilisation de JUnit5
Mémo de modèle Java
Mémo de l'environnement de développement Java
Notes d'utilisation de Spring Shell
mémo de connaissances de base java
Mémo d'apprentissage Java (méthode)
Mémo Java Kuche Day
java se 8 programmeur Ⅰ mémo
Mémo privé payant Java
Comment utiliser les variables Java
Mémo d'apprentissage Java (basique)
mémo d'expression java lambda
(Mémo) Java pour instruction
expression lambda java [écriture de notes]
Mémo d'apprentissage Java (interface)
[Java] Mémo d'héritage implicite
Mémo d'apprentissage Java (héritage)
mémo de programmation du concours java
[Mémo] Liste liée Java
Remarque sur Java (WebSphere Application Server) [1]
[Java] Mémo de nom du nom de variable
Sous-chaîne de mémo Java (classe standard)
[Java] Utilisation de Mirage-Basic de SQL
Mémo d'apprentissage Java (type de données)
Mémo d'utilisation de Spring Security CSRF
Longueur du mémo Java (classe standard)
Mémo d'utilisation de Spring Security Run-As
Mémo de la méthode d'étude Java Silver
Créer une méthode java [Memo] [java11]
Mémo de préparation à l'examen Java Silver
Règles d'utilisation pour la gestion des exceptions Java
Spring Security Usage Memo Method Security
Mémo d'utilisation de Spring Security Remember-Me
Mémo d'apprentissage Java (opérateur logique)
Mémo d'apprentissage Java (classe abstraite)
[Java] Date Termes associés mémo
Notes sur l'utilisation du plug-in de gestion des dépendances
Mémo d'étude Java 2 avec Progate
Mémo d'utilisation de Spring Security CORS
[Java11] Résumé de l'utilisation du flux -Basics-
Test de mémo d'utilisation de Spring Security
Que sont les métriques Java? _Memo_20200818
Java HashMap, entrySet [Mémo personnel]