JCA-Verwendungsprotokoll (Java Encryption Architecture)

Was ist JCA?

Abkürzung für Java Cryptography Architecture. API, Framework für die Verwendung von Kryptografie in Java.

Umgebung

Java openjdk 11.0.2

OS Windows 10 (64bit)

Vorausgesetztes Wissen

Um JCA nutzen zu können, benötigen Sie Grundkenntnisse über kryptografische Technologie (welche Art von Technologie Sie haben, welche Art von Mechanismus es ist usw.). Wenn Sie dies nicht wissen, verstehen Sie möglicherweise nicht die Bedeutung der Klassenstruktur und der korrekten Verwendung. Im schlimmsten Fall kann es in tatsächlichen Nutzungssituationen problematisch verwendet werden, und es kann eine Sicherheitslücke eingebettet sein.

Daher ist es zunächst erforderlich, zunächst die Verschlüsselungstechnologie zu untersuchen.

Eine Beschreibung der Kryptographie finden Sie unter hier.

Vordefinierte Funktionen

Jshell wird zur Überprüfung verwendet. Häufig verwendete Prozesse werden als Funktionen im Voraus definiert und ohne besondere Erklärung verwendet.

Vordefinierte Funktionen


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

//Konvertieren Sie ein Byte-Array in eine Zeichenfolge in hexadezimaler Schreibweise
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();
}

//Ausgabe-Byte-Array in Datei
void writeFile(String path, byte[] bytes) throws IOException {
    Files.write(Paths.get(path), bytes);
}

//Lesen Sie die Datei als Byte-Array
byte[] readFile(String path) throws IOException {
    return Files.readAllBytes(Paths.get(path));
}

Vorsichtsmaßnahmen

Da die Operationsüberprüfung von JShell durchgeführt wird, wird das "close ()" des Eingabe- / Ausgabestreams weggelassen, um die Beschreibung zu vereinfachen. Vergessen Sie nicht, "close ()" zu tun, wenn Sie es tatsächlich verwenden.

Grundmechanismus von JCA

Design Konzept

JCA basiert auf der Grundpolitik, Folgendes zu erreichen.

  1. Unabhängigkeit und Interoperabilität der Implementierung
  2. Algorithmusunabhängigkeit und Erweiterbarkeit

Implementierungsunabhängigkeit

Die verschiedenen von JCA bereitgestellten kryptografischen Technologien (Verschlüsselung, Hash-Funktionen, digitale Signaturen usw.) bestehen aus mehreren Implementierungen aufgrund verschiedener Umstände (wie US-Recht und das Aufkommen neuer kryptografischer Technologien). Es gibt. Diese Komponente, die eine Implementierung der Kryptografie bereitstellt, wird als ** Verschlüsselungsdienstanbieter ** (oder einfach als Anbieter) bezeichnet.

Implementierungsunabhängigkeit bezieht sich darauf, dass Sie nicht über die Existenz von Anbietern informiert werden, die diese Implementierungen bereitstellen.

Mit anderen Worten, wenn Sie nur "Ich möchte den Code verwenden" ohne ** "deklarieren, möchte ich den Code verwenden, damit ich ihn unterstütze ... Geben Sie den Anbieter an" ** Danach findet JCA einen Anbieter, der dies unterstützt, und löst die Implementierungsklasse auf.

Interoperabilität der Implementierung

Die Implementierungsinteroperabilität bezieht sich auf die Fähigkeit, mehrere Anbieter zu kombinieren.

Mit anderen Worten, verwenden Sie den Anbieter, um die Hash-Funktion zu implementieren, verwenden Sie den Anbieter, um Zufallszahlen zu generieren, verwenden Sie den *** Anbieter für die Schlüsselgenerierung und verwenden Sie den @@@ Anbieter für die digitale Signatur und Verschlüsselung. Du kannst es schaffen.

Algorithmusunabhängigkeit

Normalerweise gibt es mehrere Algorithmen in einer kryptografischen Technik. Für Hash-Funktionen (kryptografische Technologie) gibt es beispielsweise mehrere Algorithmen wie SHA-1, SHA-2, SHA-3 und MD5.

Algorithmusunabhängigkeit bedeutet, dass Kryptographie verwendet werden kann, ohne von einem bestimmten Algorithmus abhängig zu sein.

Beispielsweise wird die Hash-Funktion von einer Klasse namens "MessageDigest" abstrahiert. Diese Klasse definiert APIs, die nicht von einem bestimmten Algorithmus abhängen. Es ist notwendig, den Algorithmus beim Erstellen einer Instanz von "MessageDigest" anzugeben, aber der nachfolgende Hashing-Prozess kann unabhängig vom Algorithmus mit derselben Implementierung realisiert werden.

Erweiterbarkeit des Algorithmus

Die Erweiterbarkeit von Algorithmen bezieht sich auf das einfache Hinzufügen und Verwenden neuer kryptografischer Algorithmen, wenn diese erfunden und implementiert werden.

Verschlüsselungsdienstanbieter

Eine Komponente, die eine Implementierung einer oder mehrerer kryptografischer Techniken bereitstellt, wird als ** Verschlüsselungsdienstanbieter ** bezeichnet. Wenn wir im Kontext von JCA einfach "Anbieter" sagen, meinen wir einen Verschlüsselungsdienstanbieter.

Im JDK ist mindestens ein Anbieter installiert. Anbieter können statisch oder dynamisch hinzugefügt werden.

Die tatsächlichen Anbieter, die standardmäßig im JDK installiert sind, werden nachfolgend beschrieben (da es sich um ein Oracle Java-Dokument handelt, kann es sich vom OpenJDK unterscheiden?). JDK-Provider-Dokument

Suchen Sie nach einem Anbieter

Geben Sie beim Erstellen einer Instanz einer Klasse, die sich auf eine bestimmte kryptografische Technik bezieht, einen Algorithmus zum Erstellen der Instanz an.

Um beispielsweise eine Instanz von "MessageDigest" durch Angabe des SHA-256-Algorithmus zu erstellen, implementieren Sie Folgendes.

jshell


jshell> import java.security.*

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

Anschließend fragt JCA die installierten Anbieter nacheinander: "Unterstützt SHA-256" MessageDigest "?". Es erhält dann die konkrete Implementierung vom ersten gefundenen unterstützten Anbieter.

Auf diese Weise können Anwendungen kryptografische Technologien verwenden, ohne den jeweiligen Anbieter zu kennen. Es ist möglich, eine Instanz durch Angabe des Namens des Anbieters abzurufen, dies wird jedoch nicht empfohlen.

Überprüfen Sie die in der Ausführungsumgebung installierten Anbieter

Eine Klasse mit dem Namen java.security.Provider wird als Klasse für den Anbieter vorbereitet. ing.

Alle in der aktuellen Ausführungsumgebung installierten "Provider" sind getProviders () von "java.security.Security". Es kann mit java.base / java / security / Security.html # getProviders ()) abgerufen werden.

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

Wenn Sie es als Dokument bezeichnen möchten, 4 JDK Provider Document Ich denke du solltest dich umschauen.

Motorklasse

Eine Klasse, die eine bestimmte kryptografische Technologie bereitstellt, wird als ** Engine-Klasse ** bezeichnet.

Bestimmte Motorklassen umfassen (nur einige):

Klasse Zu liefernde Funktionen
SecureRandom Unvorhersehbare Zufallszahlengenerierung für die Kryptographie
MessageDigest Hash-Funktion zur Verschlüsselung
Signature Erstellung und Überprüfung digitaler Signaturen
Cipher Verschlüsselung und Entschlüsselung
Mac Nachrichtenüberprüfungscode
KeyGenerator Generierung privater Schlüssel
KeyPairGenerator Schlüsselpaargenerierung
KeyStore Schlüsselspeicher, der Schlüssel verwaltet

Holen Sie sich eine Instanz

jshell


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

Die Engine-Klasse bietet eine statische Factory-Methode namens getInstance (). Indem Sie den Namen des Algorithmus im Argument dieser Factory-Methode angeben, können Sie die Instanz abrufen, die den Algorithmus implementiert.

Beachten Sie, dass der Name des Algorithmus ** unempfindlich ** ist.

Von den Spezifikationen unterstützte Algorithmen

Jede Motorklasse verfügt über einen Algorithmus, der von der Spezifikation unterstützt werden muss.

Welche Algorithmen unterstützt werden müssen, wird im Javadoc für jede Motorklasse beschrieben.

Beispielsweise sind im Fall der Klasse "MessageDigest" ab Java 11 die folgenden drei erforderlich.

MessageDigest (Java SE 11 & JDK 11 )

Überprüfen Sie die vom Anbieter der Ausführungsumgebung unterstützten Algorithmen

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 ist eine Klasse, die die einzelnen vom Anbieter bereitgestellten kryptografischen Funktionen darstellt. Es gibt eine Klasse namens security / Provider.Service.html). Dieser "Service" kann mit den verschiedenen Getter-Methoden von "Provider" bezogen werden.

Service bietet eine Methode, um Informationen über die von ihm unterstützten kryptografischen Techniken abzurufen. Zum Beispiel aus getType (), " Sie können die Art der vom Dienst unterstützten kryptografischen Technologie abrufen, z. B. MessageDigest " getAlgorithm () Von der Methode `` Sie können den Namen eines bestimmten Algorithmus abrufen, z. B. SHA-256 "`.

Wenn Sie es als Dokument bezeichnen möchten, verwenden Sie Java Security Standard Algorithm Name. Ich denke du solltest es sehen.

Wenn Sie die unterstützten Algorithmen für jeden bestimmten Anbieter überprüfen möchten, JDK-Anbieterdokument FE2D2E28-C991-4EF9-9DBE-2A4982726313) Ich denke, Sie sollten es sich ansehen.

Algorithmusunabhängige API

Die Engine-Klasse stellt die API in Bezug auf die von der Klasse bereitgestellte kryptografische Technologie algorithmisch unabhängig bereit.

Mit anderen Worten, sowohl SHA-256 als auch MD5 können Hash-Werte mit derselben Implementierung mithilfe von "MessageDigest" generieren.

jshell


//Berechnen Sie den Hashwert mit 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-Berechnen Sie den Hashwert bei 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 }

↑ ist ein Beispiel für die Erzeugung von Hashwerten durch jshell mit MD5 und SHA-256. Die Implementierung des Teils, der den Hashwert berechnet, ist für beide Algorithmen gleich. Nur der Algorithmus, der beim Erstellen einer Instanz von "MessageDigest" angegeben wurde, ist unterschiedlich.

Warum das Paket in zwei Teile geteilt wird

Die von JCA bereitgestellten Klassen sind größtenteils das Paket javax.crypto. java.security Es wird separat in Paketen bereitgestellt.

Dafür gibt es einen historischen Grund. Es gab eine Zeit, in der die Vereinigten Staaten den Export von Kryptographie stark einschränkten.

[Kryptografische Exportbeschränkungen aus den USA-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)

Die JCA-Paketstruktur entspricht dieser Regelung. Das Paket "java.security" enthält exportierbare Technologieklassen ("MessageDigest" und "Signature"), und das Paket "javax.crypto" enthält nicht exportierbare Technologieklassen ("Cipher" und "KeyAgreement"). Es wird eingesetzt.

Es scheint, dass die Anbieter auch nach diesen aufgeteilt wurden: [SUN-Anbieter](https://docs.oracle.com/javase/jp/11/security/oracle-providers.html#GUID-3A80CC46-91E1-4E47- [SunJCE-Anbieter](https://docs.oracle.com/javase/jp/11/security/oracle-providers.html#GUID-A47B1249] stellt die von AC51-CB7B782CEA7D bereitgestellten Funktionen in java.security bereit. -593C-4C38-A0D0-68FA7681E0A7) scheint jede der von javax.crypto bereitgestellten Funktionen zu implementieren.

Als die Vorschriften streng waren, schien SunJCE als Erweiterung bereitgestellt zu werden, aber jetzt ist es entspannt und mit dem JDK gebündelt.

Referenz:Java-Verschlüsselung| 1.Allgemeine Sicherheit|Sicherheitsentwicklerhandbuch

Message Digest (Hash)

Verwenden Sie die Klasse java.security.MessageDigest, um die Hash-Funktion zu verwenden Machen.

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 () `Der Wert, den Sie für die Methode hashen möchten Wenn Sie ein Byte-Array übergeben, wird der Hash-Wert als Byte-Array zurückgegeben.

Eingang teilen

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 () Verwenden Sie die Methode zur Eingabe Kann in mehrere Teile unterteilt werden.

Wenn Sie alle Bytes auf einmal eingeben müssten, müssten Sie alle Daten in ein Byte-Array einfügen. Es ist kein Problem, wenn die Datengröße des Hash-Ziels klein ist. Wenn Sie jedoch eine große Datei usw. hashen möchten, werden alle Daten im Speicher erweitert, sodass sie streng sind. In einem solchen Fall können Sie die Eingabedaten nach und nach an "update ()" übergeben, damit Sie nicht alle Daten auf einmal in den Speicher lesen müssen.

Durch Ausführen von "Digest ()" wird der Status von "MessageDigest" initialisiert, sodass die Instanz wiederverwendet werden kann.

In hexadezimale Zeichenfolge konvertieren

Das Ergebnis von "Digest ()" ist ein "Byte" -Array, daher ist es schwer zu verstehen, wie es ist. Eine hexadezimale Zeichenfolge wird häufig als Zeichenfolgendarstellung des Hashwerts verwendet. Versuchen Sie daher, sie zu konvertieren.

jshell


// ※toHashString()Ist eine vordefinierte Funktion (siehe oben auf der Seite)
jshell> toHexString(hash)
$6 ==> "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"

Berechnen Sie den Hashwert der Datei

Wenn Sie den Hash-Wert aus einer Datei berechnen möchten, können Sie die Daten aus "FileInputStream" übernehmen und selbst in "MessageDigest" eingeben. Eine Klasse wird jedoch bereitgestellt, um die Implementierung zu vereinfachen.

Lassen Sie uns tatsächlich den SHA-256-Hashwert der ZIP-Datei der Windows-Version OpenJDK 12 berechnen.

jshell


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

//Generieren Sie den InputStream der Datei
jshell> var in = new BufferedInputStream(new FileInputStream("openjdk-12_windows-x64_bin.zip"))
in ==> java.io.BufferedInputStream@10bbd20a

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

//Lesen Sie alle Informationen aus InputStream (nullOutputStream, da kein Leseergebnis erforderlich ist()Wegschmeißen)
jshell> dis.transferTo(OutputStream.nullOutputStream())
$31 ==> 196405895

//Berechnen Sie den Hashwert
jshell> var hash = md.digest()
hash ==> byte[32] { 53, -88, -48, 24, -12, 32, -5, 5, -2,  ... -13, -119, 124, 78, -110 }

Jedes Mal, wenn "DigestInputStream" Daten aus einem Stream liest, werden die gelesenen Daten an "update ()" von "MessageDigest" übergeben.

jshell


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

//Generieren Sie den InputStream der Datei
jshell> var in = new BufferedInputStream(new FileInputStream("openjdk-12_windows-x64_bin.zip"))
in ==> java.io.BufferedInputStream@5a1c0542

//Generieren Sie DigestOutputStream (nullOutputStream, da keine schriftlichen Informationen benötigt werden()Wegschmeißen)
jshell> var dos = new DigestOutputStream(OutputStream.nullOutputStream(), md)
dos ==> [Digest Output Stream] SHA-256 Message Digest from SUN, <initialized>

//Lesen Sie alle Informationen aus InputStream und schreiben Sie in DigestOutputStream
jshell> in.transferTo(dos)
$24 ==> 196405895

//Berechnen Sie den Hashwert
jshell> var hash = md.digest()
hash ==> byte[32] { 53, -88, -48, 24, -12, 32, -5, 5, -2,  ... -13, -119, 124, 78, -110 }

Andererseits wird "DigestOutputStream" jedes Mal, wenn Daten in den Stream geschrieben werden, an "update ()" von "MessageDigest" übergeben.

Übrigens, wenn Sie den berechneten Hashwert in eine hexadezimale Zeichenfolge konvertieren,

jshell


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

Auf der OpenJDK-Site veröffentlichter Hash-Wert lautet `35a8d018f420fb05fe7c2aa9933122896ca50bd23dbd372e Kann berechnet werden.

Schlüssel

In der Kryptographie gibt es verschiedene "Schlüssel" (privater Schlüssel, öffentlicher Schlüssel, privater Schlüssel usw.). JCA definiert auch Typen, die verschiedene Schlüssel entsprechend darstellen (SecretKey, PublicKey, PrivateKey usw.).

Es gibt verschiedene Arten von Schlüsseln, aber alle haben eine gemeinsame Schnittstelle zu ihren Eltern. Dies ist die Schnittstelle Key, bei der es sich um den Typ der obersten Ebene handelt, der den "Schlüssel" darstellt. Wird sein.

In "Schlüssel" sind drei Methoden für den Zugriff auf die Schlüsselinformationen definiert.

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

Schlüsselspezifikationen

Informationen wie Algorithmen können von "Schlüssel" erhalten werden, aber bestimmte Daten, aus denen der Schlüssel besteht, können nicht erhalten werden. Die Unfähigkeit, auf diese Weise auf bestimmte Schlüsseldaten zuzugreifen, wird von JCA als ** undurchsichtiger Ausdruck ** bezeichnet.

Andererseits gibt es auch Typen, die auf die spezifischen Daten zugreifen können, aus denen der Schlüssel besteht. Das ist KeySpec (Schlüsselspezifikation).

Da der Zweck von "KeySpec" selbst darin besteht, auszudrücken, dass "dieser Typ eine Schlüsselspezifikation ist", ist die API für den tatsächlichen Zugriff auf die Schlüsseldaten nicht definiert. Die eigentliche API ist in der Implementierungsklasse "KeySpec" definiert.

Beispielsweise zeigt DSAPrivateKeySpec die bei der Schlüsselgenerierung verwendete Primzahl an. Sie können jetzt auf Informationen zu $ p $ und dem privaten Schlüssel $ x $ zugreifen.

Der Zugriff auf die spezifischen Daten, aus denen der Schlüssel besteht, wird von JCA als ** transparente Darstellung ** bezeichnet.

Der tatsächliche Key und KeySpec und einige Klassenhierarchien sehen folgendermaßen aus:

jca.png

Generator und Fabrik

JCA bietet zwei Möglichkeiten zum Erstellen eines Schlüssels: "Generator" und "Factory".

Generator bietet die Möglichkeit, einen neuen Schlüssel zu erstellen. Sie können beispielsweise einen neuen Schlüssel generieren, indem Sie Parameter wie die Schlüssellänge angeben.

Auf der anderen Seite bietet Factory hauptsächlich die Funktion zum Konvertieren zwischen Schlüsseln und Schlüsselspezifikationen. Es scheint, dass einige Schlüssel denselben Schlüssel aus zwei verschiedenen Schlüsselspezifikationen generieren können.

Insbesondere existieren die folgenden Klassen.

Verschlüsseln entschlüsseln

Sowohl die Verschlüsselung mit allgemeinem Schlüssel als auch die Verschlüsselung / Entschlüsselung mit öffentlichem Schlüssel sind javax.crypto.Cipher. /crypto/Cipher.html) Verwenden Sie die Klasse.

Spezifikation des Verschlüsselungsalgorithmus

jshell


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

Die an getInstance () von Cipher übergebene Zeichenfolge ist kein einfacher Algorithmusname, sondern es kann auch der Blockverschlüsselungsmodus usw. angegeben werden.

Das spezifische Format ist "Algorithmus / Modus / Auffüllen".

Geben Sie für "Algorithmus" den Namen eines Verschlüsselungsalgorithmus wie AES, DES oder RSA an. mode ist ein Blockverschlüsselungsmodus, der EZB, CBC usw. angibt. padding gibt an, wie die fehlenden Daten aufgefüllt werden sollen, wenn die zu verschlüsselnden Daten kein ganzzahliges Vielfaches der Blockgröße sind.

Es ist auch möglich, nur "Algorithmus" wie "Cipher.getInstance (" AES ")" anzugeben. In diesem Fall werden die vom Anbieter festgelegten Standardwerte für den Modus und das Auffüllen verwendet. In den meisten Fällen ist ** der Standardmodus EZB **, daher ist es immer eine gute Idee, den Modus und die Auffüllung anzugeben.

Initialisieren

Um Cipher verwenden zu können, müssen Sie es zuerst initialisieren.

Geben Sie zur Initialisierung "Art der durchzuführenden Verarbeitung" und "Parameter dafür" an. Beispielsweise werden "Verschlüsselung" und "Schlüssel" zur Initialisierung übergeben.

jshell


//Schlüssel generieren
jshell> var keyGen = KeyGenerator.getInstance("AES")
keyGen ==> javax.crypto.KeyGenerator@365185bd

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

//Verschlüsselungsinitialisierung
jshell> cipher.init(Cipher.ENCRYPT_MODE, key)

//Initialisierungsparameter abrufen
jshell> var params = cipher.getParameters()
params ==>
    iv:
[0000: F1 D7 23 45 DA A6 7B 42   66 AF ...  46  ..#E...Bf.z..a.F
]

Verwenden Sie zur Initialisierung die Methode init ().

Geben Sie den Initialisierungstyp im ersten Argument an. Der Initialisierungstyp ist [In Cipher definierte Konstanten](https://docs.oracle.com/javase/jp/11/docs/api/java.base/javax/crypto/Cipher.html#field. Zusammenfassung) verwendet wird. "ENCRYPT_MODE" befindet sich im Verschlüsselungsmodus und "DECRYPT_MODE" befindet sich im Entschlüsselungsmodus.

Initialisierungsparameter werden nach dem zweiten Argument übergeben. Geben Sie grundsätzlich den Schlüssel an, der für die Ver- / Entschlüsselung verwendet wird.

Abhängig vom Verschlüsselungsalgorithmus können zusätzliche Parameter erforderlich sein. In diesem Fall erhalten Sie zusätzliche Parameter wie "AlgorithmParameterSpec" init () Verwenden Sie die Methode #init (int, java.security.Key, java.security.spec.AlgorithmParameterSpec)). AlgorithmParameterSpec ist eine Schnittstelle, die algorithmische Parameter darstellt. Es gibt Implementierungsklassen, die für verschiedene Verschlüsselungsalgorithmen spezifisch sind.

Selbst für Algorithmen, die zusätzliche Parameter erfordern, kann der Anbieter Standardwerte für Sie festlegen. In diesem Fall kann es nur mit der Methode init () initialisiert werden, die den Verarbeitungsmodus und den Schlüssel empfängt. Die derzeit verwendeten Parameter sind getParameters () Es kann durch das Verfahren erhalten werden.

jshell


//Verschlüsselungsinitialisierung
jshell> cipher.init(Cipher.ENCRYPT_MODE, key)

//Initialisierungsparameter abrufen
jshell> var params = cipher.getParameters()
params ==>
    iv:
[0000: F1 D7 23 45 DA A6 7B 42   66 AF ...  46  ..#E...Bf.z..a.F
]

Hier wird der Schlüssel für AES generiert und "Cipher" im verschlüsselten Modus initialisiert.

Da der Modus zum Zeitpunkt der "Verschlüsselung" -Erzeugung auf CBC eingestellt war, ist ursprünglich der Parameter des Initialisierungsvektors (IV: ** I ** Nitialisierung ** V ** Ektor) erforderlich. Da jedoch die Angabe des Initialisierungsvektors in "init ()" weggelassen wurde, erzeugte der Anbieter den Initialisierungsvektor gut.

Der Initialisierungsvektor wird auch für die Initialisierung des Decodierungsmodus benötigt, daher muss er mit "getParameters ()" erhalten werden. Wenn für den verwendeten Verschlüsselungsalgorithmus keine zusätzlichen Parameter erforderlich waren, gibt getParameters () null zurück.

Durch die Initialisierung werden alle internen Zustände von "Cipher" zurückgesetzt. Mit anderen Worten, eine Instanz von "Cipher", die früher zur Verschlüsselung verwendet wurde, kann jetzt zur Entschlüsselung wiederverwendet werden. Wenn Sie dagegen während der Verschlüsselung in den Entschlüsselungsmodus initialisieren, gehen die während des Verschlüsselungsprozesses festgelegten Informationen verloren. Seien Sie also vorsichtig.

Verschlüsseln entschlüsseln

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 () Mit Methode verschlüsseln Durch Übergeben des Werts, den Sie konvertieren / entschlüsseln möchten ("Byte" -Array), können Sie den verschlüsselten / einfachen Text ("Byte" -Array) erhalten.

Ähnlich wie bei "MessageDigest" lautet die Eingabe [update ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/javax/crypto/Cipher.html#update (Byte) Es kann auch mit% 5B% 5D)) aufgeteilt werden. Im Gegensatz zu "MessageDigest" wird das Ergebnis der Verschlüsselung / Entschlüsselung jedoch jedes Mal zurückgegeben, wenn "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 }

Wenn Sie die Blockverschlüsselung verwenden, wird außerdem ein leeres Byte-Array zurückgegeben, bis die Eingabegröße der Größe des Blocks entspricht. Für AES beträgt die Blockgröße 128 Bit (16 Byte), sodass der Rückgabewert von "update ()" ein leeres Array ist, bis die Eingabe 16 Byte erreicht.

Wenn "doFinal ()" aufgerufen wird, wird die Instanz von "Cipher" bei der letzten Initialisierung zurückgegeben. Das heißt, es kann wiederverwendet werden, um eine andere Eingabe so wie sie ist zu verschlüsseln / entschlüsseln.

CipherInputStream/CipherOutputStream Ähnlich wie bei "DigestInputStream" / "DigestOutputStream" gibt es eine Klasse, die "Cipher" und "InputStream" / "OutputStream" verbindet.

Das Bild der einzelnen Daten und der Ver- / Entschlüsselungsfluss sind in der folgenden Abbildung dargestellt.

jca.jpg

Erstens, wenn Sie "CipherInputStream" verwenden.

jshell


//Erstellen Sie einen zu verschlüsselnden InputStream
jshell> var is = new ByteArrayInputStream("Java Cryptography Architecture".getBytes())
is ==> java.io.ByteArrayInputStream@5fcd892a

//Erstellen Sie CipherInputStream
jshell> var cis = new CipherInputStream(is, cipher)
cis ==> javax.crypto.CipherInputStream@b9afc07

//Erstellen Sie OutputStream des Ausgabeziels
jshell> var out = new ByteArrayOutputStream()
out ==>

//Extrahiert alle Informationen aus InputStream und schreibt das Verschlüsselungsergebnis in OutputStream
jshell> cis.transferTo(out)
$67 ==> 32

//Überprüfen Sie das in OutputStream geschriebene Ergebnis
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 }

Dann bei Verwendung von CipherOutputStream.

jshell


//Erstellen Sie einen zu verschlüsselnden InputStream
jshell> var is = new ByteArrayInputStream("Java Cryptography Architecture".getBytes())
is ==> java.io.ByteArrayInputStream@133e16fd

//Erstellen Sie OutputStream, um die Verschlüsselung auszugeben
jshell> var out = new ByteArrayOutputStream()
out ==>

//Erstellen Sie CipherOutputStream
jshell> var cos = new CipherOutputStream(out, cipher)
cos ==> javax.crypto.CipherOutputStream@51b279c9

//Gießen Sie das Verschlüsselungsziel in CipherOutputStream
jshell> is.transferTo(cos)
$92 ==> 30

// doFinal()Schließen Sie CipherOutputStream, um es auszuführen()
jshell> cos.close()

//Überprüfen Sie den 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 }

Sowohl für "CipherInputStream" als auch für "CipherOutputStream" wird "doFinal ()" von "Cipher" bei "close ()" ausgeführt. Wenn Sie also nicht vergessen, close () auszuführen, ist das Ergebnis auf halbem Weg [^ 1](Da es sich um einen Stream handelt, müssen Sie sich keine Sorgen machen, da er grundlegende Versuche mit Ressourcen usw. verwendet. Ich denke).

[^ 1]: CipherInputStream ist auch ohne close () nicht halb fertig, aber die Implementierung scheintdoFinal ()zu sein, wenn der Eingabestream das Ende erreicht.

Sollte transferTo () nicht verwendet werden?

Das Javadoc von CipherInputStream sagt: ..

Programmierer, die diese Klasse verwenden, verwenden niemals Methoden, die nicht in dieser Klasse definiert oder nicht überschrieben wurden (z. B. neue Methoden oder Konstruktoren, die später zu einer der Oberklassen hinzugefügt wurden). Bitte **. Das Design und die Implementierung dieser Methoden berücksichtigen möglicherweise nicht die Sicherheitsauswirkungen von CipherInputStream.

Für "InputStream" in Java 9 hingegen [transferTo ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/io/InputStream.html#transferTo () Eine praktische Methode namens java.io.OutputStream)) wurde hinzugefügt.

Dieses "transferTo ()" wird nicht von "CipherInputStream" überschrieben, daher erfüllt es die Bedingungen, die nicht verwendet werden sollten.

Dieses "transferTo ()" ist jedoch in OpenJDK 11 wie folgt implementiert.

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;
    }

Hier werden nur "read ()" von "InputStream" und "write ()" von "OutputStream" verwendet.

Wenn Sie versuchen, einen Prozess ohne Verwendung von "transferTo ()" zu schreiben, schreiben Sie am Ende dieselbe Implementierung wie "transferTo ()". Und die zu diesem Zeitpunkt verwendete Methode wird "read ()" überschrieben von "CipherInputStream" oder "write ()" von "CipherOutputStream".

Dann denke ich persönlich, dass es doch kein Problem gibt, "transferTo ()" zu verwenden.

Da diese Implementierung jedoch auf OpenJDK beschränkt ist, besteht die Möglichkeit, dass andere Java-Implementierungen Sicherheitsprobleme aufweisen.

Benutzung auf eigene Gefahr.

AES Versuchen Sie, die AES-Verschlüsselung und -Entschlüsselung zu implementieren.

Schlüsselgenerierung

jshell


//Erstellen Sie KeyGenerator mit dem AES-Algorithmus
jshell> var keyGen = KeyGenerator.getInstance("AES")
keyGen ==> javax.crypto.KeyGenerator@4d50efb8

//Schlüsselgenerierung
jshell> var key = keyGen.generateKey()
key ==> javax.crypto.spec.SecretKeySpec@177c6

Verwenden Sie KeyGenerator, um den Schlüssel zu generieren. Für SunJCE-Anbieter beträgt die Standardschlüssellänge 128 Bit.

Festlegen der Schlüssellänge

jshell


jshell> keyGen.init(256)

Sie können die Schlüssellänge mit der Methode init () angeben. Es können nur 128, 192 oder 256 Schlüssellängen angegeben werden (AES-Spezifikation).

Verschlüsselung

jshell


//Generieren Sie eine Verschlüsselung mit AES
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher ==> Cipher.AES/CBC/PKCS5Padding, mode: not initialize ... orithm from: (no provider)

//Im verschlüsselten Modus initialisieren
jshell> cipher.init(Cipher.ENCRYPT_MODE, key)

//Informationen zum Initialisierungsvektor abrufen
jshell> var params = cipher.getParameters()
params ==>
    iv:
[0000: 88 8E 06 A0 89 80 0D 11   FF ED ...  45  ...........&.'.E
]

//Verschlüsselung
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 }

//Überprüfen Sie, ob es verschlüsselt ist
jshell> new String(c)
$24 ==> "Nonne[Ome?1ju Sehi "Li"\017}Zum ersten Mal PW ”\026Z?|?"

Da die Initialisierungsvektorinformationen zum Zeitpunkt der Decodierung erforderlich sind, rufen Sie die Parameterinformationen mit "getParameters ()" ab.

Entschlüsselung

jshell


//Im Entschlüsselungsmodus initialisieren
jshell> cipher.init(Cipher.DECRYPT_MODE, key, params)

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

//Überprüfen Sie, ob es entschlüsselt werden kann
jshell> new String(p)
$23 ==> "Java Cryptography Architecture"

Bei init () müssen Sie nicht nur den Schlüssel, sondern auch die Parameter angeben, die zum Initialisieren der Verschlüsselung verwendet werden.

Geben Sie den Schlüssel in eine Datei aus

jshell


//Ausgabeschlüssel zur Datei
jshell> writeFile("secret-key", key.getEncoded())

//Geben Sie die während der Verschlüsselung verwendeten Parameter in eine Datei aus
jshell> writeFile("aes-params", params.getEncoded())

//Kryptografische Erzeugung
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 }

//Ausgabecode in Datei
jshell> writeFile("aes-cryptogram", c)

Die codierten Daten des privaten Schlüssels sind die [getEncoded ()] von "Key" (https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/Key.html#) Es kann mit getEncoded ()) erhalten werden. Außerdem lautet die Parameterinformation getEncoded () von AlgorithmParameters. )) erhalten werden.

Schlüssel aus Datei wiederherstellen

jshell


//Lesen Sie die geheimen Schlüsselinformationen aus der Datei und generieren Sie SecretKeySpec
jshell> Key key = new SecretKeySpec(readFile("secret-key"), "AES")
key ==> javax.crypto.spec.SecretKeySpec@178a8

//Holen Sie sich eine Instanz von AlgorithmParameters und initialisieren Sie sie mit den aus der Datei gelesenen Informationen
jshell> var params = AlgorithmParameters.getInstance("AES")
params ==>

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

//Initialisieren Sie mit den von Cipher generierten und gelesenen Informationen
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher ==> javax.crypto.Cipher@df27fae

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

//Lesen Sie den Code aus der Datei und entschlüsseln Sie ihn
jshell> var m = cipher.doFinal(readFile("aes-cryptogram"))
m ==> byte[13] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 33 }

//Überprüfen Sie das Ergebnis
jshell> new String(m)
$34 ==> "Hello World!!"

Der private Schlüssel kann mit SecretKeySpec wiederhergestellt werden (der Algorithmus lautet ) Geben Sie "AES" an ). Obwohl diese Klasse "KeySpec" ist, implementiert sie auch "Key", sodass sie so wie sie ist als Schlüssel verwendet werden kann.

Parameter können mit AlgorithmParameters wiederhergestellt werden. Verwenden Sie zunächst getInstance (), um Holen Sie sich eine Instanz, indem Sie "AES"angeben. Dann mit init () Parameterdaten einstellen.

Passwortbasierte Verschlüsselung (PBE)

jshell


//Generieren Sie KeySpec für die kennwortbasierte Verschlüsselung
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

//Generieren Sie den Schlüssel aus KeySpec mit 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 verwendet einen Schlüssel namens PBEKey.

PBEKey generiert PBEKeySpec basierend auf dem Passwort. Verwenden Sie dann SecretKeyFactory, um in PBEKey zu konvertieren und es abzurufen. ..

Das Passwort muss in einem Array von char angegeben werden. Der Grund ist im Javadoc von "PBEKeySpec" beschrieben, aber es scheint, dass er später nicht gelöscht werden kann, da der Wert mit "String" unveränderlich ist.

Bei der Instanzerfassung von "SecretKeyFactory" wird "PBEWithHmacSHA256AndAES_128" im Algorithmus angegeben.

Der Algorithmusname hat das Format "PBEWith <Digest | Prf> und <Verschlüsselung>". Wenn Digest | prf MD5 oder SHA1 ist, ist es PBES1 in RFC8018, und wenn Hmac *, wird PBES2 verwendet.

jshell


//Generieren Sie eine Verschlüsselung für die kennwortbasierte Verschlüsselung und initialisieren Sie sie im Verschlüsselungsmodus
jshell> var cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_128/CBC/PKCS5Padding")
cipher ==> javax.crypto.Cipher@5891e32e

Cipher erhält auch eine Instanz mit demselben Algorithmus. (Der SunJCE-Anbieter "Cipher" unterstützt nur die CBC / PKCS5Padding-Modi und das Auffüllen in PBE. Ich bin daher der Meinung, dass es nicht erforderlich ist, dies explizit anzugeben, sondern nur für den Fall.)

jshell


//Erstellen eines Pseudozufallszahlengenerators
jshell> var random = new SecureRandom()
random ==> Hash_DRBG,SHA-256,128,reseed_only

//Erzeugung von Salz
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)

Salz erzeugen.

Salt verwendet einen kryptografischen Pseudozufallszahlengenerator (SecureRandom). Generieren.

In RFC8018 ist die Salzgröße auf mindestens 64 Bit festgelegt, daher habe ich sie vorerst mit 64 Bit generiert.

jshell


//Erklärung der Anzahl der Iterationen
jshell> int iterationCount = 1000
iterationCount ==> 1000

//Generieren Sie AlgorithmParameterSpec für die kennwortbasierte Verschlüsselung
jshell> var keyParamSpec = new PBEParameterSpec(salt, iterationCount)
keyParamSpec ==> javax.crypto.spec.PBEParameterSpec@eec5a4a

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

Generieren Sie PBEParameterSpec unter Verwendung der Anzahl der Iterationen und des Salzwerts. Machen.

Initialisieren Sie dann "Cipher" mit dem zuvor erstellten Schlüssel.

jshell


//Ausführung der Verschlüsselung
jshell> var c = cipher.doFinal("Hello World!!".getBytes())
c ==> byte[16] { 101, -52, -106, 26, 67, 118, -20, 22,  ...  42, -71, 115, 123, -122 }

//Überprüfen Sie das Verschlüsselungsergebnis
jshell> new String(c)
$18 ==> "efu?\032Cv?\026\031\000*S.{?"

Danach wird die Verschlüsselung auf die gleiche Weise wie bei anderen Algorithmen durchgeführt.

jshell


//Rufen Sie die während der Verschlüsselung verwendeten Parameter ab
jshell> var params = cipher.getParameters()
params ==> PBEWithHmacSHA256AndAES_128

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

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

// IV (Initialisierungsvektor)Überprüfen Sie den Wert von
jshell> var iv = ivSpec.getIV()
iv ==> byte[16] { -67, 47, 111, -2, 34, -17, -89, 74, -7 ... 46, -124, 99, 79, 23, 88 }

Da es mit CBC verschlüsselt wurde, ist es notwendig, den Initialisierungsvektor für die Entschlüsselung zu erhalten. Die während der Verschlüsselung automatisch festgelegten Initialisierungsvektorinformationen (IvParameterSpec) sind PBEParameterSpec. Es kann mit der Methode getParameterSpec () von /spec/PBEParameterSpec.html abgerufen werden.

jshell


//Generieren Sie PBEParameterSpec zur Entschlüsselung
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

//Im Entschlüsselungsmodus initialisieren (* Schlüsselgenerierung entspricht der Verschlüsselung)
jshell> cipher.init(Cipher.DECRYPT_MODE, key, pbeParamSpec)

//Entschlüsselung durchführen
jshell> var m = cipher.doFinal(c)
m ==> byte[13] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 33 }

//Überprüfen Sie das Entschlüsselungsergebnis
jshell> new String(m)
$21 ==> "Hello World!!"

Zum Zeitpunkt der Entschlüsselung werden "Key" und "AlgorithmParameterSpec" erneut unter Verwendung der Informationen "Passwort", "Salt" und "Anzahl der Iterationen" generiert und "Cipher" initialisiert. (Da die IV-Informationen zum Zeitpunkt der Verschlüsselung verwendet werden müssen, unterscheidet sich die Generierung von "PBEParameterSpec" geringfügig.)

RSA Versuchen Sie, RSA für die Verschlüsselung mit öffentlichen Schlüsseln zu verwenden.

Schlüsselgenerierung

jshell


//Lassen Sie KeyPairGenerator ein Schlüsselpaar generieren
jshell> var keyPairGen = KeyPairGenerator.getInstance("RSA")
keyPairGen ==> java.security.KeyPairGenerator$Delegate@42dafa95

//Schlüsselpaar generieren
jshell> var keyPair = keyPairGen.generateKeyPair()
keyPair ==> java.security.KeyPair@1dfe2924

Verwenden Sie KeyPairGenerator, um ein Schlüsselpaar für die Verschlüsselung mit öffentlichen Schlüsseln zu generieren. Machen. Da RSA verwendet wird, übergeben Sie "RSA" als Algorithmusnamen.

Wenn Sie "generateKeyPair ()" ausführen, enthält KeyPair den Satz aus öffentlichem und privatem Schlüssel /KeyPair.html) erhalten.

Festlegen der Schlüssellänge

jshell


jshell> keyPairGen.initialize(4096)

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

Schlüssel mit der initialize () -Methode von "KeyPairGenerator" Sie können die Länge angeben.

Verschlüsselung

jshell


//Holen Sie sich Cipher für RSA
jshell> var cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
cipher ==> javax.crypto.Cipher@35a50a4c

//Im verschlüsselten Modus initialisieren
jshell> cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic())

//Ausführung der Verschlüsselung
jshell> var c = cipher.doFinal("Hello World!!".getBytes())
c ==> byte[256] { 44, 48, -56, -42, -117, -48, 17, -76, ... , -15, 63, 46, -115, -42 }

//Überprüfen Sie das Verschlüsselungsergebnis
jshell> new String(c)
$14 ==> ",0 Breite\021\030?0\022:Li 斃 w?9 ァ,Mann| ノ_? Brunft Mond m bis Щa]\002)Ma\024L5XD?齡 訶({x?Shi\024,5, S.@wM-?杣 woo g1\025 "02 Washi Herato J Bonmi?\t ゥ  Rem F F 悊^?\"Miu\023 ゥ&Moho 輙 蓸 子.Mri\037 ku i?9m ED Care gg\023q 亥 X4\021pM3 廱 ヤ\017G\02,4 Milliarden\017 Also?;Q.\177 Winter U5wgL B Flach>\006e?g?9l:\t] 垉 R.\020 Also?nmi?!?&s 㽴?Oder ein\016`H\f6 eingelegt ?Les?4\nfU?.Sai"

Sie können den öffentlichen Schlüssel mit getPublic () von KeyPair erhalten, also verschlüsseln Sie ihn. Das Verfahren ist das gleiche wie bei AES.

Entschlüsselung

jshell


//Holen Sie sich die während der Verschlüsselung generierten Parameter
jshell> var params = cipher.getParameters()
params ==> MD: SHA-256
MGF: MGF1SHA-1
PSource: PSpecified

//Im Entschlüsselungsmodus initialisieren
jshell> cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate(), params)

//Entschlüsselungsausführung
jshell> var m = cipher.doFinal(c)
m ==> byte[13] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 33 }

//Überprüfen Sie das Entschlüsselungsergebnis
jshell> new String(m)
$18 ==> "Hello World!!"

Sie können den privaten Schlüssel mit "getPrivate ()" von "KeyPair" abrufen, verwenden Sie ihn also zur Entschlüsselung. Auch hier ist die Verwendung von "Cipher" dieselbe wie für AES.

Nachrichtenüberprüfungscode (MAC)

jshell


//Generieren Sie einen privaten Schlüssel für MAC
jshell> var keyGen = KeyGenerator.getInstance("HmacSHA256")
keyGen ==> javax.crypto.KeyGenerator@17579e0f

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

//Erstellen Sie eine Instanz von MAC
jshell> var mac = Mac.getInstance("HmacSHA256")
mac ==> javax.crypto.Mac@7a765367

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

//Berechnen Sie den MAC-Wert
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 }

Verwenden Sie die Klasse Mac, um den MAC-Wert zu berechnen.

Der MAC benötigt einen privaten Schlüssel. Verwenden Sie daher "KeyGenerator", um den Schlüssel zu generieren.

Nachdem Sie eine Instanz von "Mac" erstellt haben, initialisieren Sie sie zunächst mit der Methode "init ()" unter Verwendung des privaten Schlüssels. Übergeben Sie als Nächstes den Wert ("Byte" -Array), für den Sie den MAC-Wert berechnen möchten, an "doFinal ()". Der berechnete MAC-Wert wird als Byte-Array zurückgegeben.

Teilen und berechnen

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 }

Ähnlich wie bei "Cipher" usw. können Sie "update ()" verwenden, um den zu berechnenden Wert zu teilen und festzulegen.

Digitale Unterschrift

RSA-Signatur

jshell


//Generieren Sie ein RSA-Schlüsselpaar
jshell> var keyPairGen = KeyPairGenerator.getInstance("RSA")
keyPairGen ==> java.security.KeyPairGenerator$Delegate@42dafa95

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

Generieren Sie zunächst ein RSA-Schlüsselpaar wie bei der RSA-Verschlüsselung.

jshell


//Signaturinstanz abrufen
jshell> var signature = Signature.getInstance("SHA256WithRSA")
signature ==> Signature object: SHA256WithRSA<not initialized>

//Im Signiermodus initialisieren
jshell> signature.initSign(keyPair.getPrivate())

//Registrieren Sie die zu signierenden Daten
jshell> signature.update("Hello World!!".getBytes())

//Digitale Signatur generieren
jshell> var sign = signature.sign()
sign ==> byte[256] { 29, -91, 95, 8, -19, -56, 118, -29, 1 ... 7, -1, 53, 58, -99, -117 }

Verwenden Sie zum Erstellen einer digitalen Signatur die Klasse Signatur.

Signatur hat zwei Initialisierungsmethoden. Eine davon ist initSign () , Der andere ist initVerify () werden.

Wenn Sie eine Signatur erstellen möchten, verwenden Sie initSign (), um sie zu initialisieren. Übergeben Sie zu diesem Zeitpunkt den Signaturschlüssel (privaten Schlüssel), der zum Signieren als Argument verwendet wird.

Nachdem die Initialisierung abgeschlossen ist, besteht der nächste Schritt darin, die zu signierenden Daten mit der Methode "update ()" zu registrieren. Wenn Sie schließlich die Methode sign () ausführen, ist sie digital. Die Signatur wird in einem Byte-Array zurückgegeben.

jshell


//Signatur im Überprüfungsmodus initialisieren
jshell> signature.initVerify(keyPair.getPublic())

//Registrieren Sie die zu überprüfenden Daten
jshell> signature.update("Hello World!!".getBytes())

//Überprüfen Sie
jshell> signature.verify(sign)
$12 ==> true

Verwenden Sie zur Überprüfung "initVerify ()", um "Signature" zu initialisieren. Zu diesem Zeitpunkt wird ein Bestätigungsschlüssel (öffentlicher Schlüssel) als Argument übergeben.

Registrieren Sie auch hier die zu überprüfenden Daten mit update (). Und schließlich verify () Führen Sie eine Validierung mit der Methode durch. Übergeben Sie zu diesem Zeitpunkt ein Byte-Array digitaler Signaturen als Argument.

Wenn die Validierung erfolgreich ist, wird "true" zurückgegeben.

DSA-Signatur

jshell


//Generieren Sie ein Schlüsselpaar für DSA
jshell> var keyPairGen = KeyPairGenerator.getInstance("DSA")
keyPairGen ==> sun.security.provider.DSAKeyPairGenerator$Current@f6c48ac

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

//Signaturinstanz für DSA abrufen
jshell> var signature = Signature.getInstance("SHA256WithDSA")
signature ==> Signature object: SHA256WithDSA<not initialized>

//Im Signiermodus initialisieren
jshell> signature.initSign(keyPair.getPrivate())

//Registrieren Sie die zu signierenden Daten
jshell> signature.update("Hello World!!".getBytes())

//Digitale Signatur generieren
jshell> var sign = signature.sign()
sign ==> byte[63] { 48, 61, 2, 29, 0, -72, 46, -94, 42, 12 ... 4, 59, -38, -5, -48, 127 }

//Im Überprüfungsmodus initialisieren
jshell> signature.initVerify(keyPair.getPublic())

//Registrieren Sie die zu überprüfenden Daten
jshell> signature.update("Hello World!!".getBytes())

//Überprüfen Sie
jshell> signature.verify(sign)
$12 ==> true

Ändern Sie einfach den Algorithmusnamen usw. für DSA, und die Implementierungsmethode ist dieselbe wie für RSA.

keytool keytool | Java Platform, Standard Edition-Tool-Referenz

Das JDK wird mit einem Befehlszeilentool namens ** keytool ** ausgeliefert. Mit keytool können Sie Schlüssel und Zertifikate generieren und verwalten.

Keytool-Hilfe


$ keytool -h
Tools zur Schlüssel- und Zertifikatsverwaltung

Befehl:

 -certreq Generiert eine Zertifikatanforderung
 -changealias Ändert Aliase für Einträge
 -Löschen Löscht den Eintrag
 -exportcert Exportiert das Zertifikat
 -genkeypair Generiert ein Schlüsselpaar
 -genseckey Erzeugt einen privaten Schlüssel
 -gencert Generiert ein Zertifikat aus einer Zertifikatanforderung
 -importcert Importiert ein Zertifikat oder eine Zertifikatkette
 -importpass Passwort importieren
 -importkeystore Importiert einen oder alle Einträge aus einem anderen Keystore
 -Ändern Sie das Schlüsselkennwort für den Eintrag keypasswd
 -list Listet die Einträge im Keystore auf
 -printcert Druckt den Inhalt des Zertifikats
 -printcertreq Druckt den Inhalt der Zertifikatanforderung
 -printcrl Druckt den Inhalt der CRL-Datei
 -storepasswd Ändern Sie das Kennwort für den Keystore-Speicher

So zeigen Sie diese Hilfemeldung an"keytool -?、-h oder--help"Verwenden Sie die
command_Für die Verwendung von Namen,"keytool -command_name --help"Verwenden Sie die.
So geben Sie eine vorkonfigurierte Optionsdatei an-conf <url>Verwenden Sie die Option.

Befehlsformat

keytool <Befehl> <Möglichkeit>

keytool kann verwendet werden, indem der auszuführende Befehl und seine Optionen übergeben werden. Alle Befehle und Optionen haben die Form -name.

Sie können die Befehlsliste mit -h überprüfen. Darüber hinaus finden Sie Hilfe zu jedem Befehl im Detail, indem Sie "-h" an die Option für diesen Befehl übergeben.

-Hilfe zum Befehl certreq


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

Generieren Sie eine Zertifikatanforderung

Möglichkeit:

 -alias <alias>Ein anderer Name für den zu verarbeitenden Eintrag
 -sigalg <alg>Name des Signaturalgorithmus
 -file <file>Name der Ausgabedatei
 -keypass <arg>Schlüsselpasswort
 -keystore <keystore>Name des Schlüsselspeichers
 -dname <name>Identifikationsname
 -ext <value>            X.509 Erweiterung
 -storepass <arg>Keystore-Passwort
 -storetype <type>Keystore-Typ
 -providername <name>Anbietername
 -addprovider <name>Fügen Sie den Sicherheitsanbieter nach Namen hinzu(SunPK CS11 usw.)
   [-providerarg <arg>]    -Konfigurieren Sie addprovider-Argumente
 -providerclass <class>Fügen Sie einen Sicherheitsanbieter mit vollständig qualifiziertem Klassennamen hinzu
   [-providerarg <arg>]    -Konfigurieren Sie das Argument der Anbieterklasse
 -providerpath <list>Pfad der Anbieterklasse
 -v Detaillierte Ausgabe
 -geschütztes Passwort mit Schutzmechanismus

So zeigen Sie diese Hilfemeldung an"keytool -?、-h oder--help"Verwenden Sie die

Schlüsselspeicher

keytool speichert Informationen wie Schlüssel und Zertifikate in einer Datei, die als Keystore bezeichnet wird. Bis Java 8 wurden Keystores im Oracle-eigenen Dateiformat JKS erstellt. Java 9 und höher werden in dem Dateiformat erstellt, das vom PKCS # 12-Standard (RFC7292) definiert wurde.

Jeder Keytool-Befehl erhält jetzt eine Option namens "-keystore". Diese Option gibt den Speicherort der Keystore-Datei an. Wenn nicht angegeben, wird standardmäßig home directory / .keystore als Pfad der Keystore-Datei verwendet.

Wie Sie der Befehlsliste entnehmen können, gibt es keinen dedizierten Befehl zum Erstellen eines neuen Schlüsselspeichers. Die Keystore-Datei wird automatisch erstellt, wenn beim ersten Versuch, einen Schlüssel oder ähnliches zu generieren, keine Datei vorhanden ist.

Der Keystore selbst ist mit einer kennwortbasierten Verschlüsselung verschlüsselt, und Sie müssen das Kennwort eingeben, um auf den Inhalt zugreifen zu können. Das Schlüsselspeicherkennwort wird beim ersten Erstellen des Schlüsselspeichers abgefragt. Die Passwortlänge muss mindestens 6 Stellen betragen.

Auf Keystore-Informationen kann auch über Java-Programme zugegriffen werden. Die Klasse dafür ist KeyStore (Details werden später beschrieben).

Eintrag

Im Keystore gespeicherte Schlüssel und Zertifikate werden zusammen als ** Einträge ** bezeichnet.

Der Schlüsselspeicher ist eine Art Schlüsselwertspeicher, und jeder Eintrag wird verwaltet, indem ihm ein Name mit dem Namen ** alias ** zugewiesen wird. Um die Informationen eines bestimmten Eintrags anzuzeigen / zu bearbeiten, wird der Eintrag durch einen Alias angegeben.

Eintragsart

Es gibt zwei Arten von Einträgen.

Ein "Schlüsseleintrag" ist ein Eintrag, der einen privaten Schlüssel oder einen Satz privater Schlüssel und deren gepaarte öffentliche Schlüsselzertifikate enthält. Das Public-Key-Zertifikat kann ein selbstsigniertes Zertifikat sein oder mit dem Stammzertifikat verkettet sein.

Andererseits enthält der "vertrauenswürdige Zertifikatseintrag" ein ** einzelnes ** Zertifikat mit öffentlichem Schlüssel. In diesem Eintrag wird nur ein öffentliches Schlüsselzertifikat gespeichert (nicht verkettet), auch wenn es von einem anderen öffentlichen Schlüssel signiert ist.

Überprüfen Sie den Inhalt des Schlüsselspeichers

Liste anzeigen

$ keytool -list
Keystore-Typ: PKCS12
Keystore-Anbieter: SUN

Keystore enthält 8 Einträge

hogekey,2019/04/10, SecretKeyEntry,
pbekey,2019/04/10, SecretKeyEntry,
rsakey,2019/04/13, PrivateKeyEntry,
Zertifikat Fingerabdruck(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,
Zertifikat Fingerabdruck(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,
Zertifikat Fingerabdruck(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

Sie können die Liste der Einträge mit dem Befehl -list überprüfen.

Der Eintrag, in dem das Paar aus privatem und öffentlichem Schlüssel gespeichert ist, wird als "PrivateKeyEntry" (Schlüsseleintrag) geschrieben. Der Eintrag für den privaten Schlüssel wird als "SecretKeyEntry" geschrieben (dies ist auch der Schlüsseleintrag). Der Eintrag für ein vertrauenswürdiges Zertifikat wird als "trustedCertEntry" geschrieben.

Anzeige durch Angabe eines Eintrags

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

$ keytool -list -alias rsakey
rsakey,2019/04/13, PrivateKeyEntry,
Zertifikat Fingerabdruck(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

Wenn Sie den Alias des Eintrags, den Sie einzeln anzeigen möchten, mit der Option "-alias" angeben, können Sie nur die Informationen dieses Alias ausgeben.

Detaillierte Informationen können durch Hinzufügen der Option -v ausgegeben werden.

$ keytool -list -alias hogekey -v
alias: hogekey
Erstellungsdatum: 2019/04/10
Eintragsart: SecretKeyEntry

$ keytool -list -alias rsakey -v
alias: rsakey
Erstellungsdatum: 2019/04/13
Eintragsart: PrivateKeyEntry
Länge der Zertifikatskette: 1
Zertifikat[1]:
Inhaber: CN=Alice, C=JP
Der Emittent: CN=Alice, C=JP
Ordnungsnummer: 6a59c918
Gültigkeitsbeginn: Sat Apr 13 08:41:13 JST 2019 Enddatum: Fri Jul 12 08:41:13 JST 2019
Zertifikat Fingerabdruck:
         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
Name des Signaturalgorithmus: SHA256withRSA
Betreff Public-Key-Algorithmus:2048-Bit-RSA-Schlüssel
Ausführung: 3

Erweiterung:

#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                                        @.;.
]
]

Ändern Sie den Schlüsselspeicherpfad

$ keytool -storepasswd
Neues Keystore-Passwort:
Bitte geben Sie Ihr neues Keystore-Passwort erneut ein:

Sie können das Keystore-Passwort mit dem Befehl -storepasswd ändern.

Eintrag löschen

$ keytool -delete -alias foo

Sie können jeden Eintrag mit dem Befehl -delete löschen.

Der zu löschende Eintrag wird durch die Option -alias angegeben.

Eintragsalias ändern

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

Sie können den Alias eines beliebigen Eintrags mit dem Befehl -changealias ändern.

-alias gibt den zu ändernden Alias an. -destalias gibt den Namen des geänderten Alias an.

Überprüfen Sie den in die JRE integrierten Schlüsselspeicher

Java führt einen Schlüsselspeicher, der das Stammzertifikat in der Ausführungsumgebung aufzeichnet.

Die spezifische Datei lautet "$ {JAVA_HOME} / lib / security / cacerts". Dies ist auch ein Keystore, sodass Sie den Inhalt mit dem Keytool überprüfen und bearbeiten können.

Überprüfen Sie den Inhalt von Cacerts


$ keytool -list -cacerts
Bitte geben Sie das Keystore-Passwort ein:
Keystore-Typ: JKS
Keystore-Anbieter: SUN

Der Keystore enthält 93 Einträge

verisignclass2g2ca [jdk],2018/06/13, trustedCertEntry,
Zertifikat Fingerabdruck(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,
Zertifikat Fingerabdruck(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,
Zertifikat Fingerabdruck(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

...

Zertifikat Fingerabdruck(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,
Zertifikat Fingerabdruck(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

Das Standardkennwort lautet "changeit". In der Dokumentation heißt es übrigens: "Systemadministratoren müssen nach der Installation des SDK das Kennwort und die Standardberechtigungen für diese Datei ändern."

Wenn Sie die Option "-cacerts" angeben, befindet sie sich in demselben Zustand, in dem Sie die cacerts-Datei als Schlüsselspeicher angegeben haben, sodass Sie darauf zugreifen können, ohne den Dateipfad mit "-keystore" anzugeben.

Wenn Sie SSL-Kommunikation usw. in Java implementieren, wird die Zertifikatüberprüfung standardmäßig mit diesen Zertifikaten durchgeführt. Beim Importieren eines Zertifikats kann auch darauf verwiesen werden, worauf später noch eingegangen wird.

Generieren Sie einen privaten Schlüssel

python


$ keytool -genseckey -keyalg AES -keysize 256 -alias HogeKey
Bitte geben Sie das Keystore-Passwort ein:**********
Bitte geben Sie Ihr neues Passwort erneut ein:**********

Verwenden Sie den Befehl -genseckey, um einen privaten Schlüssel für die allgemeine Schlüsselverschlüsselung zu generieren.

Sie werden aufgefordert, ein Kennwort einzugeben, das das Kennwort zum Ver- / Entschlüsseln der Keystore-Datei selbst ist. Insbesondere diesmal war es der erste Zugriff (ich habe den Schlüsselspeicher zum ersten Mal erstellt), daher werde ich auch aufgefordert, das neue Kennwort erneut einzugeben. Ab dem nächsten Mal müssen Sie das Passwort nur noch einmal eingeben.

AES wird als Schlüsselalgorithmus in -keyalg angegeben, und die Schlüssellänge beträgt 256.

-alias gibt den Alias an, der an den registrierten privaten Schlüssel angehängt werden soll. Es kann weggelassen werden, aber in diesem Fall ist es der Wert "mykey".

Sie können keinen kennwortbasierten Schlüssel generieren?

Ich habe versucht, einen Eintrag zu erstellen, indem ich den passwortbasierten Schlüsselalgorithmus in -keyalg angegeben habe.

$ keytool -genseckey -keyalg PBEWithHmacSHA256AndAES_128 -keysize 256 -alias pbeKey
Bitte geben Sie das Keystore-Passwort ein:*******
Bitte geben Sie das Passwort zum Speichern ein:**************
Passwort erneut eingeben:**************

Ich konnte einen fehlerfreien Eintrag erstellen und habe das Gefühl, erfolgreich zu sein. Aber wenn ich diesen Eintrag von einem Java-Programm bekomme, sieht es so aus:

jshell


(Weggelassen)

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

Der Algorithmus ist "PBEWithMD5AndDES". .. .. Bedeutet das, dass es keine formelle Unterstützung gibt?

Generieren Sie ein Verschlüsselungsschlüsselpaar mit öffentlichem Schlüssel

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

Verwenden Sie den Befehl -genkeypair, um ein Schlüsselpaar für die Verschlüsselung mit öffentlichen Schlüsseln zu generieren.

Wie bei der allgemeinen Schlüsselverschlüsselung können Sie den Schlüsselalgorithmus mit -keyalg und die Schlüssellänge (Anzahl der Bits) mit -keysize angeben. (Um ein DSA-Schlüsselpaar zu erstellen, geben Sie "DSA" in "-keyalg" an.)

In -genkeypair wird der öffentliche Schlüssel in Form eines selbstsignierten Zertifikats (X.509) generiert. -dname gibt den definierten X.500-Namen an, der für den Aussteller und den Betreff des Zertifikats festgelegt wurde. Wenn Sie die Angabe von "-dname" weglassen, werden Sie aufgefordert, den definierten X.500-Namen über die Befehlszeile einzugeben.

X.500 Distinguished Name

Beschreiben Sie den definierten X.500-Namen in Form von " = ", getrennt durch Kommas, z. B. "CN = Amazon, OU = Server CA 1B, O = Amazon, C = US". Die folgenden Werte können für den Attributnamen angegeben werden.

Attributname Bedeutung
CN Gemeinsamen Namen
Name des Besitzers usw.
OU Abteilungsname (Organisationseinheit)
Namen von Abteilungen und Abteilungen
O Name der Organisation
Firmenname etc.
L Ortsname
Name der Stadt usw.
S Name des Staates
Staatsname usw. (Präfekturebene in Japan?)
C Ländercode (Land)
Zweistelliger Code, der das Land identifiziert (JP, USEine solche)

Einige echte Beispiele.

Bestätigte Seite Wert des Subjekts
Qiita
https://qiita.com/
CN=qiita.com
Japan Oracle
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
Kabinettsbüro
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

Es ist nicht erforderlich, alle Attributnamen anzugeben. Es gibt jedoch eine Einschränkung für die Bestellung, und die Bestellung muss "CN", "OU", "O", "L", "S", "C" sein.

Bei Angabe mit der Option -dname muss der Optionswert in Anführungszeichen ( ") eingeschlossen werden, wenn Leerzeichen eingefügt werden sollen.

-Beispiel für das Einfügen von dname in Anführungszeichen


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

Kommas mit halber Breite haben auch eine besondere Bedeutung für die Trennung von Attributen. Wenn Sie also die halbe Breite in den Attributwert aufnehmen möchten, müssen Sie mit einem Backslash maskieren.

Verwenden Sie Kommas mit halber Breite für Attributwerte


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

Gültigkeitsdauer des Zertifikats

Die Gültigkeitsdauer des Zertifikats kann durch die folgenden zwei Optionen gesteuert werden.

-startdate ist das Startdatum und die Startzeit des Gültigkeitszeitraums des Zertifikats. -validity gibt die Gültigkeitsdauer des Zertifikats in Tagen an.

Mit anderen Worten, die Gültigkeitsdauer des Zertifikats liegt zwischen dem durch "Startdatum" angegebenen Datum und der Uhrzeit und der durch "Gültigkeit" angegebenen Anzahl von Tagen.

Der Standardwert von "-startdate" ist das Ausführungsdatum und die Ausführungszeit, zu der der Schlüssel erstellt wurde, und der Standardwert von "-validity" ist "90". Daher beträgt die Standardgültigkeitsdauer eines Zertifikats 90 Tage ab dem Datum und der Uhrzeit der Erstellung des Schlüsselpaars.

-startdate kann entweder relativ oder absolut angegeben werden.

Bei der relativen Methode wird das Startdatum und die Startzeit durch die Zeit relativ zum Ausführungsdatum und der Ausführungszeit angegeben. Insbesondere wird es wie folgt beschrieben.

Angabe eines relativen Startdatums und einer relativen Startzeit


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

Diese Bezeichnung ist "+ 10y" für "plus 10 Jahre", "-1m" für "minus 1 Monat", "+ 5d" für "plus 5 Tage" und "+ 8H" für "plus 8 Stunden". Es gibt.

Das relative Spezifikationsformat ist ([+ -] nnn [ymdHMS]) +. Mit anderen Worten, geben Sie an, um wie viel Jahr, Monat, Tag, Stunde, Minute und Sekunde um Plus / Minus verschoben werden sollen.

Andererseits wird die absolute Spezifikation wie folgt spezifiziert.

Angabe des absoluten Startdatums und der absoluten Startzeit


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

Dies ist der, den ich gesehen habe.

Das Format ist "[JJJJ / MM / TT] [HH: MM: SS]". Wenn nicht angegeben, werden das Ausführungsdatum und die Ausführungszeit verwendet.

Mit anderen Worten, wenn Sie nur das Datum angeben, sind Stunde, Minute und Sekunde die Ausführungszeit. Wenn nur Stunden, Minuten und Sekunden angegeben werden, ist das Datum das Ausführungsdatum.

Ausgabezertifikat

$ keytool -exportcert -alias alice-key -file alice-cert
Zertifikat ist Datei<alice-cert>Gespeichert in

Mit dem Befehl -exportcert können Sie das Zertifikat im angegebenen Eintrag im X.509-Format ausgeben. (Der Eintrag für den privaten Schlüssel kann nicht angegeben werden, da kein Zertifikat vorhanden ist.)

Die Standardeinstellung ist die Standardausgabe. Geben Sie die Option "-file" an, die in eine Datei ausgegeben werden soll.

Das Ausgabeformat ist standardmäßig im Binärformat. Geben Sie für das PEM-Format die Option -rfc an.

Beispiel für die Ausgabe im PEM-Format


$ keytool -exportcert -alias alice-key -rfc
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIEFj7+/DANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJj
...Abkürzung...
7ticcWecmUspMZ2dx/lO
-----END CERTIFICATE-----

Zeigen Sie den Inhalt der Zertifikatdatei an

$ keytool -printcert -file alice-cert
Inhaber: CN=alice
Der Emittent: CN=ca
Ordnungsnummer: 163efefc
Gültigkeitsbeginn: Sat Apr 13 11:45:48 JST 2019 Enddatum: Fri Jul 12 11:45:48 JST 2019
Zertifikat Fingerabdruck:
         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
Name des Signaturalgorithmus: SHA256withRSA
Betreff Public-Key-Algorithmus:2048-Bit-RSA-Schlüssel
Ausführung: 3

Erweiterung:
...

Sie können den Inhalt der angegebenen Zertifikatdatei überprüfen, indem Sie den Befehl -printcert mit der Option -file ausführen. Wenn Sie "-file" nicht angeben, werden die Informationen standardmäßig aus der Standardeingabe gelesen.

Die Standardeinstellung wird wie oben beschrieben in einem für Menschen lesbaren Format ausgegeben. Geben Sie die Option -rfc an, die im PEM-Format ausgegeben werden soll.

Bei Ausgabe im PEM-Format


$ keytool -printcert -file alice-cert -rfc
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIEFj7+/DANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJj
...Abkürzung...
7ticcWecmUspMZ2dx/lO
-----END CERTIFICATE-----

Der Befehl -printcert bezieht sich nicht auf den Schlüsselspeicher, daher kann er ohne Angabe von -keystore ausgeführt werden.

Zeigen Sie das Zertifikat eines beliebigen SSL-Servers an

$ keytool -printcert -sslserver qiita.com
Certificate #0
====================================
Inhaber: CN=qiita.com
Der Emittent: CN=Amazon, OU=Server CA 1B, O=Amazon, C=US
Ordnungsnummer: 793de44ec0e815de65a3fbb3e35a1e2
Gültigkeitsbeginn: Sun Mar 31 09:00:00 JST 2019 Enddatum: Thu Apr 30 21:00:00 JST 2020
Zertifikat Fingerabdruck:
         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
Name des Signaturalgorithmus: SHA256withRSA
Betreff Public-Key-Algorithmus:2048-Bit-RSA-Schlüssel
Ausführung: 3

...

Certificate #3
====================================
Inhaber: CN=Starfield Services Root Certificate Authority - G2, O="Starfield Technologies, Inc.", L=Scottsdale, ST=Arizona, C=US
Der Emittent: OU=Starfield Class 2 Certification Authority, O="Starfield Technologies, Inc.", C=US
Ordnungsnummer: a70e4a4c3482b77f
...

Sie können das Zertifikat des angegebenen SSL-Servers drucken, indem Sie den Befehl -printcert mit der Option -sslserver ausführen.

Der Host und der Port können in -sslserver in der Form host: port angegeben werden. Die Portnummer ist optional und standardmäßig 443.

Wenn Sie in einer Proxy-Umgebung ausgeführt werden, geben Sie die Proxy-Informationen mit den Optionen -J-Dhttps.proxyHost und -J-Dhttps.proxyPort an (z. B. -J-Dhttps.proxyHost = proxy.host.name). -J-Dhttps.proxyPort = 8080).

Dies ist standardmäßig auch ein für Menschen lesbares Format. Geben Sie für das PEM-Format die Option -rfc an.

Wenn Sie es lokal als Zertifikatdatei speichern möchten, können Sie das Ausgabeergebnis mit der Option "-rfc" in eine Datei umleiten ("-file" ist eine Eingabedateioption. Selbst wenn Sie dies angeben, befindet es sich in der Datei. Wird nicht gespeichert).

Erstellen Sie eine Zertifikatsignierungsanforderung

$ keytool -certreq -alias alice-key
-----BEGIN NEW CERTIFICATE REQUEST-----
MIICkDCCAXgCAQAwGzELMAkGA1UEBhMCSlAxDDAKBgNVBAMTA1RvbTCCASIwDQYJ
...Abkürzung...
wX7FTUBXCD8CjXFxosmZ97YJf7Xy89cmdArgVNE9T9pJJpht
-----END NEW CERTIFICATE REQUEST-----

Mit dem Befehl -certreq können Sie eine Zertifikatsignierungsanforderung erstellen.

Geben Sie in der Option -alias den Alias des Zieleintrags für den öffentlichen Schlüssel an.

Die Standardeinstellung ist die Standardausgabe. Wenn Sie in eine Datei ausgeben möchten, leiten Sie die Ausgabezieldatei mit der Option "-file" um oder geben Sie sie an.

Signaturanforderung für Ausgabezertifikat in Datei


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

Generieren Sie ein Zertifikat aus einer Zertifikatsignierungsanforderung

#Überprüfen Sie den Inhalt des Schlüsselspeichers
$ keytool -list
...

Keystore enthält 2 Einträge

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

#Generieren Sie ein Zertifikat aus einer Anforderungsdatei für die Zertifikatsignierung
$ keytool -gencert -infile alice-certreq -alias ca-key -outfile alice-cert

Mit dem Befehl -gencert können Sie ein Zertifikat (im X.509-Format) aus einer Anforderungsdatei für die Zertifikatsignierung generieren.

Das obige Beispiel generiert ein Zertifikat für die Zertifikatsignierungsanforderungsdatei "alice-certreq" unter Verwendung des Schlüsselpaareintrags "ca-key", der im Schlüsselspeicher vorhanden ist.

-infile gibt die zu verarbeitende Zertifikatsignierungsanforderungsdatei an. Wenn nicht angegeben, geben Sie über die Standardeingabe ein.

-alias gibt den Schlüsseleintrag an, mit dem das Zertifikat signiert wird.

-outfile gibt die Datei an, in der das resultierende Zertifikat ausgegeben werden soll. Wenn nicht angegeben, wird es an die Standardausgabe ausgegeben.

Standardmäßig werden Zertifikate im Binärformat gedruckt. Sie können im PEM-Format ausgeben, indem Sie die Option -rfc angeben.

-Wenn die Option rfc angegeben ist


$ keytool -gencert -infile alice-certreq -alias ca-key -rfc
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIEF+xeBDANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJj
...Abkürzung...
es0hcACS0jLw0VoJYjSB
-----END CERTIFICATE-----

Importieren Sie das Zertifikat

Verwenden Sie den Befehl -importcert, um das Zertifikat zu importieren.

Das Format der Daten, die importiert werden können, kann eine Zertifikatskette im PKCS # 7-Format oder eine einzelne oder eine Folge von X.509-Zertifikaten sein.

Dieser Befehl hat zwei Funktionen.

  1. Importieren Sie die Zertifikatantwort
  2. Importieren Sie ein vertrauenswürdiges Zertifikat

Der Befehl -importcert führt abhängig von den Bedingungen eine dieser Funktionen aus.

Es wird als Import einer Zertifikatantwort ausgeführt, wenn die folgenden Bedingungen erfüllt sind.

Wenn andererseits die folgenden Bedingungen erfüllt sind, wird es als vertrauenswürdiger Zertifikatimport ausgeführt.

Zertifikatantwort importieren

Wenn der Eintrag mit dem durch "-alias" angegebenen Namen bereits im Keystore als privater Schlüsseleintrag vorhanden ist, wird das Zertifikat, das Sie importieren möchten, als Zertifikatantwort betrachtet. Mit anderen Worten, es wird so behandelt, als würde versucht, die von der Zertifizierungsstelle signierte und als Antwort auf die vom öffentlichen Schlüsselzertifikat im Schlüsseleintrag generierte Zertifikatsignierungsanforderung zurückgegebene Zertifikatantwort zu importieren.

Daher ersetzt keytool das im Schlüsseleintrag enthaltene öffentliche Schlüsselzertifikat durch die für den Import angegebene Zertifikatantwort.

Überprüfung der Zertifikatantwort

Bevor der Import tatsächlich durchgeführt wird, wird die Zertifikatantwort validiert.

Die Überprüfung wird grob auf die folgenden zwei Arten durchgeführt.

  1. Entspricht die Zertifikatantwort dem durch "-alias" angegebenen Schlüsseleintrag?
  2. Ob die Zertifikatantwort zuverlässig ist

Ob die Zertifikatantwort dem durch "-alias" angegebenen Schlüsseleintrag entspricht, hängt davon ab, ob die Zertifikatantwort denselben öffentlichen Schlüssel im Schlüsseleintrag enthält. Wenn das öffentliche Schlüsselzertifikat im Schlüsseleintrag in der Zertifikatantwort nicht vorhanden ist, ist die Zertifikatantwort völlig irrelevant und es tritt ein Fehler auf.

jca.jpg

Die Methode zum Bestimmen, ob die Zertifikatantwort zuverlässig ist oder nicht, wird in Abhängigkeit von der Anzahl der in der Zertifikatantwort vorhandenen Zertifikate grob in zwei Teile geteilt.

Wenn mehrere Zertifikate in der Zertifikatantwort enthalten sind

Wenn die Zertifikatantwort mehrere Zertifikate enthält, wird geprüft, ob mit diesen Zertifikaten eine gültige Zertifikatkette erstellt werden kann.

Suchen Sie zunächst das Zertifikat mit demselben öffentlichen Schlüssel wie der Eintrag für den Importzielschlüssel in der Zertifikatantwort. Mit diesem Zertifikat oben werden dann die Zertifikate nacheinander verbunden, deren Signaturüberprüfung in Ordnung ist.

jca.jpg

Wenn es sich bei dem nachfolgenden Zertifikat um ein selbstsigniertes Zertifikat handelt, wird überprüft, ob dasselbe Zertifikat im Keystore vorhanden ist. Wenn dasselbe selbstsignierte Zertifikat im Schlüsselspeicher vorhanden ist, wird die Zertifikatkette als legitim angesehen und die erstellte Zertifikatkette in den Schlüsseleintrag importiert. Wenn nicht dasselbe selbstsignierte Zertifikat vorhanden ist, werden Sie von keytool gefragt, ob Sie den Import fortsetzen möchten.

Wenn das Zertifikat am Ende kein selbstsigniertes Zertifikat ist, wird überprüft, ob sich im Schlüsselspeicher ein Zertifikat befindet, das die Signatur überprüfen kann. Wenn das entsprechende Zertifikat im Schlüsselspeicher gefunden wird, wird das im Schlüsselspeicher gefundene Zertifikat am Ende der Zertifikatkette hinzugefügt und die Zertifikatkette in den Schlüsseleintrag importiert. Wenn das Zertifikat nicht gefunden wird, werden Sie von keytool gefragt, ob Sie den Import fortsetzen möchten.

jca.jpg

Wenn die Zertifikatantwort ein einzelnes Zertifikat ist

Wenn die Zertifikatantwort nur ein Zertifikat enthält, ändert sich das Validierungsverhalten geringfügig.

In diesem Fall versucht keytool, die Zertifikatkette mithilfe des im Zielschlüsselspeicher vorhandenen Zertifikats zu reproduzieren.

jca.jpg

Wenn die Zertifikatskette endgültig bis zur Wurzel reproduziert werden kann, wird die als zuverlässig reproduzierte Kette im Schlüsseleintrag registriert. Wenn die Kette nicht reproduziert werden kann (wenn Sie keinen öffentlichen Schlüssel finden, der in der Mitte überprüft werden kann), werden Sie von keytool gefragt, ob Sie den Import fortsetzen möchten.

Vertrauenswürdiges Zertifikat importieren

Wenn der durch -alias angegebene Eintrag nicht im Keystore vorhanden ist, wird er als vertrauenswürdiges Zertifikat importiert.

In diesem Fall wird, selbst wenn die zu importierenden Daten mehrere Zertifikate enthalten, nur das erste importiert.

Auch zu diesem Zeitpunkt wird überprüft, ob das Zertifikat gültig ist. Diese Überprüfung verhält sich so, als wäre die Zertifikatantwort ein einzelnes Zertifikat.

Mit anderen Worten, ob das Zertifikat gültig ist oder nicht, hängt davon ab, ob die Zertifikatkette mithilfe des Zertifikats im Schlüsselspeicher reproduziert werden kann oder nicht.

Wenn die Kette reproduziert werden kann, ist der Import abgeschlossen. Wenn es nicht reproduziert werden kann, werden Sie von keytool gefragt, ob Sie den Import fortsetzen möchten.

Verwenden Sie zur Validierung auch Cacerts-Zertifikate

Wenn Sie in der Option des Befehls "-importcert" "-trustcacerts" angeben, wird das Stammzertifikat in "cacerts" auch während der Validierung verwendet. (Standardmäßig wird nur das Zertifikat im Zielschlüsselspeicher verwendet.)

Eigentlich versuchen

#Überprüfen Sie Alices Keystore
$ keytool -keystore alice.keystore -list
...
Keystore enthält 1 Eintrag

alice-key,2019/04/18, PrivateKeyEntry,
Zertifikat Fingerabdruck(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

#Überprüfen Sie den CA-Keystore
$ keytool -keystore ca.keystore -list
...
Keystore enthält 2 Einträge

middle-ca-key,2019/04/18, PrivateKeyEntry,
Zertifikat Fingerabdruck(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,
Zertifikat Fingerabdruck(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

Zwei Schlüsselgeschäfte wurden zur Überprüfung vorbereitet.

Der erste ist Alices Keystore, der derzeit nur einen Eintrag für ein öffentliches Schlüsselpaar selbstsignierter Zertifikate ("Alice-Key") enthält.

Der andere ist der CA-Schlüsselspeicher (Zertifizierungsstelle), der ein öffentliches Schlüsselpaar für die Zwischenauthentifizierungsbehörde ("Middle-Ca-Key") und ein öffentliches Schlüsselpaar für die Root-Authentifizierungsstelle ("Root-Ca-Key") enthält. Es gibt zwei Einträge. (Übrigens ist "Middle-Ca-Key" mit "Root-Ca-Key" signiert.)

Generieren Sie von hier aus eine Zertifikatsignierungsanforderung aus dem öffentlichen Schlüsselpaar von Alice, signieren Sie sie mit dem Schlüssel der Zertifizierungsstelle und importieren Sie sie in den Schlüsselspeicher von Alice.

#Generieren Sie eine Zertifikatsignierungsanforderung aus Alices Schlüssel
$ keytool -keystore alice.keystore -certreq -alias alice-key -file alice.csr

#Unterzeichnet mit dem Schlüssel der Zwischenzertifizierungsstelle
$ keytool -keystore ca.keystore -gencert -alias middle-ca-key -infile alice.csr -outfile alice.cer

#Überprüfen Sie den Inhalt des generierten Zertifikats
$ keytool -printcert -file alice.cer
Zertifikat[1]:
Inhaber: CN=alice
Der Emittent: CN=middle-ca
Ordnungsnummer: 25fc2e01
...

Zertifikat[2]:
Inhaber: CN=middle-ca
Der Emittent: CN=root-ca
Ordnungsnummer: 248f2111
...

Generierte eine Zertifikatsignierungsanforderung aus Alices Schlüsselpaar und generierte ein öffentliches Schlüsselzertifikat, das mit einem Zwischenschlüssel der Zertifizierungsstelle signiert war.

Wenn Sie sich den Inhalt ansehen, sehen Sie, dass die Zertifikate in der Reihenfolge Alice → Intermediate Certification Authority verkettet sind (das Zertifikat der Root-Zertifizierungsstelle ist nicht enthalten).

Importieren Sie dies in Alices ursprünglichen Schlüsseleintrag (dh importieren Sie es als Zertifikatantwort).

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

Antwortzertifikat der obersten Ebene:

Inhaber: CN=middle-ca
Der Emittent: CN=root-ca
Ordnungsnummer: 248f2111
...

...Ist nicht vertrauenswürdig. Möchten Sie die Antwort installieren?[Nein]:

Das Zertifikat der obersten Ebene wurde als nicht vertrauenswürdig eingestuft und aufgefordert, zu bestätigen, dass es importiert werden kann. Da das "root-ca" -Zertifikat in Alices Schlüsselspeicher nicht vorhanden ist, wird die Zertifikatantwort, die Sie importieren wollten, als nicht vertrauenswürdig angesehen.

Hier wird der Import vorübergehend ausgesetzt und das Zertifikat der Stammzertifizierungsstelle wird zuerst in den Schlüsselspeicher von Alice importiert.

#Exportieren Sie das Public-Key-Zertifikat der Root-Zertifizierungsstelle
$ keytool -keystore ca.keystore -exportcert -alias root-ca-key -file root-ca.cer
Zertifikat ist Datei<root-ca.cer>Gespeichert in

#Importieren Sie das Zertifikat der Stammzertifizierungsstelle in den Schlüsselspeicher von Alice
$ keytool -keystore alice.keystore -importcert -file root-ca.cer -alias root-ca-cert
Inhaber: CN=root-ca
Der Emittent: CN=root-ca
Ordnungsnummer: 13b64cb6
...
Zertifikat Fingerabdruck:
         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
...

Vertrauen Sie diesem Zertifikat?[Nein]:  y
Zertifikat zum Keystore hinzugefügt

Der für "-alias" angegebene Eintrag ist nicht vorhanden, daher verhält sich der Befehl "-importcert" wie "Importieren eines vertrauenswürdigen Zertifikats".

Da es sich bei diesem Zertifikat um ein selbstsigniertes Zertifikat handelt, hat der Importeur (Keystore-Administrator) keine andere Wahl, als zu entscheiden, ob er ihm vertraut. Ich vertraue ihm hier, also habe ich den Import mit "y" abgeschlossen.

Wenn Sie tatsächlich ein vertrauenswürdiges Zertifikat importieren, sollten Sie die Informationen wie die auf dem Bildschirm angezeigten Fingerabdrücke sorgfältig prüfen, um sicherzustellen, dass Sie das beabsichtigte Zertifikat importieren.

Versuchen Sie erneut, die Zertifikatantwort von Alice zu importieren.

#Importieren Sie die Zertifikatantwort von Alice
$ keytool -keystore alice.keystore -importcert -file alice.cer -alias alice-key
Zertifikatantwort im Keystore installiert

#Überprüfen Sie nach dem Import die Schlüsseleingabe von Alice
$ keytool -keystore alice.keystore -storepass password -list -v -alias alice-key
alias: alice-key
Erstellungsdatum: 2019/04/18
Eintragsart: PrivateKeyEntry
Länge der Zertifikatskette: 3
Zertifikat[1]:
Inhaber: CN=alice
Der Emittent: CN=middle-ca
Ordnungsnummer: 25fc2e01
...

Zertifikat[2]:
Inhaber: CN=middle-ca
Der Emittent: CN=root-ca
Ordnungsnummer: 248f2111
...

Zertifikat[3]:
Inhaber: CN=root-ca
Der Emittent: CN=root-ca
Ordnungsnummer: 13b64cb6
...

Dieses Mal war das Zertifikat der Stammzertifizierungsstelle bereits installiert, sodass die Überprüfung der Zertifikatkette erfolgreich war und die Installation unverändert abgeschlossen wurde.

Nach dem Import können Sie außerdem sehen, dass die Zertifikatkette im öffentlichen Schlüsselzertifikat in Alices Schlüsseleintrag gespeichert ist.

KeyStore Die vom Befehl keytool generierte und verwaltete Keystore-Datei verfügt über eine API, auf die über Java-Programme zugegriffen werden kann.

Holen Sie sich eine KeyStore-Instanz

jshell


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

Verwenden Sie die Klasse KeyStore, um auf den Keystore zuzugreifen.

getInstance () gibt den Typ des Schlüsselspeichers an. In Java 9 und höher ist der Keystore-Typ standardmäßig PKCS12, sodass Sie mit "PKCS12" instanziieren. (Wenn es sich um einen alten Schlüsselspeicher handelt, kann es sich um JKS handeln. Geben Sie in diesem Fall "JKS" an.)

Lesen Sie die Keystore-Datei

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)

Um die Keystore-Datei zu laden, [load ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#load(java.) io.InputStream, char% 5B% 5D))) Verwenden Sie die Methode. Geben Sie im ersten Argument die Keystore-Datei "InputStream" und im zweiten Argument das Kennwort der Keystore-Datei im Array "char" an.

Lesen Sie gleichzeitig mit dem Erwerb der Instanz

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)) können Sie alles tun, von der Erstellung einer KeyStore-Instanz bis zum Laden einer Keystore-Datei auf einmal.

Der Keystore-Typ wird automatisch ermittelt.

Aliasnamen für alle Einträge abrufen

jshell


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

aliases () In der Methode Aliasnamen aller Einträge Kann erhalten werden.

Überprüfen Sie den Eintragstyp anhand des Aliasnamens

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

Ob der Eintrag, auf den der Alias verweist, ein Schlüsseleintrag ist, lautet isKeyEntry () überprüfen.

Ob es sich um einen vertrauenswürdigen Zertifikatseintrag handelt, lautet [isCertificateEntry ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#isCertificateEntry (java) Sie können dies mit .lang.String)) überprüfen.

Holen Sie sich den privaten Schlüssel

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%) Mit 5D)) können Sie den Schlüssel des angegebenen Alias als privaten Schlüssel abrufen.

Das erste Argument ist der Aliasname, und das zweite Argument ist das Kennwort, das den Schlüssel schützt (normalerweise dasselbe wie das Schlüsselspeicherkennwort).

Holen Sie sich einen privaten Schlüssel

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

Sie können den privaten Schlüssel abrufen, indem Sie getKey () für den Schlüsseleintrag des öffentlichen Schlüsselpaars verwenden.

Holen Sie sich einen öffentlichen Schlüssel (Zertifikat)

jshell


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

]

//Holen Sie sich den öffentlichen Schlüssel aus dem Zertifikat
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)] für den Schlüsseleintrag des öffentlichen Schlüsselpaars Mit .lang.String)) das Zertifikat für den öffentlichen Schlüssel (java.security.cert.Certificate /security/cert/Certificate.html))) kann abgerufen werden.

Holen Sie sich eine Zertifikatskette

jshell


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

] }

Verwenden von getCertificateChain () , Sie können die im angegebenen Eintrag vorhandene Zertifikatkette als Array von "Zertifikat" abrufen.

Fügen Sie einen privaten Schlüsseleintrag hinzu

jshell


//Generieren Sie einen privaten Schlüssel
jshell> var secKey = KeyGenerator.getInstance("AES").generateKey()
secKey ==> javax.crypto.spec.SecretKeySpec@fffe806b

//Generieren Sie einen privaten Schlüsseleintrag
jshell> var secKeyEntry = new KeyStore.SecretKeyEntry(secKey)
secKeyEntry ==> Secret key entry with algorithm AES

//Registrieren Sie die Eingabe eines privaten Schlüssels im Keystore
jshell> var protection = new KeyStore.PasswordProtection(password)
protection ==> java.security.KeyStore$PasswordProtection@27d415d9

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

//Extrahieren und bestätigen Sie die registrierte Eingabe des privaten Schlüssels
jshell> keystore.getKey("sec-key", password)
$10 ==> javax.crypto.spec.SecretKeySpec@fffe806b

Um einen Eintrag zum Keystore hinzuzufügen, setEntry ().

Das erste Argument ist der Eintragsalias. Das zweite Argument ist der Eintrag zur Registrierung. Das dritte Argument übergibt Parameter für den Eintragsschutz.

Erstellen Sie einen privaten Schlüsseleintrag mit KeyStore.SecretKeyEntry.

Der Parameter übergibt das Kennwort, das beim Verschlüsseln des Eintrags verwendet werden soll. Es ist möglich, ein Kennwort anzugeben, das sich vom Schlüsselspeicher unterscheidet. Ist es jedoch sicher, es unverändert zu lassen? (Keytool verwendet dasselbe Kennwort wie der Keystore, wenn der Keystore-Typ PKCS12 ist.)

Ausgabe in Datei

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%) Mit 5D)) können Sie den Inhalt von KeyStore an den angegebenen Ausgabestream ausgeben.

Zertifikat

Gibt es eine neue API zu erstellen?

[Java.security.cert.Certificate] als Klasse, die ein Zertifikat darstellt (https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/cert/Certificate.html) Es gibt eine Klasse namens java.security.cert.CertificateFactory als Klasse zum Erstellen dieser Instanz. Es gibt eine Klasse namens /java/security/cert/CertificateFactory.html).

CertificateFactory liest jedoch ein vorhandenes Zertifikat und erstellt Certificate, kein Zertifikat von Grund auf neu.

Ungefähr java.security.cert Auch wenn Sie sich das Paket ansehen Ich kann keine Klasse finden, die ein Zertifikat generiert.

Ich habe mir die Implementierung des Befehls "-gencert" von Keytool angesehen, aber die Klasse des Pakets "sun.security.x509" (interne API) wurde verwendet.

Es gibt keine Möglichkeit, ein Zertifikat nur mit der Standard-API zu generieren. Es scheint, dass es keine andere Wahl gibt, als es mit Keytool zu generieren.

Lesen Sie das Zertifikat

Spezifizierbarer Zertifikatstyp

Verwenden Sie CertificateFactory, um das codierte Zertifikat zu lesen Machen.

Der Zertifikatstyp, der mit getInstance () angegeben werden kann, ist standardmäßig nur X.509, und Javadoc usw. werden hauptsächlich durch die Erläuterung von X.509 erklärt.

Name des CertificateFactory-Typs | Java-Sicherheitsstandardalgorithmus

Daher basiert die Erklärung hier auch auf X.509.

Lesen Sie die Zertifikate nacheinander

jshell


//Generieren Sie CertificateFactory
jshell> var factory = CertificateFactory.getInstance("X.509")
factory ==> java.security.cert.CertificateFactory@3abbfa04

//Zu lesende Zertifikatsdatei (Alice.Eingabestream von cer erzeugen)
jshell> var in = new FileInputStream("alice.cer")
in ==> java.io.FileInputStream@31ef45e3

//Lesen Sie die Zertifikatinformationen aus dem Eingabestream
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)
|Ausnahme 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)
|Ursache: java.io.IOException: Empty input
|        at X509Factory.engineGenerateCertificate (X509Factory.java:106)
|        ...

Um das codierte X.509-Zertifikat zu lesen, generieren Sie "Certificate ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/cert/CertificateFactory) Verwenden Sie .html # generateCertificate (java.io.InputStream)).

Die Zieldaten müssen DER-codierte Daten im Binärformat oder Daten im PEM-Format sein.

Wenn die Zieldaten Daten von mehreren Zertifikaten enthalten, kann bei jedem Aufruf von "generateCertificate ()" von Anfang an ein Zertifikat gelesen werden. (Wenn das Zertifikat nicht mehr vorhanden ist und Sie versuchen, es weiter zu lesen, wird eine Ausnahme ausgelöst.)

Lesen Sie mehrere Zertifikate gleichzeitig

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

If generateCertificates () Alle Zertifikate in den Zieldaten können gleichzeitig gelesen werden.

Überprüfen Sie die Signatur des Zertifikats

jshell


//Extrahieren Sie die Zertifikate einzeln aus der Sammlung der in ↑ gelesenen Zertifikate
jshell> var iterator = certs.iterator()
iterator ==> java.util.ArrayList$Itr@27808f31

//Holen Sie sich Alice Zertifikat
jshell> var aliceCert = iterator.next()
aliceCert ==> [
[
  Version: V3
  Subject: CN=alice
  Signature ... 3 D5  .........%$.m...

]

//Besorgen Sie sich das Zertifikat der Zwischenzertifizierungsstelle
jshell> var middleCaCert = iterator.next()
middleCaCert ==> [
[
  Version: V3
  Subject: CN=middle-ca
  Signa ... 0 70  ....$..&...2.n.p

]

//Überprüfen Sie das Zertifikat von Alice mit dem öffentlichen Schlüssel der Zwischenzertifizierungsstelle (Überprüfung OK).
jshell> aliceCert.verify(middleCaCert.getPublicKey())

//Überprüfen Sie das Zertifikat von Alice mit dem eigenen öffentlichen Schlüssel von Alice (Überprüfung NG)
jshell> aliceCert.verify(aliceCert.getPublicKey())
|Ausnahme Java.security.SignatureException: Signature does not match.
|        at X509CertImpl.verify (X509CertImpl.java:459)
|        at X509CertImpl.verify (X509CertImpl.java:391)
|        at (#18:1)

So überprüfen Sie die Signatur des Zertifikats verify () (java.security.PublicKey)) wird verwendet.

Übergeben Sie den zur Überprüfung verwendeten öffentlichen Schlüssel als Argument.

Wenn die Validierung in Ordnung ist, passiert nichts und wenn es NG ist, wird eine Ausnahme ausgelöst.

Zufallszahl

jshell


//Erstellen Sie eine SecureRandom-Instanz
jshell> var random = new SecureRandom()
random ==> Hash_DRBG,SHA-256,128,reseed_only

//Erhalten Sie Zufallszahlen unter 100
jshell> random.nextInt(100)
$3 ==> 91

//Generieren Sie ein zufälliges 512-Bit-Byte-Array
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 }

Verwenden Sie SecureRandom, um Zufallszahlen für die Verschlüsselung zu generieren.

Java hat auch eine [Random] -Klasse (https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/Random.html) zum Generieren von Zufallszahlen. Machen. Dies ist jedoch nicht unvorhersehbar und sollte nicht für die Kryptographie verwendet werden.

SecureRandom unterscheidet sich von anderen Engine-Klassen [getInstace ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/SecureRandom.html# Zusätzlich zu getInstance (java.lang.String)), [Constructor](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/SecureRandom.html#%3Cinit Sie können eine Instanz mit% 3E ()) erstellen.

Wenn eine Instanz vom Konstruktor erstellt wird, wird der Algorithmus von den Anbietern mit der höchsten Priorität ausgewählt.

Welcher Algorithmus für jede Umgebung ausgewählt wird, ist SecureRandom Implementation | 4 JDK Provider Document Dies kann durch -6D01-4B2E-9E85-B88E3BEE7453) bestätigt werden.

Soll ich getInstance () oder einen Konstruktor verwenden?

Eine Instanz von "SecureRandom" kann wie andere Engine-Klassen mit "getInstance ()" oder mit einem Konstruktor abgerufen werden. Im ersteren Fall muss der Algorithmus angegeben werden, im letzteren Fall wird der Algorithmus durch eine vorgegebene Priorität bestimmt.

Welche sollte verwendet werden, um eine Instanz von "SecureRandom" zu erstellen?

In der offiziellen Javadoc- und JCA-Dokumentation konnte ich nichts finden, das eindeutig "use this" besagt.

Die persönliche Schlussfolgerung, die ich nach verschiedenen Untersuchungen gezogen habe, war "Verwenden Sie den Konstruktor, es sei denn, es gibt einen bestimmten Grund" (es kann falsch sein, weil es eine persönliche Schlussfolgerung ist).

Der Grund dafür ist, dass Sie mit einem Konstruktor die Portabilität beibehalten und gleichzeitig den optimalen Algorithmus für jede Umgebung auswählen können.

In Unix-ähnlichen Umgebungen lautet der Standardalgorithmus "NativePRNG". Dieser Algorithmus kann jedoch nicht in einer Windows-Umgebung verwendet werden. Mit anderen Worten, wenn der Algorithmus als "NativePRNG" angegeben ist, kann das Programm nicht nach Windows portiert werden. (Umgekehrt kann Windows-PRNG nicht in Unix-basierten Umgebungen verwendet werden.)

In der Windows-Umgebung war der Standardalgorithmus bis Java 8 "SHA1PRNG". Mit der Hinzufügung von "DRBG" in Java 9 wurde die oberste Priorität jedoch durch "DRBG" ersetzt (JEP 273: DRBG-basierte SecureRandom-Implementierungen). ).

DRBG ist eine Methode zur Erzeugung von Pseudozufallszahlen, die in NIST Special Publication 800-90A Revision 1 beschrieben ist. Es scheint stärker zu sein als "SHA1PRNG".

Ich denke, es ist besser, einen Konstruktor zu verwenden, da es einfach ist, Fälle zu behandeln, in denen der Algorithmus durch einen neuen ersetzt wird. (Es ist nur eine persönliche Schlussfolgerung)

getInstanceStrong() Java 8 hat eine Methode namens [getInstanceStrong ()] hinzugefügt (https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/SecureRandom.html#getInstanceStrong ()). ing.

Ein Algorithmus, der starke Zufallszahlen erzeugt, wird ausgewählt, wie der Name "Stark" sagt. Es scheint verwendet zu werden, wenn wichtige Werte wie die Schlüsselpaargenerierung für die Verschlüsselung mit öffentlichen Schlüsseln generiert werden.

In einer Unix-basierten Umgebung lautet der spezifische Algorithmus beispielsweise "NativePRNGBlocking".

Was sich vom Standard "NativePRNG" unterscheidet, ist nextBytes (). security / SecureRandom.html # nextBytes (Byte% 5B% 5D)) und [generateSeed ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/ SecureRandom.html # generateSeed (int)) verwendet "/ dev / random" bzw. "/ dev / urandom".

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

SecureRandom | Tabelle 4-4 Algorithmen in SUN Provider

Diese / dev / random und / dev / urandom sind Pseudogeräte zum Generieren von Zufallszahlen und können auf Unix-basierten Betriebssystemen verwendet werden.

/ dev / random sammelt Umgebungsgeräusche und generiert Zufallszahlen, sodass echte Zufallszahlen erhalten werden können. Wenn jedoch versucht wird, eine Zufallszahl zu erhalten, wenn nicht genügend Informationen gesammelt wurden, wird der Prozess blockiert (Warten).

Andererseits kann "/ dev / urandom" Zufallszahlen erzeugen, ohne die Verarbeitung zu blockieren, indem die gesammelten Informationen wiederverwendet werden. Da die Informationen jedoch wiederverwendet werden, ist die Sicherheit als Zufallszahl "/ dev / random" unterlegen.

Referenz

/ dev / random ist sehr sicher, jedoch auf Kosten einer Verschlechterung der Programmleistung. / dev / urandom ist weniger sicher, aber auf Kosten der Beeinträchtigung der Programmleistung.

Die Standardauswahl "NativePRNG" verwendet "/ dev / random", um echte Zufallszahlen für die Startgenerierung zu generieren, verwendet jedoch "/ dev / urandom" für die normale Zufallszahlengenerierung. Mit anderen Worten, "NativePRNG" kann die Leistung gewährleisten und gleichzeitig die Sicherheit leicht verringern.

Andererseits erhält "NativePRNGBlocking" immer eine Zufallszahl von "/ dev / random". Mit anderen Worten, die Leistung wird aufgegeben und die Sicherheit ist voll und ganz gewidmet.

Ich denke, Sie sollten "getInstanceStrong ()" verwenden, wenn die Sicherheit Vorrang vor der Programmleistung haben soll, und die Standardimplementierung im Konstruktor verwenden, wenn beide kompatibel (persönlich) sein müssen. Meinung).

Diffie-Hellman-Schlüsselaustausch

KeyAgreement ist eine Klasse zum Implementieren des Diffie-Hellman-Schlüsselaustauschs. Es ist vorbereitet.

Der Ablauf des Schlüsselaustauschs mit "KeyAgreement" ist wie folgt.

jca.png

Starten Sie unten jshell für Alice und jshell für Bob und versuchen Sie, Schlüssel auszutauschen (beide aktuellen Verzeichnisse befinden sich am selben Speicherort).

Alice


//Generieren Sie ein Schlüsselpaar
jshell> var aliceKeyPairGen = KeyPairGenerator.getInstance("DH")
aliceKeyPairGen ==> java.security.KeyPairGenerator$Delegate@6d7b4f4c

//Schlüsselpaar generieren
jshell> var aliceKeyPair = aliceKeyPairGen.generateKeyPair()
aliceKeyPair ==> java.security.KeyPair@1786dec2

//Generieren Sie Alice's KeyAgreement
jshell> var aliceKeyAgree = KeyAgreement.getInstance("DH")
aliceKeyAgree ==> javax.crypto.KeyAgreement@2c039ac6

//Initialisiert mit Alices privatem Schlüssel
jshell> aliceKeyAgree.init(aliceKeyPair.getPrivate())

//Gib den öffentlichen Schlüssel von Alice in eine Datei aus
jshell> writeFile("alice-dh-public-key", aliceKeyPair.getPublic().getEncoded())

//Das Format des öffentlichen Schlüssels ist X..509
jshell> aliceKeyPair.getPublic().getFormat()
$32 ==> "X.509"

Verwenden Sie zum Austauschen von Schlüsseln zunächst "KeyPairGenerator", um ein Schlüsselpaar zu generieren. Geben Sie "DiffieHellman" oder die Abkürzung "DH" für den Algorithmusnamen an.

Der öffentliche Schlüssel für dieses Schlüsselpaar enthält die Primzahl $ p $, das Primitiv $ g $ und den Wert $ y = g ^ {a} \ bmod p $, berechnet aus der entsprechend ausgewählten Zufallszahl $ a $. Es gibt.

Holen Sie sich für "KeyAgreement" eine Instanz mit "DiffieHellman" (oder "DH") und initialisieren Sie sie mit dem privaten Schlüssel.

Bob


//Lesen Sie den öffentlichen Schlüssel von 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 }

//Stellen Sie den verschlüsselten öffentlichen Schlüssel von Alice wieder her
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

Der von Alice empfangene öffentliche Schlüssel hat das X.509-Format, also X509EncodedKeySpec ) Kann zur Wiederherstellung verwendet werden.

Da es die Rolle von "KeyFactory" ist, "Key" aus "KeySpec" zu generieren, stellen Sie diese mit "KeyFactory" wieder her. Geben Sie für den Algorithmusnamen "DiffieHellman" oder "DH" an.

Bob


//Generieren Sie Bobs Schlüsselpaar
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

Der wiederhergestellte öffentliche Schlüssel von Alice ist vom Typ DHPublicKey. Es ist geworden. Verwenden dieser Methode getPrams (), Holen Sie sich die spezifischen Parameter des Schlüsselaustauschs, der auf der Alice-Seite generiert wurde.

Sobald die Schlüsselaustauschparameter erhalten wurden, wird aus den Werten ein Schlüsselpaar auf der Bob-Seite erzeugt.

Bob


//Generieren Sie Bobs KeyAgreement und initialisieren Sie mit Bobs privatem Schlüssel
jshell> var bobKeyAgree = KeyAgreement.getInstance("DH")
bobKeyAgree ==> javax.crypto.KeyAgreement@27f723

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

//Geben Sie den öffentlichen Schlüssel von Bob in eine Datei aus
jshell> writeFile("bob-dh-public-key", bobKeyPair.getPublic().getEncoded())

Initialisieren Sie "KeyAgreement" auf Bobs Seite mit Bobs privatem Schlüssel.

Dann sende Bobs öffentlichen Schlüssel an Alice.

Alice


//Lesen Sie Bobs öffentlichen Schlüssel
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 }

//Stellen Sie Bobs öffentlichen Schlüssel auf Alices Seite wieder her
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 stellt den von Bob erhaltenen öffentlichen Schlüssel wieder her. Die Vorgehensweise ist dieselbe wie bei der Wiederherstellung von Alices öffentlichem Schlüssel durch Bob.

Alice


//Generieren Sie einen privaten Schlüssel basierend auf Bobs Informationen zum öffentlichen Schlüssel
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


//Generieren Sie einen privaten Schlüssel basierend auf den öffentlichen Schlüsselinformationen von 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 }

Schließlich jede der "KeyAgreement" [doPhase ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/javax/crypto/KeyAgreement.html#doPhase (Java). Durch Aufrufen der Methode security.Key (boolean)) wird die Vorbereitung für die Generierung des privaten Schlüssels abgeschlossen.

Verwenden Sie generateSecret (), um den privaten Schlüssel zu generieren. ..

Im Folgenden haben wir bestätigt, dass die Ver- und Entschlüsselung mit dem generierten privaten Schlüssel durchgeführt werden kann.

Alice


//Generieren Sie einen privaten AES-Schlüssel mit den ersten 128 Bits des generierten Schlüssels
jshell> var secretKey = new SecretKeySpec(secret, 0, 16, "AES")
secretKey ==> javax.crypto.spec.SecretKeySpec@fffe86a4

//Mit AES verschlüsselt
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5PAdding")
cipher ==> javax.crypto.Cipher@12cdcf4

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

//Exportieren Sie den Code
jshell> writeFile("cryptograph", cipher.doFinal("Hello Diffie-Hellman!!".getBytes()))

//Initialisierungsvektor exportieren
jshell> writeFile("iv", cipher.getParameters().getEncoded())

Bob


//Generieren Sie einen privaten Schlüssel für AES und Alice
jshell> var secretKey = new SecretKeySpec(secret, 0, 16, "AES")
secretKey ==> javax.crypto.spec.SecretKeySpec@fffe86a4

//Stellen Sie den Initialisierungsvektor aus der Datei wieder her
jshell> var params = AlgorithmParameters.getInstance("AES")
params ==>

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

//Bereiten Sie die Verschlüsselung im Entschlüsselungsmodus vor
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher ==> javax.crypto.Cipher@cb51256

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

//Entschlüsseln Sie den 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!!"

Referenz

Recommended Posts

JCA-Verwendungsprotokoll (Java Encryption Architecture)
Java-Memo
Java alles Memo
Java Silver Memo
Java, Maven Memo
Verwendungshinweise zu JavaParser
Java SE 7 Hinweis
Hinweise zur Verwendung von WatchService
PlantUML-Nutzungsnotiz
Java alles Memo 2
Java-Spezifikationsnotiz
Verwendungshinweise zu JUnit5
Java-Muster-Memo
Memo zur Java-Entwicklungsumgebung
Hinweise zur Verwendung von Spring Shell
Java Grundwissen Memo
Java-Lernnotiz (Methode)
Java Kuche Day Memo
Java Se 8 Programmierer Ⅰ Memo
Java bezahlte private Memo
Verwendung von Java-Variablen
Java-Lernnotiz (grundlegend)
Java Lambda Ausdruck Memo
(Memo) Java für Anweisung
Java Lambda Ausdruck [Notiz schreiben]
Java-Lernnotiz (Schnittstelle)
[Java] Implizites Vererbungsprotokoll
Java-Lernnotiz (Vererbung)
Programmiernotiz für Java-Wettbewerbe
[Memo] Java Linked List
Java (WebSphere Application Server) Hinweis [1]
[Java] Namensnotiz des Variablennamens
Java-Memo-Teilzeichenfolge (Standardklasse)
[Java] Mirage-Basic-Verwendung von SQL
Java-Lernnotiz (Datentyp)
Spring Security-Nutzungsnotiz CSRF
Länge des Java-Memos (Standardklasse)
Spring Security-Nutzungsnotiz Run-As
Java Silver Lernmethode Memo
Erstellen Sie eine Java-Methode [Memo] [java11]
Java Silver Prüfungsvorbereitungsnotiz
Verwendungsregeln für die Behandlung von Java-Ausnahmen
Sicherheit der Verwendungsnotizmethode für Spring Security
Spring Security-Nutzungsnotiz Remember-Me
Java-Lernnotiz (logischer Operator)
Java-Lernnotiz (abstrakte Klasse)
[Java] Datum Verwandte Begriffsnotiz
Hinweise zur Verwendung des Abhängigkeitsmanagement-Plugins
Java Study Memo 2 mit Progate
Spring Security-Nutzungsnotiz CORS
[Java11] Stream Usage Summary -Basics-
Spring Security-Verwendungsnotiztest
Was sind Java-Metriken? _Memo_20200818
Java HashMap, entrySet [Persönliches Memo]