it-swarm.com.de

CryptoJS AES-Verschlüsselung und Java-AES-Entschlüsselung

Ich frage das nur, weil ich seit zwei Tagen viele Beiträge über die Verschlüsselung mit AES-Verschlüsselung gelesen habe, und als ich dachte, ich würde es bekommen, wurde mir klar, dass ich es überhaupt nicht bekam. 

Dieser Beitrag ist am nächsten zu meinem Thema, ich habe genau das gleiche Problem, aber es ist nicht beantwortet:

CryptoJS-AES-Verschlüsselung und Nichtübereinstimmung der Java-AES-Entschlüsselungswerte

Ich habe es in vielerlei Hinsicht versucht, aber ich habe es nicht richtig verstanden.

Erst einmal

Ich bekomme die bereits verschlüsselte Zeichenfolge (ich habe nur den Code erhalten, um zu sehen, wie sie es getan haben), also ist das Ändern der Verschlüsselungsmethode keine Option. Deshalb sind mir alle ähnlichen Fragen nicht so nützlich.

Zweite

Ich habe Zugriff auf den geheimen Schlüssel und kann ihn modifizieren (das Anpassen der Länge ist eine Option, falls erforderlich).

Die Verschlüsselung erfolgt auf CryptoJS und sie senden die verschlüsselte Zeichenfolge als GET-Parameter.

GetParamsForAppUrl.prototype.generateUrlParams = function() {
const self = this;
 return new Promise((resolve, reject) => {
   const currentDateInMilliseconds = new Date().getTime();
   const secret = tokenSecret.secret;
   var encrypted = CryptoJS.AES.encrypt(self.authorization, secret);
   encrypted = encrypted.toString();
   self.urlParams = {
     token: encrypted,
     time: currentDateInMilliseconds
   };
   resolve();
 });
};

Ich kann dies auf Javascript mit CryptoJS leicht entschlüsseln mit:

var decrypted = CryptoJS.AES.decrypt(encrypted_string, secret);
    console.log(decrypted.toString(CryptoJS.enc.Utf8)); 

Aber ich möchte dies aus Sicherheitsgründen nicht auf Javascript machen, also versuche ich, dies auf Java zu entschlüsseln:

String secret = "secret";
byte[] cipherText = encrypted_string.getBytes("UTF8");
SecretKey secKey = new SecretKeySpec(secret.getBytes(), "AES");
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.DECRYPT_MODE, secKey);
byte[] bytePlainText = aesCipher.doFinal(byteCipherText);
String myDecryptedText = = new String(bytePlainText);

Bevor ich eine Vorstellung von dem hatte, was ich tat, habe ich die base64-Dekodierung versucht, ein paar IV hinzugefügt und viele Sachen, die ich gelesen habe. Natürlich hat nichts davon funktioniert. 

Aber nachdem ich angefangen hatte, irgendwie zu verstehen, was ich tat, schrieb ich oben dieses einfache Skript und erhielt den gleichen Fehler in dem Post: Invalid AES-Schlüssellänge

Ich weiß nicht, wohin ich gehen soll. Nachdem ich viel darüber gelesen habe, scheint die Lösung Hashing oder Padding zu sein, aber ich habe keine Kontrolle über die Verschlüsselungsmethode, also kann ich das Geheimnis nicht wirklich hashen oder aufpolstern.

Aber wie gesagt, ich kann den geheimen Schlüssel ändern, damit er mit einer bestimmten Länge übereinstimmt, und ich habe versucht, ihn zu ändern, aber da ich hier im Dunkeln schieße, weiß ich nicht wirklich, ob dies die Lösung ist.

Meine Frage ist also im Grunde: Wenn ich die verschlüsselte Zeichenfolge (in Javascript wie das erste Skript) und den geheimen Schlüssel erhalten habe, gibt es eine Möglichkeit, sie zu entschlüsseln (in Java)? Wenn ja, wie geht das?

8
Lauro182

CryptoJS implementiert dieselbe Schlüsselableitungsfunktion wie OpenSSL und dasselbe Format, um die IV in die verschlüsselten Daten einzufügen. Daher gilt der gesamte Java-Code, der mit OpenSSL-codierten Daten arbeitet.

Gegeben der folgende Javascript-Code:

var text = "The quick brown fox jumps over the lazy dog. ???? ????";
var secret = "René Über";
var encrypted = CryptoJS.AES.encrypt(text, secret);
encrypted = encrypted.toString();
console.log("Cipher text: " + encrypted);

Wir bekommen den Chiffretext:

U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=

Auf der Java-Seite haben wir

String secret = "René Über";
String cipherText = "U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=";

byte[] cipherData = Base64.getDecoder().decode(cipherText);
byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);

MessageDigest md5 = MessageDigest.getInstance("MD5");
final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes(StandardCharsets.UTF_8), md5);
SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);

byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length);
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decryptedData = aesCBC.doFinal(encrypted);
String decryptedText = new String(decryptedData, StandardCharsets.UTF_8);

System.out.println(decryptedText);

Das Ergebnis ist:

The quick brown fox jumps over the lazy dog. ???? ????

Mit diesem Text haben wir angefangen. Auch Emojis, Akzente und Umlaute funktionieren.

GenerateKeyAndIV ist eine Hilfsfunktion, die OpenSSLs Schlüsselableitungsfunktion EVP_BytesToKey neu implementiert (siehe https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c ).

/**
 * Generates a key and an initialization vector (IV) with the given salt and password.
 * <p>
 * This method is equivalent to OpenSSL's EVP_BytesToKey function
 * (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c).
 * By default, OpenSSL uses a single iteration, MD5 as the algorithm and UTF-8 encoded password data.
 * </p>
 * @param keyLength the length of the generated key (in bytes)
 * @param ivLength the length of the generated IV (in bytes)
 * @param iterations the number of digestion rounds 
 * @param salt the salt data (8 bytes of data or <code>null</code>)
 * @param password the password data (optional)
 * @param md the message digest algorithm to use
 * @return an two-element array with the generated key and IV
 */
public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {

    int digestLength = md.getDigestLength();
    int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
    byte[] generatedData = new byte[requiredLength];
    int generatedLength = 0;

    try {
        md.reset();

        // Repeat process until sufficient data has been generated
        while (generatedLength < keyLength + ivLength) {

            // Digest data (last digest if available, password data, salt if available)
            if (generatedLength > 0)
                md.update(generatedData, generatedLength - digestLength, digestLength);
            md.update(password);
            if (salt != null)
                md.update(salt, 0, 8);
            md.digest(generatedData, generatedLength, digestLength);

            // additional rounds
            for (int i = 1; i < iterations; i++) {
                md.update(generatedData, generatedLength, digestLength);
                md.digest(generatedData, generatedLength, digestLength);
            }

            generatedLength += digestLength;
        }

        // Copy key and IV into separate byte arrays
        byte[][] result = new byte[2][];
        result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
        if (ivLength > 0)
            result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);

        return result;

    } catch (DigestException e) {
        throw new RuntimeException(e);

    } finally {
        // Clean out temporary data
        Arrays.fill(generatedData, (byte)0);
    }
}

Beachten Sie, dass Sie die Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy installieren müssen. Andernfalls funktioniert AES mit einer Schlüsselgröße von 256 nicht und löst eine Ausnahme aus:

Java.security.InvalidKeyException: Illegal key size

Update

Ich habe Ola Binis Java-Code von EVP_BytesToKey, den ich in der ersten Version meiner Antwort verwendet habe, durch einen idiomatischeren und verständlicheren Java-Code ersetzt (siehe oben).

Siehe auch Wie entschlüsselt man Dateien in Java, die mit dem Befehl openssl mit AES verschlüsselt wurden? .

19
Codo

Wenn Sie auf einem System verschlüsseln und auf einem anderen entschlüsseln, sind Sie den Systemvorgaben ausgeliefert. Wenn Systemeinstellungen nicht übereinstimmen (und dies häufig nicht tun), schlägt Ihre Entschlüsselung fehl.

Alles muss Byte für Byte auf beiden Seiten gleich sein. Das bedeutet effektiv, auf beiden Seiten alles anzugeben, anstatt sich auf Standardwerte zu verlassen. Sie können Standardeinstellungen nur verwenden, wenn Sie auf beiden Seiten dasselbe System verwenden. Auch dann ist es besser, genau anzugeben.

Schlüssel, IV, Verschlüsselungsmodus, Auffüllen und Umwandlung von Zeichenfolge in Byte müssen an beiden Enden gleich sein. Es lohnt sich insbesondere zu prüfen, dass die Schlüsselbytes gleich sind. Wenn Sie eine Schlüsselableitungsfunktion (KDF) verwenden, um Ihren Schlüssel zu generieren, müssen alle Parameter für diesen Schlüssel gleich sein und daher genau angegeben werden.

Ihre "ungültige AES-Schlüssellänge" kann auf ein Problem beim Generieren Ihres Schlüssels hinweisen. Sie verwenden getBytes(). Das ist wahrscheinlich ein Fehler. Sie müssen angeben, welche Art von Byte Sie erhalten: ANSI, UTF-8, EBCDIC oder was auch immer. Die Standardannahme für die Umwandlung von Zeichenfolge in Byte ist die wahrscheinliche Ursache dieses Problems. Geben Sie die Konvertierung an, die explizit an beiden Enden verwendet werden soll. Auf diese Weise können Sie sicher sein, dass sie übereinstimmen.

Crypto ist so konzipiert, dass Fehler auftreten, wenn die Parameter für Verschlüsselung und Entschlüsselung nicht genau übereinstimmen. Zum Beispiel führt ein Unterschied von einem Bit beim Schlüssel dazu, dass der Schlüssel fehlschlägt.

2
rossum