如何使用 Java 从 CryptoJS 解密加密的 AES-256 字符串? [英] How to decrypt an encrypted AES-256 string from CryptoJS using Java?

查看:83
本文介绍了如何使用 Java 从 CryptoJS 解密加密的 AES-256 字符串?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个来自 CryptoJS 的加密 AES-256 字符串,带有密码.我需要用 Java 解密它,但不知道怎么做.似乎您需要 IV、密钥和盐来解密,如 CryptoJS 主页,加密数据已经包含所有这些,CryptoJS 可以以某种方式从加密输入中解析它们.

I have an encrypted AES-256 string from CryptoJS with a passphrase. I need to decrypt it in Java but can't figure out how to do it. It seems that you need IV, key and salt to decrypt, and as in CryptoJS mainpage, the encrypted data already contains all of them, and CryptoJS can somehow parse them out of encrypted input.

有人知道怎么做吗?我见过很多关于 CryptoJS 的例子——Java 加密/解密,但大多数使用硬编码的 IV/key,或者只是将 IV/key 从 cryptoJS 端发送到 Java 端.我只有一个密码,就像这个网站所做的一样!

Anyone know how to do that? I've seen so much example about CryptoJS - Java encrypt/decrypt but most of them use hardcoded IV/key, or just send IV/key from cryptoJS side to Java side. All I have is a passphrase, just like what this site do!

推荐答案

当邮件以这种方式加密时:

When a message is encrypted in this way:

CryptoJS.AES.encrypt("message", "passphrase")

使用基于密码的方法.为此,CryptoJS 生成一个新的盐,并使用此盐和密码来派生密钥和 IV(我为 这个问题).
密文生成后一个特殊的OpenSSLformatter 用于对密文进行编码,包括使用的盐:

a password-based approach is used. For that CryptoJS generates a new salt and uses this salt in conjunction with the passphrase to derive the key and IV (I've recreated a derivation function for this question).
After the ciphertext is produced a special OpenSSL formatter is used to encode the ciphertext including the used salt:

var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext);

如果是,您需要解码 Base64 编码的字符串以获取字节数组.然后你可以检查标题是否匹配:

You need to decode the Base64 encoded string to get the byte array out if it. Then you can check if the header matches:

byte[] ctBytes = Base64.getDecoder().decode(ciphertext.getBytes("UTF-8"));
System.out.println("Is salted: " + new String(Arrays.copyOf(ctBytes, 8)).equals("Salted__"));

之后你可以从中恢复盐和密文:

After that you can recover the salt and ciphertext from it:

byte[] saltBytes = Arrays.copyOfRange(ctBytes, 8, 16);
byte[] ciphertextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.length);

导出密钥+IV:

byte[] key = new byte[keySize/8];
byte[] iv = new byte[ivSize/8];
EvpKDF(password.getBytes("UTF-8"), keySize, ivSize, saltBytes, key, iv);

并解密密文:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"),
        new IvParameterSpec(iv));
byte[] recoveredPlaintextBytes = cipher.doFinal(ciphertextBytes);
String recoveredPlaintext = new String(recoveredPlaintextBytes);

完整代码:

public static void main(String[] args) throws UnsupportedEncodingException, GeneralSecurityException {
    String ciphertext = "U2FsdGVkX1+0m/gle/XQX1shjnpveUrl1fO3oOlurPMlTks6+oQlEPfOrucihzEz";
    String plaintext = "This is some example plaintext";
    String password = "This is a very strong password";
    int keySize = 256;
    int ivSize = 128;

    // var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext);
    byte[] ctBytes = Base64.getDecoder().decode(ciphertext.getBytes("UTF-8"));
    System.out.println("Is salted: " + Arrays.equals(Arrays.copyOf(ctBytes, 8), new byte[]{0x53, 0x61, 0x6c, 0x74, 0x65, 0x64, 0x5f, 0x5f}));
    System.out.println("Is salted: " + new String(Arrays.copyOf(ctBytes, 8)).equals("Salted__"));

    byte[] saltBytes = Arrays.copyOfRange(ctBytes, 8, 16);
    System.out.println("Salt matches: " + Arrays.equals(saltBytes, hexStringToByteArray("b49bf8257bf5d05f")));

    byte[] ciphertextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.length);
    System.out.println("CT matches: " + Arrays.equals(ciphertextBytes, hexStringToByteArray("5b218e7a6f794ae5d5f3b7a0e96eacf3254e4b3afa842510f7ceaee722873133")));

    byte[] key = new byte[keySize/8];
    byte[] iv = new byte[ivSize/8];
    EvpKDF(password.getBytes("UTF-8"), keySize, ivSize, saltBytes, key, iv);

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
    byte[] recoveredPlaintextBytes = cipher.doFinal(ciphertextBytes);
    String recoveredPlaintext = new String(recoveredPlaintextBytes);

    System.out.println("Recovered Plaintext: " + recoveredPlaintext);
    System.out.println("Expected Plaintext: " + plaintext);
}

public static byte[] EvpKDF(byte[] password, int keySize, int ivSize, byte[] salt, byte[] resultKey, byte[] resultIv) throws NoSuchAlgorithmException {
    return EvpKDF(password, keySize, ivSize, salt, 1, "MD5", resultKey, resultIv);
}

public static byte[] EvpKDF(byte[] password, int keySize, int ivSize, byte[] salt, int iterations, String hashAlgorithm, byte[] resultKey, byte[] resultIv) throws NoSuchAlgorithmException {
    keySize = keySize / 32;
    ivSize = ivSize / 32;
    int targetKeySize = keySize + ivSize;
    byte[] derivedBytes = new byte[targetKeySize * 4];
    int numberOfDerivedWords = 0;
    byte[] block = null;
    MessageDigest hasher = MessageDigest.getInstance(hashAlgorithm);
    while (numberOfDerivedWords < targetKeySize) {
        if (block != null) {
            hasher.update(block);
        }
        hasher.update(password);
        block = hasher.digest(salt);
        hasher.reset();

        // Iterations
        for (int i = 1; i < iterations; i++) {
            block = hasher.digest(block);
            hasher.reset();
        }

        System.arraycopy(block, 0, derivedBytes, numberOfDerivedWords * 4,
                Math.min(block.length, (targetKeySize - numberOfDerivedWords) * 4));

        numberOfDerivedWords += block.length/4;
    }

    System.arraycopy(derivedBytes, 0, resultKey, 0, keySize * 4);
    System.arraycopy(derivedBytes, keySize * 4, resultIv, 0, ivSize * 4);

    return derivedBytes; // key + iv
}

/**
 * Copied from https://stackoverflow.com/a/140861
 * */
public static byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                + Character.digit(s.charAt(i+1), 16));
    }
    return data;
}

JavaScript 代码:

JavaScript code:

var pw = "This is a very strong password";
var pt = "This is some example plaintext";
var encrypted = CryptoJS.AES.encrypt(pt, pw);
encrypted.toString(); // U2FsdGVkX1+0m/gle/XQX1shjnpveUrl1fO3oOlurPMlTks6+oQlEPfOrucihzEz
encrypted.salt.toString(); // b49bf8257bf5d05f
encrypted.ciphertext.toString(); // 5b218e7a6f794ae5d5f3b7a0e96eacf3254e4b3afa842510f7ceaee722873133

这篇关于如何使用 Java 从 CryptoJS 解密加密的 AES-256 字符串?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆