JavaRush /Java-Blog /Random-DE /Java-Kryptographiearchitektur: Erste Einführung
Viacheslav
Level 3

Java-Kryptographiearchitektur: Erste Einführung

Veröffentlicht in der Gruppe Random-DE
Die Sicherheit des Datenaustauschs ist eine der wichtigsten Eigenschaften moderner Anwendungen. Seit der Antike haben die Menschen raffinierte Methoden entwickelt, die mit der Entwicklung der Menschheit zur gesamten Wissenschaft der Kryptographie wurden. Natürlich blieb Java nicht stehen und bot Entwicklern die Java Cryptography Architecture (JCA) an. Dieser Testbericht soll einen ersten Eindruck davon vermitteln, wie es funktioniert.

Vorwort

Ich schlage vor, in die Vergangenheit zu reisen. Vor uns liegt das antike Rom. Und vor uns steht Gaius Julius Caesar, der eine Botschaft an seine Kommandeure sendet. Mal sehen, was in dieser Nachricht steht:
Java-Kryptographiearchitektur: Erste Einführung – 2
Was könnte das bedeuten: "ЕСКЕУГЬГМХИФЯ Е УЛП"? Öffnen wir den Java Online Compiler, zum Beispiel: repl.it
class Main {
  public static void main(String[] args) {
    String code = "ЕСКЕУГЬГМХИФЯ Е УЛП";
    for (char symbol : code.toCharArray()) {
      if (symbol != ' ') {
        symbol = (char) (symbol - 3);
      }
      System.out.print(symbol);
    }
  }
}
Vor uns liegt die einfachste Implementierung der Caesar-Chiffre. Laut dem Werk des antiken römischen Historikers Sueton mit dem Titel „Das Leben der zwölf Cäsaren“ verschlüsselte Caesar genau auf diese Weise Nachrichten an seine Generäle. Und dies ist einer der ältesten Hinweise auf die Verwendung von Kryptographie . Das Wort „Kryptographie“ kommt von den altgriechischen Wörtern „versteckt“ und „schreiben“, d.h. Es ist die Wissenschaft der Datenschutztechniken. Java verfügt über eine eigene Unterstützung für Kryptographie, die als Java Cryptography Architecture (JCA) bezeichnet wird. Die Beschreibung findet sich in der offiziellen Dokumentation von Oracle – „ Java Cryptography Architecture (JCA) “. Ich schlage vor, dass Sie sich ansehen, welche Möglichkeiten wir dank JCA erhalten.
Java-Kryptographiearchitektur: Erste Einführung - 3

J.C.A.

Wie wir bereits erfahren haben, bietet Java die Java Cryptography Architecture (JCA) für die Arbeit mit Kryptographie. Diese Architektur enthält eine API (d. h. einen bestimmten Satz von Schnittstellen) und Anbieter (die diese implementieren):
Java-Kryptographiearchitektur: Erste Einführung - 4
In der Dokumentation heißt es: „ Die Java-Plattform umfasst eine Reihe integrierter Anbieter .“ Das heißt, die Java-Plattform stellt eine Reihe integrierter Anbieter bereit, die bei Bedarf erweitert werden können. Das können Sie selbst sehen:
import java.security.Provider;
import java.security.Security;
class Main {
  public static void main(String[] args) {
    Provider[] providers = Security.getProviders();
    for (Provider p : providers) {
      System.out.println(p.getName());
    }
  }
}
Die Registrierung eines Drittanbieters ist sehr einfach. Zum Beispiel: Security.addProvider(new BouncyCastleProvider()); Dieses Beispiel verbindet einen der bekanntesten Anbieter – BouncyCastle . In diesem Testbericht werden wir jedoch nur grundlegende Tools verwenden, ohne Bibliotheken von Drittanbietern. Unser Hauptdokument: „ Java Cryptography Architecture (JCA) “. Wenn Sie verstehen, wie JCA funktioniert, können Sie die Technologien, in denen dieselbe JCA aktiv eingesetzt wird, besser verstehen. Zum Beispiel: HTTPS (siehe „ Von HTTP zu HTTPS “).
Java-Kryptographiearchitektur: Erste Einführung - 5

MessageDigest

Das erste, was in der JCA-Dokumentation erwähnt wird, ist MessageDigest. Im Allgemeinen wird Digest auf Russisch dasselbe sein – ein Digest entspricht in seiner Bedeutung einer „Zusammenfassung“. Aber in der Kryptographie ist ein Digest eine Hash-Summe. Sie können sich auch leicht daran erinnern, dass „Digest“ im Englischen auch mit „Digest“ übersetzt werden kann. Weitere Details finden Sie in der JCA-Dokumentation im Abschnitt „ MessageDigest “. Wie in der Dokumentation angegeben, generiert MessageDigest ein Ergebnis fester Größe namens Digest oder Hash. Hashing ist eine Einwegfunktion, d.h. Wenn wir etwas gehasht haben, können wir aus dem Ergebnis (d. h. aus dem Hash) nicht die Originalquelle ermitteln. Wenn jedoch identische Objekte gehasht werden (z. B. Zeichenfolgen mit identischen Zeichen), muss ihr Hash übereinstimmen. Wie in der Dokumentation angegeben, wird ein solcher Hash manchmal auch als „Prüfsumme“ oder „digitaler Fingerabdruck“ von Daten bezeichnet. Hashing kann mit verschiedenen Algorithmen durchgeführt werden. Verfügbare Algorithmen können im Dokument „ Java Cryptography Architecture Standard Algorithm Name Documentation for JDK 8 “ eingesehen werden. Lassen Sie uns das Hashing durchführen und den Hash auf der Konsole ausgeben:
import javax.xml.bind.DatatypeConverter;
import java.security.*;
public class Main {
  public static void main(String[] args) {
    try {
      MessageDigest digester = MessageDigest.getInstance("SHA-512");
      byte[] input = "Secret string".getBytes();
      byte[] digest = digester.digest(input);
      System.out.println(DatatypeConverter.printHexBinary(digest));
    } catch (NoSuchAlgorithmException e) {
      throw new IllegalStateException(e);
    }
  }
}
Hashing kann beispielsweise beim Speichern von Passwörtern nützlich sein. Denn der Hash des eingegebenen Passwortes kann mit einem zuvor gespeicherten Hash verglichen werden. Wenn die Hashes übereinstimmen, stimmt auch das Passwort überein. Für noch sichereres Hashing wird ein Konzept namens „Salt“ verwendet. Salt kann mit der SecureRandom -Klasse implementiert werden . Bevor wir die Digest-Methode ausführen, beschreiben wir das Hinzufügen von „Salz“:
byte[] salt = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(salt);
digester.update(salt);
Aber Hash ist eine Einwegfunktion. Aber was ist, wenn Sie verschlüsseln und entschlüsseln möchten?
Java-Kryptographiearchitektur: Erste Einführung – 6

Symmetrische Schlüsselkryptographie

Bei der symmetrischen Verschlüsselung handelt es sich um eine Verschlüsselung, die für die Ver- und Entschlüsselung denselben Schlüssel verwendet. Um die symmetrische Verschlüsselung nutzen zu können, benötigen wir einen Schlüssel. Um es zu bekommen, verwenden wir KeyGenerator . Darüber hinaus benötigen wir eine Klasse, die eine Chiffre darstellt ( Cipher ). Wie in der JCA-Dokumentation im Abschnitt „ Erstellen eines Cipher-Objekts “ angegeben, müssen Sie zum Erstellen eines Cipher nicht nur einen Algorithmus, sondern auch eine „Transformation“ in der Zeile angeben. Die Transformationsbeschreibung sieht so aus: „algorithm/mode/padding“:
  • Algorithmus : Hier sehen wir uns die Standardnamen für „ Verschlüsselungsalgorithmen “ an. Es wird empfohlen, AES zu verwenden.
  • Modus : Verschlüsselungsmodus. Zum Beispiel: ECB oder CBC (wir werden etwas später darüber sprechen)
  • Einrückung/Aufteilung : Jeder Datenblock wird separat verschlüsselt. Dieser Parameter bestimmt, wie viele Daten als 1 Block gezählt werden.
Nehmen Sie zum Beispiel die folgende Transformation: "AES/ECB/PKCS5Padding". Das heißt, der Verschlüsselungsalgorithmus ist AES, der Verschlüsselungsmodus ist ECB (kurz für Electronic Codebook) und die Blockgröße ist PKCS5Padding. Laut PKCS5Padding beträgt die Größe eines Blocks 2 Byte (16 Bit). Der Verschlüsselungsmodus „Electronic Codebook“ beinhaltet die sequentielle Verschlüsselung jedes Blocks:
Java-Kryptographiearchitektur: Erste Einführung – 7
Im Code könnte es so aussehen:
import javax.xml.bind.DatatypeConverter;
import javax.crypto.*;
import java.security.Key;
public class Main {
  public static void main(String[] args) throws Exception {
    String text = "secret!!secret!!secret!!secret!!";
    // Generate new key
    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    keygen.init(256);
    Key key = keygen.generateKey();
    // Encrypt with key
    String transformation = "AES/ECB/PKCS5Padding";
    Cipher cipher = Cipher.getInstance(transformation);
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] encrypted = cipher.doFinal(text.getBytes());
    System.out.println(DatatypeConverter.printHexBinary(encrypted));
    // Decrypt with key
    cipher.init(Cipher.DECRYPT_MODE, key);
    String result = new String(cipher.doFinal(encrypted));
    System.out.println(result);
  }
}
Wenn wir es ausführen, werden wir mit einer Wiederholung rechnen, denn wir haben 32 Zeichen angegeben. Diese Zeichen bilden 2 Blöcke zu je 16 Bit:
Java-Kryptographiearchitektur: Erste Einführung - 8
Um in diesem Fall eine Wiederholung zu vermeiden, sollten Sie einen anderen Modus verwenden – Cipher Block Chaining (CBC). In diesem Modus wird das Konzept des Initialisierungsvektors (dargestellt durch die IvParameterSpec-Klasse) eingeführt. Und dank dieses Modus wird das Ergebnis der Generierung des letzten Blocks auch zur Generierung des nächsten verwendet:
Java-Kryptographiearchitektur: Erste Einführung - 9
Schreiben wir das jetzt im Code:
import javax.xml.bind.DatatypeConverter;
import javax.crypto.*;
import java.security.*;
import javax.crypto.spec.IvParameterSpec;
public class Main {
  public static void main(String[] args) throws Exception {
    // Initialization Vector
    SecureRandom random = SecureRandom.getInstanceStrong();
    byte[] rnd = new byte[16];
    random.nextBytes(rnd);
    IvParameterSpec ivSpec = new IvParameterSpec(rnd);
    // Prepare key
    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    keygen.init(256);
    Key key = keygen.generateKey();
    // CBC
    String text = "secret!!secret!!secret!!secret!!";
    String transformation = "AES/CBC/PKCS5Padding";
    Cipher cipher = Cipher.getInstance(transformation);
    cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
    byte[] enc = cipher.doFinal(text.getBytes());
    System.out.println(DatatypeConverter.printHexBinary(enc));
    // Decrypt
    cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
    String result = new String(cipher.doFinal(enc));
    System.out.println(result);
  }
}
Wie wir sehen, sehen wir daher keine wiederholten Chiffrierblöcke. Aus diesem Grund wird der ECB-Modus nicht empfohlen, weil ermöglicht es, Wiederholungen zu erkennen und dieses Wissen zur Entschlüsselung zu nutzen. Für weitere Informationen zu ECB und CBC empfehle ich Ihnen, das Material zu lesen: „ Elektronischer Codebuchmodus “. Bei der symmetrischen Verschlüsselung besteht jedoch ein offensichtliches Problem: Sie müssen den Schlüssel irgendwie von demjenigen, der verschlüsselt, auf den übertragen, der verschlüsselt. Und auf diesem Weg kann dieser Schlüssel abgefangen werden und dann ist es möglich, Daten abzufangen. Und die asymmetrische Verschlüsselung soll dieses Problem lösen.
Java-Kryptographiearchitektur: Erste Einführung – 10

Asymmetrische Verschlüsselung

Asymmetrische Verschlüsselung oder Public-Key-Kryptografie ist eine Verschlüsselungsmethode, die ein Schlüsselpaar verwendet: einen privaten Schlüssel (der vor allen geheim gehalten wird) und einen öffentlichen Schlüssel (der für die Öffentlichkeit zugänglich ist). Diese Trennung ist notwendig, um den öffentlichen Schlüssel sicher zwischen den am Informationsaustausch beteiligten Parteien auszutauschen und gleichzeitig den geheimen Schlüssel sicher aufzubewahren. Beim Erstellen eines Schlüsselpaares reicht uns KeyGenerator nicht mehr aus, wir benötigen KeyPairGenerator . Schauen wir uns ein Beispiel an:
import javax.crypto.*;
import java.security.*;
public class Main {
  public static void main(String[] args) throws Exception {
    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
    generator.initialize(1024);
    KeyPair keyPair = generator.generateKeyPair();
    // Encrypt with PRIVATE KEY
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
    byte[] data = cipher.doFinal("Hello!".getBytes());
    // Decrypt with PUBLIC KEY
    cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
    byte[] result = cipher.doFinal(data);
    System.out.println(new String(result));
  }
}
Hierbei ist es wichtig zu verstehen, dass wir bei der Verwendung der asymmetrischen Verschlüsselung immer KeyPair verwenden, um einen Schlüssel für die Verschlüsselung und einen anderen für die Entschlüsselung zu verwenden. Aber weil Der Sinn der Verschlüsselung besteht darin, dass nur der Empfänger sie entschlüsseln kann; sie wird mit einem öffentlichen Schlüssel verschlüsselt und nur mit einem privaten entschlüsselt.
Java-Kryptographiearchitektur: Erste Bekanntschaft - 11

Digitale Unterschrift

Wie wir oben gesehen haben, können Sie mit Kenntnis des öffentlichen Schlüssels Daten so senden, dass nur der Besitzer des privaten Schlüssels sie entschlüsseln kann. Das heißt, das Wesen der asymmetrischen Verschlüsselung besteht darin, dass jeder verschlüsselt, aber nur wir lesen. Es gibt auch ein umgekehrtes Verfahren – eine digitale Signatur, dargestellt durch die Signature- Klasse . Eine digitale Signatur kann die folgenden Algorithmen verwenden: „ Signaturalgorithmen “. Die JCA-Dokumentation schlägt vor, sich diese beiden genauer anzusehen: DSAwithMD5 und RSAwithMD5. Was ist besser als DSA oder RSA und was ist ihr Unterschied? Lesen Sie hier: „Welches funktioniert am besten für verschlüsselte Dateiübertragungen – RSA oder DSA? “. Oder lesen Sie die Diskussionen hier: „ RSA vs. DSA für SSH-Authentifizierungsschlüssel “. Also digitale Signatur. Wir benötigen wie zuvor ein KeyPair und eine neue Signature-Klasse. Wenn Sie bisher Online-Compiler getestet haben, könnte das folgende Beispiel für sie etwas schwierig sein. Mein Beispiel lief nur hier: rextester.com . Wir importieren die Klassen, die wir brauchen:
import javax.crypto.*;
import java.security.*;
Wir werden auch die Hauptmethode umschreiben:
public static void main(String[] args) throws Exception {
    // Generate keys
    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
    SecureRandom random = SecureRandom.getInstanceStrong();
    generator.initialize(2048, random);
    KeyPair keyPair = generator.generateKeyPair();
    // Digital Signature
    Signature dsa = Signature.getInstance("SHA256withRSA");
    dsa.initSign(keyPair.getPrivate());
    // Update and sign the data
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
    byte[] data = cipher.doFinal("Hello!".getBytes());
    dsa.update(data);
    byte[] signature = dsa.sign();
    // Verify signature
    dsa.initVerify(keyPair.getPublic());
    dsa.update(data);
    boolean verifies = dsa.verify(signature);
    System.out.println("Signature is ok: " + verifies);
    // Decrypt if signature is correct
    if (verifies) {
      cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
      byte[] result = cipher.doFinal(data);
      System.out.println(new String(result));
    }
}
So funktioniert eine digitale Signatur. Digitale Signatur ist ein interessantes Thema. Ich empfehle Ihnen, sich den Bericht zu diesem Thema anzusehen:
Java-Kryptographiearchitektur: Erste Bekanntschaft - 12
Oben haben wir gesehen, wie die Parteien Daten austauschen. Gibt es in JCA nicht eine Standardschnittstelle für diese Interaktion? Es stellt sich heraus, dass es so ist. Schauen wir es uns an.
Java-Kryptographiearchitektur: Erste Bekanntschaft - 13

Schlüsselvereinbarung

Die Java-Kryptographiearchitektur stellt ein wichtiges Werkzeug vor: Die Schlüsselvereinbarung ist ein Protokoll. Es wird durch die KeyAgreement- Klasse dargestellt . Wie in der JCA-Dokumentation angegeben, können Sie mit diesem Protokoll denselben kryptografischen Schlüssel für mehrere Parteien festlegen, ohne dass geheime Informationen zwischen den Parteien übertragen werden. Klingt komisch? Dann schauen wir uns ein Beispiel an:
// 1. Одна из сторон (Алиса) генерирует пару ключей. Encoded публичный ключ отдаёт.
KeyPairGenerator generator = KeyPairGenerator.getInstance("DH");
KeyPair aliceKeyPair = generator.generateKeyPair();
byte[] alicePubKeyEncoded = aliceKeyPair.getPublic().getEncoded();

// 2. Другая сторона (например, Боб) получает открытый ключ Алисы
KeyFactory bobKeyFactory = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(alicePubKeyEncoded);
PublicKey alicePubKey = bobKeyFactory.generatePublic(x509KeySpec);
// Параметры, которые использовала Алиса при генерации ключей
DHParameterSpec dhParamFromAlicePubKey = ((DHPublicKey)alicePubKey).getParams();
// Создаёт свою пару ключей. Отдаёт свой Encoded открытый ключ
KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
bobKpairGen.initialize(dhParamFromAlicePubKey);
KeyPair bobKeyPair = bobKpairGen.generateKeyPair();
byte[] bobPubKeyEncoded = bobKeyPair.getPublic().getEncoded();

Теперь, у Алисы есть открытый ключ Боба, а у Боба есть открытый ключ Алисы. Was дальше?
Как сказано в dokumentierenации JCA, у нас есть инструмент KeyAgreement, https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyAgreement который позволяет установить одинаковые ключи шифрования без необходимости обмениваться секретной информацией (т.е. без обмена private key). Соглашение выглядит следующим образом:
// 3. Соглашение по протоколу Диффи-Хеллмана (Diffie–Hellman, DH)
KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
aliceKeyAgree.init(aliceKeyPair.getPrivate());
// Алиса на основе ключа боба и своего private key создаёт общий shared ключ
KeyFactory aliceKeyFactory = KeyFactory.getInstance("DH");
x509KeySpec = new X509EncodedKeySpec(bobPubKeyEncoded);
PublicKey bobPubKey = aliceKeyFactory.generatePublic(x509KeySpec);
aliceKeyAgree.doPhase(bobPubKey, true);
byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();
SecretKeySpec aliceAesKey = new SecretKeySpec(aliceSharedSecret, 0, 16, "AES");
// Боб на основе ключа Алисы и своего private key создаёт общий shared ключ
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
bobKeyAgree.init(bobKeyPair.getPrivate());
bobKeyAgree.doPhase(alicePubKey, true);
byte[] bobSharedSecret = bobKeyAgree.generateSecret();
SecretKeySpec bobAesKey = new SecretKeySpec(bobSharedSecret, 0, 16, "AES");
// Общий ключ у Алисы и Боба одинаков
System.out.println("Shared keys are equals: " + Arrays.equals(aliceSharedSecret, bobSharedSecret));

Далее Боб и Алиса, используя общий ключ, про который больше никто не знает, обмениваются зашифрованными данными:
// 4. Боб шифрует сообщение для Алисы
Cipher bobCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
bobCipher.init(Cipher.ENCRYPT_MODE, bobAesKey);
byte[] ciphertext = bobCipher.doFinal("Hello, Alice!".getBytes());
// Передаёт Алисе параметры, с которыми выполнялась шифровка
byte[] encodedParamsFromBob = bobCipher.getParameters().getEncoded();

// 5. Алиса принимает сообщение и расшифровывает его
AlgorithmParameters aesParams = AlgorithmParameters.getInstance("AES");
aesParams.init(encodedParamsFromBob);
Cipher aliceCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aliceCipher.init(Cipher.DECRYPT_MODE, aliceAesKey, aesParams);
byte[] recovered = aliceCipher.doFinal(ciphertext);
System.out.println(new String(recovered));
Dieses Beispiel wurde dem JCA-Dokumentationsbeispiel „ Diffie-Hellman Key Exchange between 2 Parties “ entnommen. In etwa sieht die asymmetrische Verschlüsselung in der Java-Kryptographiearchitektur unter Verwendung des Schlüsselvereinbarungsprotokolls aus. Weitere Informationen zur asymmetrischen Verschlüsselung finden Sie in den empfohlenen Videos:
Java-Kryptographiearchitektur: Erste Bekanntschaft - 14

Zertifikate

Nun, zum Nachtisch haben wir noch etwas nicht weniger Wichtiges – Zertifikate. Normalerweise werden Zertifikate mit dem im JDK enthaltenen Dienstprogramm keytool generiert. Weitere Details können Sie beispielsweise hier nachlesen: „ Generieren eines selbstsignierten SSL-Zertifikats mit dem Java-Befehl keytool “. Sie können auch die Handbücher von Oracle lesen. Zum Beispiel hier: „ So verwenden Sie keytool zum Erstellen eines Serverzertifikats “. Verwenden wir zum Beispiel den Tutorialspoint Java Online Compiler :
import sun.security.tools.keytool.CertAndKeyGen;
import sun.security.x509.*;
import java.security.cert.*;
import java.security.*;
// Compiler args: -XDignore.symbol.file
public class Main {
  public static void main(String[] args) throws Exception {
    CertAndKeyGen certGen = new CertAndKeyGen("RSA", "SHA256WithRSA", null);
    // generate it with 2048 bits
    certGen.generate(2048);
    PrivateKey privateKey = certGen.getPrivateKey();
    X509Key publicKey = certGen.getPublicKey();
    // prepare the validity of the certificate
    long validSecs = (long) 365 * 24 * 60 * 60; // valid for one year
    // enter your details according to your application
    X500Name principal = new X500Name("CN=My Application,O=My Organisation,L=My City,C=DE");
    // add the certificate information, currently only valid for one year.
    X509Certificate cert = certGen.getSelfCertificate(principal, validSecs);
    // Public Key from Cert equals Public Key from generator
    PublicKey publicKeyFromCert = cert.getPublicKey();
    System.out.println(publicKeyFromCert.equals(publicKey));
  }
}
Wie wir sehen, bietet ein Zertifikat die Möglichkeit, einen öffentlichen Schlüssel bereitzustellen. Diese Methode hat einen Nachteil – wir verwenden sun.security, was als riskant gilt, weil... Dieses Paket ist nicht Teil der öffentlichen Java-API. Deshalb ist es beim Kompilieren notwendig, den Parameter - anzugeben XDignore.symbol.file. Es gibt eine andere Möglichkeit – ein Zertifikat manuell zu erstellen. Der Nachteil besteht darin, dass eine interne API verwendet wird, die nicht dokumentiert ist. Es ist jedoch nützlich, darüber Bescheid zu wissen. Zumindest, weil deutlich erkennbar ist, wie die RFC-2459-Spezifikation genutzt wird: „ Internet X.509 Public Key Infrastructure “. Hier ist ein Beispiel:
// 1. Генерируем пару ключей
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(4096);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 2. Определяем данные сертификата
// Определяем срок действия сертификата
Date from = new Date();
Date to = new Date(from.getTime() + 365 * 1000L * 24L * 60L * 60L);
CertificateValidity interval = new CertificateValidity(from, to);
// Определяем subject name, т.е. Name того, с чем ассоциирован публичный ключ
// CN = Common Name. Через точку с запятой могут быть указаны также другие атрибуты
// См. https://docs.oracle.com/cd/E24191_01/common/tutorials/authz_cert_attributes.html
X500Name owner = new X500Name("cn=Unknown");
// Уникальный в пределах CA, т.е. Certificate Authority (тот, кто выдаёт сертификат) номер
BigInteger number = new BigInteger(64, new SecureRandom());
CertificateSerialNumber serialNumber = new CertificateSerialNumber(number);
// Определяем алгоритм подписи сертификата
AlgorithmId algorithmId = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
CertificateAlgorithmId certificateAlgorithmId = new CertificateAlgorithmId(algorithmId);
// 3. По подготовленной информации создаём сертификат
X509CertInfo info = new X509CertInfo();
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, serialNumber);
info.set(X509CertInfo.SUBJECT, owner);
info.set(X509CertInfo.ISSUER, owner);
info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
info.set(X509CertInfo.ALGORITHM_ID, certificateAlgorithmId);
// 4. Подписываем сертификат
X509CertImpl certificate = new X509CertImpl(info);
certificate.sign(keyPair.getPrivate(), "SHA256withRSA");
// 5. Проверка сертификата
try {
	// В случае ошибки здесь будет брошено исключение. Например: java.security.SignatureException
	certificate.verify(keyPair.getPublic());
} catch (Exception e) {
	throw new IllegalStateException(e);
}
Java-Kryptographiearchitektur: Erste Einführung – 15

Schlüsselspeicher (KeyStore)

Das Letzte, worüber ich sprechen möchte, ist der Schlüssel- und Zertifikatsspeicher, der KeyStore genannt wird. Es ist klar, dass die ständige Generierung von Zertifikaten und Schlüsseln teuer und sinnlos ist. Daher müssen sie irgendwie sicher aufbewahrt werden. Dafür gibt es ein Tool – KeyStore. Der Schlüsselspeicher ist in der JCA-Dokumentation im Kapitel „ KeyManagement “ beschrieben. Die API zum Arbeiten damit ist sehr klar. Hier ein kleines Beispiel:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
String alias = "EntityAlias";
java.security.cert.Certificate[] chain = {certificate};
keyStore.setKeyEntry(alias, keyPair.getPrivate(), "keyPassword".toCharArray(), chain);
// Загрузка содержимого (Private Key + Certificate)
Key key = keyStore.getKey(alias, "keyPassword".toCharArray());
Certificate[] certificateChain = keyStore.getCertificateChain(alias);
// Сохранение KeyStore на диск
File file = File.createTempFile("security_", ".ks");
System.out.println(file.getAbsolutePath());
try (FileOutputStream fos = new FileOutputStream(file)) {
	keyStore.store(fos, "keyStorePassword".toCharArray());
}
Wie Sie dem Beispiel entnehmen können, wird es zunächst loadfür den KeyStore ausgeführt. Aber in unserem Fall haben wir das erste Attribut als null angegeben, d. h. Es gibt keine Quelle für KeyStore. Das bedeutet, dass der KeyStore leer angelegt wird, um ihn weiter zu speichern. Der zweite Parameter ist ebenfalls null, weil Wir erstellen einen neuen KeyStore. Wenn wir KeyStore aus einer Datei laden würden, müssten wir hier ein Passwort angeben (ähnlich der KeyStore-Methode namens „store“).

Endeffekt

Deshalb haben wir mit Ihnen die grundlegendsten und elementarsten Aktionen im Rahmen der Java Cryptography Architecture (auch bekannt als JCA) besprochen. Wir haben gesehen, was symmetrische und asymmetrische Verschlüsselung ist und wie sie in JCA implementiert wird. Wir haben gesehen, wie Zertifikate und digitale Signaturen erstellt und verwendet werden. Dies sind alles nur die Grundlagen, hinter denen sich noch viele komplexere und interessantere Dinge verbergen. Ich hoffe, dass dieses Rezensionsmaterial nützlich ist und Sie für die weitere Beschäftigung mit diesem Bereich interessiert.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION