web-dev-qa-db-ja.com

CryptoJS AES暗号化とJava AES復号化

暗号AES暗号化について2日間多くの記事を読んでいるので、私はこれを聞いているだけです。そして、私はそれを手に入れようと思ったとき、私はまったく手に入れていないことに気付きました。

この投稿は私の問題に最も近いものです。私はまったく同じ問題を抱えていますが、未回答です。

CryptoJS AES暗号化とJava AES復号化値の不一致

いろいろとやってみましたが、うまくいきませんでした。

最初のオフ

私はすでに暗号化された文字列を取得しています(彼らがそれをどのようにやっているかを見るためのコードしか手に入れていません)ので、暗号化の方法を変更することはオプションではありません。それが、すべての同様の質問が私にとってそれほど有用ではない理由です。

私は秘密鍵にアクセスでき、それを修正することができます(したがって、長さの調整は必要な場合のオプションです)。

暗号化はCryptoJSで行われ、暗号化された文字列をGETパラメーターとして送信します。

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

私はCryptoJSを使用してjavascriptでこれを簡単に解読できます:

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

しかし、セキュリティ上の理由から、Javascriptでこれを行いたくないので、Javaでこれを復号化しようとしています:

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

自分が何をしているのかわからないうちに、base64デコードを試みて、IVと読んだものをたくさん追加しましたが、どれもうまくいきませんでした。

しかし、私がやっていることを理解し始めた後、上記の簡単なスクリプトを書いて、同じエラーを投稿で受け取りました:Invalid AES key length

ここからどこへ行くべきかわかりません。これについて多くを読んだ後、解決策はハッシュまたはパディングであるように見えますが、暗号化方式を制御できないため、秘密をハッシュしたりパディングしたりすることはできません。

しかし、私が言ったように、特定の長さに一致するように秘密鍵を変更することができ、それを変更しようとしましたが、ここで暗闇で撮影しているので、これが解決策であるかどうかはわかりません。

だから、私の質問は基本的に、暗号化された文字列(最初のスクリプトのようなjavascriptで)と秘密鍵を取得した場合、解読する方法がありますit(in Java)?もしそうなら、それを行う方法は?

11
Lauro182

CryptoJSは、OpenSSLと同じキー派生関数と同じ形式を実装して、IVを暗号化されたデータに入れます。したがって、OpenSSLでエンコードされたデータを処理するすべてのJavaコードが適用されます。

次のJavascriptコードを考えます:

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

暗号テキストを取得します。

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

Java側では、

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

結果は次のとおりです。

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

それが私たちが始めたテキストです。また、絵文字、アクセント、ウムラウトも機能します。

GenerateKeyAndIVは、OpenSSLのキー派生関数を再実装するヘルパー関数ですEVP_BytesToKeyhttps://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);
    }
}

Java Cryptography Extension(JCE) nlimited Strength Jurisdiction Policy 。をインストールする必要があることに注意してください。 :

Java.security.InvalidKeyException: Illegal key size

更新

Ola Bini's Java code of EVP_BytesToKey、これは私の回答の最初のバージョンで使用したもので、より慣用的で理解しやすいJavaコード(上記参照)。

Java AESを使用してopensslコマンドで暗号化されたJava $ ===)でファイルを復号化する方法 も参照してください。

24
Codo

あるシステムで暗号化して別のシステムで復号化するとき、システムのデフォルトに翻弄されます。システムのデフォルトが一致しない場合(そしてしばしば一致しない場合)、復号化は失敗します。

すべてがbyte for byte両側で同じでなければなりません。つまり、デフォルトに依存するのではなく、両側ですべてを指定することを意味します。両端で同じシステムを使用している場合にのみ、デフォルトを使用できます。それでも、正確に指定することをお勧めします。

キー、IV、暗号化モード、パディング、および文字列からバイトへの変換はすべて、両端で同じである必要があります。キーバイトが同じであることを確認することは特に価値があります。キー派生関数(KDF)を使用してキーを生成する場合、そのためのすべてのパラメーターは同じである必要があるため、正確に指定する必要があります。

「無効なAESキーの長さ」は、キーの生成に関する問題を示している可能性があります。 getBytes()を使用します。これはおそらくエラーです。取得するバイトの種類(ANSI、UTF-8、EBCDICなど)を指定する必要があります。文字列からバイトへの変換のデフォルトの仮定が、この問題の原因である可能性があります。両端で明示的に使用される変換を指定します。そうすれば、それらが一致することを確認できます。

暗号化と復号化のパラメーターが完全に一致しない場合、暗号化は失敗するように設計されています。たとえば、キーに1ビットの違いがあっても、キーは失敗します。

2
rossum