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

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

问题描述

我有一个加密的AES-256字符串从CryptoJS与一个密码。我需要解密它在Java,但不能弄清楚如何做到。您似乎需要IV,密钥和盐来解密,如 CryptoJS mainpage ,加密的数据已经包含了所有的数据,CryptoJS可以用加密的输入来解析它们。



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

解决方案

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

  CryptoJS.AES.encrypt(message,passphrase)

方法。对于CryptoJS生成一个新的盐,并使用这个盐结合密钥导出密钥和IV(我写了一个派生函数)。

在生成密文之后,会生成一个
href =https://github.com/CryptoStore/crypto-js/tree/3.1.2/src/cipher-core.js#L576 =nofollow>特殊OpenSSL格式器用于编码包括所使用的盐的密文:

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

您可能需要解码Base64编码字符串以获取字节数组。然后你可以检查头是否匹配:

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

之后,您可以从中获取盐和密文:

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

完整的代码:

  public static void main(String [] args)throws UnsupportedEncodingException,GeneralSecurityException {
String ciphertext =U2FsdGVkX1 + 0m / gle / XQX1shjnpveUrl1fO3oOlurPMlTks6 + oQlEPfOrucihzEz;
String plaintext =这是一些示例明文;
String 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.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(restoredPlaintextBytes);

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

//迭代
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
}

/ **
*从http://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 data [i / 2] =(byte)((Character.digit(s.charAt )<<< 4)
+ Character.digit(s.charAt(i + 1),16)
}
返回数据;
}

JavaScript代码:



< pre class =lang-js prettyprint-override> var pw =这是一个非常强的密码;
var pt =这是一些示例明文;
var encrypted = CryptoJS.AES.encrypt(pt,pw);
encrypted.toString(); // U2FsdGVkX1 + 0m / gle / XQX1shjnpveUrl1fO3oOlurPMlTks6 + oQlEPfOrucihzEz
encrypted.salt.toString(); // b49bf8257bf5d05f
encrypted.ciphertext.toString(); // 5b218e7a6f794ae5d5f3b7a0e96eacf3254e4b3afa842510f7ceaee722873133


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.

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

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

You can 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 get the recover the salt and ciphertext from it:

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

Derive the key+IV:

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

And decrypt the ciphertext:

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

Full complete code:

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 http://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 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

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

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