Teilen Sie die password
(a char[]
) und salt
(a byte[]
-8 Bytes, ausgewählt durch eine SecureRandom
ist ein gutes Salz, das nicht geheim gehalten werden muss) mit dem Empfänger out-of-band. Aus diesen Informationen kann dann ein guter Schlüssel abgeleitet werden:
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Die magischen Zahlen (die irgendwo als Konstanten definiert werden könnten) 65536 und 256 sind die Iterationszahl der Schlüsselableitung bzw. die Schlüsselgröße.
Die Funktion zur Ableitung des Schlüssels wird iteriert, so dass ein erheblicher Rechenaufwand erforderlich ist, der Angreifer daran hindert, schnell viele verschiedene Passwörter auszuprobieren. Die Anzahl der Iterationen kann je nach den verfügbaren Rechenressourcen geändert werden.
Die Schlüsselgröße kann auf 128 Bit reduziert werden, was immer noch als "starke" Verschlüsselung gilt, aber keine große Sicherheitsmarge bietet, wenn Angriffe entdeckt werden, die AES schwächen.
In Verbindung mit einem geeigneten Blockchain-Modus kann derselbe abgeleitete Schlüssel zur Verschlüsselung vieler Nachrichten verwendet werden. Unter Cipher Block Chaining (CBC) Bei CBC wird für jede Nachricht ein zufälliger Initialisierungsvektor (IV) generiert, der selbst bei identischem Klartext zu unterschiedlichem Chiffriertext führt. CBC ist vielleicht nicht der sicherste Modus, der Ihnen zur Verfügung steht (siehe AEAD weiter unten); es gibt viele andere Modi mit unterschiedlichen Sicherheitseigenschaften, aber sie verwenden alle eine ähnliche Zufallsinitialisierung. In jedem Fall sind die Ausgaben jeder Verschlüsselungsoperation der verschlüsselte Text y den Initialisierungsvektor:
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes(StandardCharsets.UTF_8));
Speichern Sie die ciphertext
und die iv
. Bei der Entschlüsselung wird die SecretKey
wird auf genau dieselbe Weise neu generiert, wobei das Kennwort mit demselben Salt und denselben Iterationsparametern verwendet wird. Initialisieren Sie die Chiffre mit diesem Schlüssel y den mit der Nachricht gespeicherten Initialisierungsvektor:
/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8);
System.out.println(plaintext);
Java 7 enthaltene API Unterstützung für AEAD-Verschlüsselungsmodi und der in OpenJDK und Oracle-Distributionen enthaltene "SunJCE"-Anbieter implementiert diese ab Java 8. Einer dieser Modi wird anstelle von CBC dringend empfohlen; er schützt sowohl die Integrität der Daten als auch ihre Privatsphäre.
A java.security.InvalidKeyException
mit der Meldung "Illegale Schlüsselgröße oder Standardparameter" bedeutet, dass die Kryptostärke es begrenzt; die Dateien mit den Richtlinien für die unbegrenzte Zuständigkeit befinden sich nicht an der richtigen Stelle. In einem JDK sollten sie sich unter ${jdk}/jre/lib/security
Ausgehend von der Problembeschreibung klingt es so, als wären die Richtliniendateien nicht korrekt installiert. Systeme können leicht mehrere Java-Laufzeiten haben; überprüfen Sie, ob der richtige Speicherort verwendet wird.