简体   繁体   English

如何在 Java 中实现 CryptoJS.AES.encrypt 函数?

[英]How to implement CryptoJS.AES.encrypt function in Java?

I'm trying to implement following code from crypto-js in java for encryption我正在尝试在java中实现以下来自crypto-js的代码以进行加密

let toEncrypt= "my data";
cryptoJs.AES.encrypt(toEncrypt,"apasswordblabla").toString();

here is my implementation (AES/CBC/PKCS7Padding):这是我的实现(AES/CBC/PKCS7Padding):

public String encrypt(Map<String,Object> param){
try {
            String toEncrypt= objectMapper.writeValueAsString(param);
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] saltData = Arrays.copyOfRange(stringToEncrypt.getBytes(StandardCharsets.UTF_8), 8, 16);
            final byte[][] keyAndIV = generateKeyAndIV(32, 16, 1, saltData, "apasswordblabla".getBytes(StandardCharsets.UTF_8), md5);
            SecretKeySpec skeySpec = new SecretKeySpec(keyAndIV[0], "AES");
            IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
            Cipher cipher;
            cipher = Cipher.getInstance("AES/CBC/PKCS7Padding",BouncyCastleProvider.PROVIDER_NAME);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec,iv);
            byte[] base64Encoded = Base64.getEncoder().encode(cipher.doFinal(toEncrypt.getBytes(StandardCharsets.UTF_8)));
            return new String(base64Encoded);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException
                | BadPaddingException | InvalidKeyException
                | JsonProcessingException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
            e.printStackTrace();
            

        }
}

Implementation of generateIV(int ,int ,int ,byte[] , byte[], MessageDigest): generateIV(int ,int ,int ,byte[] , byte[], MessageDigest) 的实现:

  • This method is equivalent to OpenSSL's EVP_BytesToKey function, as I see in cryptojs source code ( crypto-js@4.1.1 on file cipher-core.js line 658 ), the default formatter for AES is the OpenSSLFormatter此方法等效于OpenSSL 的 EVP_BytesToKey函数,正如我在 cryptojs 源代码(文件 cipher-core.js 行 658 上的 crypto-js@4.1.1 )中看到的,AES 的默认格式化程序是 OpenSSLFormatter
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);
        }
    }

and then I try to decrypt it Here using然后我尝试在这里使用解密它

let toDecrypt="[MY ENCRYPTED DATA IN BASE64]"
let decrypted = cryptoJs.AES.decrypt(toDecrypt, "apasswordblabla").toString(cryptoJs.enc.Utf8).toString(); 
console.log(decrypted);

And its always fail to decrypt, with error "Malformed utf-8 data" .而且它总是无法解密,并出现错误"Malformed utf-8 data" Hence, my java implementation is wrong因此,我的 java 实现是错误的

What did I got wrong here?我在这里做错了什么? or if there is any ready library to solve this issues please suggest或者如果有任何现成的库来解决这个问题,请建议

The format used by OpenSSL and CryptoJS turns out to be base64("Salted__" + <salt 8 bytes> + <encrypted data>) OpenSSL 和 CryptoJS 使用的格式原来是base64("Salted__" + <salt 8 bytes> + <encrypted data>)

Here's the code that produces result in such format (reusing generateKeyAndIV method from above without any modifications):这是以这种格式产生结果的代码(从上面重用generateKeyAndIV方法,无需任何修改):

    public static void main(String args[]) {
        Security.addProvider(new BouncyCastleProvider());
        System.out.println(encrypt());
    }

    public static String encrypt() {
        try {
            String stringToEncrypt = "Hello world 12345";
            String password = "apasswordblabla";
            SecureRandom sr = new SecureRandom();
            byte[] salt = new byte[8];
            sr.nextBytes(salt);
            final byte[][] keyAndIV = generateKeyAndIV(32, 16, 1, salt, password.getBytes(StandardCharsets.UTF_8),
                    MessageDigest.getInstance("MD5"));
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", BouncyCastleProvider.PROVIDER_NAME);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyAndIV[0], "AES"), new IvParameterSpec(keyAndIV[1]));
            byte[] encryptedData = cipher.doFinal(stringToEncrypt.getBytes(StandardCharsets.UTF_8));
            byte[] prefixAndSaltAndEncryptedData = new byte[16 + encryptedData.length];
            // Copy prefix (0-th to 7-th bytes)
            System.arraycopy("Salted__".getBytes(StandardCharsets.UTF_8), 0, prefixAndSaltAndEncryptedData, 0, 8);
            // Copy salt (8-th to 15-th bytes)
            System.arraycopy(salt, 0, prefixAndSaltAndEncryptedData, 8, 8);
            // Copy encrypted data (16-th byte and onwards)
            System.arraycopy(encryptedData, 0, prefixAndSaltAndEncryptedData, 16, encryptedData.length);
            return Base64.getEncoder().encodeToString(prefixAndSaltAndEncryptedData);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

Example result produced is: U2FsdGVkX1/O8jV2bfcM/06DM106oLAzdf7z66/JakGNefts4MftzXquopkxPaDo产生的示例结果是: U2FsdGVkX1/O8jV2bfcM/06DM106oLAzdf7z66/JakGNefts4MftzXquopkxPaDo

Decoding in JSFiddle using this code:使用此代码在 JSFiddle 中解码:

console.log(CryptoJS.AES.decrypt("U2FsdGVkX1/O8jV2bfcM/06DM106oLAzdf7z66/JakGNefts4MftzXquopkxPaDo", "apasswordblabla").toString(CryptoJS.enc.Utf8));

Produced desired output in browser console: Hello world 12345在浏览器控制台中产生所需的输出: Hello world 12345

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM