Leitfaden zur Verschlüsselungsklasse

1. Übersicht

Einfach ausgedrückt bedeutet Verschlüsselung, dass eine Nachricht so verschlüsselt wird, dass nur autorisierte Benutzer sie verstehen oder darauf zugreifen können.

Die als Klartext bezeichnete Nachricht wird mit einem Verschlüsselungsalgorithmus verschlüsselt - einem Chiffrier- generierenden Chiffretext , der nur von autorisierten Benutzern durch Entschlüsselung gelesen werden kann.

In diesem Artikel beschreiben wir ausführlich die Kernklasse von Cipher , die kryptografische Ver- und Entschlüsselungsfunktionen in Java bereitstellt .

2. Verschlüsselungsklasse

Java Cryptography Extension (JCE) ist der Teil der Java Cryptography Architecture (JCA) , der eine Anwendung mit kryptografischen Chiffren für die Datenverschlüsselung und -entschlüsselung sowie für das Hashing privater Daten bereitstellt.

Die Cipher- Klasse im Paket javax.crypto bildet den Kern des JCE-Frameworks und bietet die Funktionalität für die Ver- und Entschlüsselung.

2.1. Verschlüsselungsinstanziierung

Um ein Cipher- Objekt zu instanziieren , rufen wir die statische Methode getInstance auf und übergeben den Namen der angeforderten Transformation . Optional kann der Name eines Anbieters angegeben werden.

Schreiben wir eine Beispielklasse, die die Instanziierung einer Chiffre veranschaulicht :

public class Encryptor { public byte[] encryptMessage(byte[] message, byte[] keyBytes) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); //... } }

Die Transformation AES / ECB / PKCS5Padding weist die getInstance- Methode an, das Cipher- Objekt als AES-Chiffre mit ECB-Betriebsmodus und PKCS5-Auffüllschema zu instanziieren .

Wir können das Cipher- Objekt auch instanziieren, indem wir nur den Algorithmus in der Transformation angeben:

Cipher cipher = Cipher.getInstance("AES");

In diesem Fall verwendet Java anbieterspezifische Standardwerte für den Modus und das Auffüllschema.

Beachten Sie, dass getInstance eine NoSuchAlgorithmException auslöst, wenn die Umwandlung null , leer oder in einem ungültigen Format ist oder wenn der Anbieter sie nicht unterstützt.

Es wird eine NoSuchPaddingException ausgelöst, wenn die Umwandlung ein nicht unterstütztes Auffüllschema enthält.

2.2. Gewindesicherheit

Die Cipher- Klasse ist eine Stateful-Klasse ohne interne Synchronisation. Tatsächlich ändern Methoden wie init () oder update () den internen Status einer bestimmten Cipher- Instanz.

Daher ist die Cipher- Klasse nicht threadsicher. Also sollten wir erstellen Cipher Instanz pro Verschlüsselung / Entschlüsselung notwendig.

2.3. Schlüssel

Die Schlüsselschnittstelle repräsentiert Schlüssel für kryptografische Operationen. Schlüssel sind undurchsichtige Container, die einen codierten Schlüssel, das Codierungsformat des Schlüssels und seinen kryptografischen Algorithmus enthalten.

Schlüssel werden im Allgemeinen über Schlüsselgeneratoren, Zertifikate oder Schlüsselspezifikationen unter Verwendung einer Schlüsselfabrik erhalten.

Erstellen wir einen symmetrischen Schlüssel aus den angegebenen Schlüsselbytes:

SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");

2.4. Verschlüsselungsinitialisierung

Wir rufen die Methode init () auf, um das C ipher- Objekt mit einem Schlüssel oder Zertifikat und einem Operationsmodus zu initialisieren, der den Betriebsmodus der Verschlüsselung angibt.

Optional können wir eine Zufallsquelle übergeben . Standardmäßig wird eine SecureRandom- Implementierung des installierten Anbieters mit der höchsten Priorität verwendet. Andernfalls wird eine vom System bereitgestellte Quelle verwendet.

Optional können wir eine Reihe von algorithmischen Parametern angeben. Beispielsweise können wir eine IvParameterSpec übergeben , um einen Initialisierungsvektor anzugeben .

Hier sind die verfügbaren Verschlüsselungsbetriebsarten:

  • ENCRYPT_MODE : initialize Chiffre - Objekt Verschlüsselungsmodus
  • DECRYPT_MODE : Initialisiert das Verschlüsselungsobjekt in den Entschlüsselungsmodus
  • WRAP_MODE : Initialisiert das Verschlüsselungsobjekt in den Schlüsselumhüllungsmodus
  • UNWRAP_MODE : Initialisieren Sie das Verschlüsselungsobjekt in den Schlüsselentpackungsmodus

Initialisieren wir das Cipher- Objekt:

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); // ...

Jetzt löst die init- Methode eine InvalidKeyException aus, wenn der angegebene Schlüssel nicht zum Initialisieren der Verschlüsselung geeignet ist, z. B. wenn eine Schlüssellänge / -codierung ungültig ist.

Es wird auch ausgelöst, wenn für die Verschlüsselung bestimmte Algorithmusparameter erforderlich sind, die nicht aus dem Schlüssel ermittelt werden können, oder wenn der Schlüssel eine Schlüsselgröße hat, die die maximal zulässige Schlüsselgröße überschreitet (ermittelt aus den konfigurierten JCE-Zuständigkeitsrichtliniendateien).

Schauen wir uns ein Beispiel mit einem Zertifikat an :

public byte[] encryptMessage(byte[] message, Certificate certificate) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, certificate); // ... }

Das Cipher- Objekt erhält den öffentlichen Schlüssel für die Datenverschlüsselung aus dem Zertifikat, indem die Methode getPublicKey aufgerufen wird.

2.5. Verschlüsselung und Entschlüsselung

Nach dem Initialisieren des Cipher- Objekts rufen wir die Methode doFinal () auf, um die Verschlüsselungs- oder Entschlüsselungsoperation auszuführen. Diese Methode gibt ein Byte-Array zurück, das die verschlüsselte oder entschlüsselte Nachricht enthält.

The doFinal() method also resets the Cipher object to the state it was in when previously initialized via a call to init() method, making the Cipher object available to encrypt or decrypt additional messages.

Let's call doFinal in our encryptMessage method:

public byte[] encryptMessage(byte[] message, byte[] keyBytes) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(message); }

To perform a decrypt operation, we change the opmode to DECRYPT_MODE:

public byte[] decryptMessage(byte[] encryptedMessage, byte[] keyBytes) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES"); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(encryptedMessage); }

2.6. Providers

Designed to use a provider-based architecture, the JCE allows for qualified cryptography libraries such as BouncyCastle to be plugged in as security providers and new algorithms to be added seamlessly.

Now let's add BouncyCastle as a security provider. We can add a security provider either statically or dynamically.

To add BouncyCastle statically, we modify the java.security file located in /jre/lib/security folder.

We add the line at the end of the list:

... security.provider.4=com.sun.net.ssl.internal.ssl.Provider security.provider.5=com.sun.crypto.provider.SunJCE security.provider.6=sun.security.jgss.SunProvider security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider

When adding the provider property, the property key is in the format security.provider.N where the number N is one more than the last one on the list.

We can also add the BouncyCastle security provider dynamically without having to modify the security file:

Security.addProvider(new BouncyCastleProvider());

We can now specify the provider during cipher initialization:

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC");

BC specifies BouncyCastle as the provider. We can get the list of registered providers via the Security.getProviders() method.

3. Testing Encryption and Decryption

Let's write an example test to illustrate message encryption and decryption.

In this test, we use AES encryption algorithm with a 128-bit key and assert that the decrypted result is equal to the original message text:

@Test public void whenIsEncryptedAndDecrypted_thenDecryptedEqualsOriginal() throws Exception { String encryptionKeyString = "thisisa128bitkey"; String originalMessage = "This is a secret message"; byte[] encryptionKeyBytes = encryptionKeyString.getBytes(); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(encryptionKeyBytes, "AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedMessageBytes = cipher.doFinal(message.getBytes()); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decryptedMessageBytes = cipher.doFinal(encryptedMessageBytes); assertThat(originalMessage).isEqualTo(new String(decryptedMessageBytes)); }

4. Conclusion

In this article, we discussed the Cipher class and presented usage examples. More details on the Cipher class and the JCE Framework can be found in the class documentation and the Java Cryptography Architecture (JCA) Reference Guide.

Die Implementierung all dieser Beispiele und Codefragmente finden Sie auf GitHub . Dies ist ein Maven-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.