无法使用 CryptoJS (Javascript) 复制加密 AES-CBC (Java)

问题描述 投票:0回答:1

目标:使用 CryptoJS (Javascript) 复制 Java-AES-CBC 加密/解密

为什么?: 使用 CryptoJS 创建加密字符串(Base64 或 HEX)并使用 Java 进行解码(不同的服务器环境)

问题:我无法让 CryptoJS 部分生成与 Java 相同的 Base64(或 HEX)编码字符串。 因此,在当前的代码状态下,无法将加密/解密交给不同的各方。

我的 Java 代码输出如下:

SECRET_KEY: 431.396
SALT: d413321c765f563c8288ce281ae6808d
originalString: test123_XXX
encryptedString: suBMmUzhQMtuAboORtvr6g==
encryptedStringHex: 7375424d6d557a68514d747541626f4f5274767236673d3d
decryptedString: test123_XXX

我的 Javascript 代码输出以下内容

[Log] SECRET_KEY: 431.396
[Log] SALT: d413321c765f563c8288ce281ae6808d
[Log] originalString: test123_XXX
[Log] Encrypted: l+R6aYgrQI2+RAIH+X/iJw==
[Log] encryptedHex: 97E47A69882B408DBE440207F97FE227
[Log] Decrypted: test123_XXX

这是我正在使用的完整Java代码:

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Base64;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import java.security.*;
import org.apache.commons.codec.binary.Hex;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.spec.KeySpec;


public class AES {
    
    public static String encrypt(String strToEncrypt, String SECRET_KEY, String SALT) {
        try {
            byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
            IvParameterSpec ivspec = new IvParameterSpec(iv);
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
            KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT.getBytes(), 65536, 256);
            SecretKey tmp = factory.generateSecret(spec);
            SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
            return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            System.out.println("Error while encrypting: " + e.toString());
        }
        return null;
    }
    
    public static String decrypt(String strToDecrypt, String SECRET_KEY, String SALT) {
        try {
            byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
            IvParameterSpec ivspec = new IvParameterSpec(iv);
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
            KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT.getBytes(), 65536, 256);
            SecretKey tmp = factory.generateSecret(spec);
            SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
            return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
        } catch (Exception e) {
            System.out.println("Error while decrypting: " + e.toString());
        }
        return null;
    }
    
    ///
    public static void main(String args[]) {
      
        String originalString = "test123_XXX";
        String SECRET_KEY = "431.396";
        String SALT = "d413321c765f563c8288ce281ae6808d";
         
        String encryptedString = AES.encrypt(originalString, SECRET_KEY, SALT);
        String encryptedStringHex = Hex.encodeHexString(encryptedString.getBytes());
        String decryptedString = AES.decrypt(encryptedString, SECRET_KEY, SALT);
    
        System.out.println("SECRET_KEY: " + SECRET_KEY);
        System.out.println("SALT: " + SALT);
        System.out.println("originalString: " + originalString);
        System.out.println("encryptedString: " + encryptedString);
        System.out.println("encryptedStringHex: " + encryptedStringHex);
        System.out.println("decryptedString: " + decryptedString);
    }
}

这是我正在使用的完整Javascript代码:

var SECRET_KEY = "431.396";
var SALT = "d413321c765f563c8288ce281ae6808d";
var originalString = "test123_XXX";

var iv = '0000000000000000';
iv = CryptoJS.enc.Hex.parse(iv);

var iterations = 65536;
var keySize = 256;

function encrypt (msg) {
  
  var key = CryptoJS.PBKDF2(SECRET_KEY, SALT, {
      keySize: keySize/32,
      iterations: iterations
    });

  var encrypted = CryptoJS.AES.encrypt(msg, key, { 
    iv: iv, 
    padding: CryptoJS.pad.Pkcs7,
    mode: CryptoJS.mode.CBC,
    hasher: CryptoJS.algo.SHA256
  });
  
  return encrypted;
}

function decrypt (encrypted) {
  
  var key = CryptoJS.PBKDF2(SECRET_KEY, SALT, {
      keySize: keySize/32,
      iterations: iterations
    });

  var decrypted = CryptoJS.AES.decrypt(encrypted, key, { 
    iv: iv, 
    padding: CryptoJS.pad.Pkcs7,
    mode: CryptoJS.mode.CBC,
    hasher: CryptoJS.algo.SHA256
  });
  
  return decrypted;
}

var encrypted = encrypt(originalString);
var encryptedHex = encrypted.ciphertext.toString(CryptoJS.enc.Hex).toUpperCase();
var decrypted = decrypt(encrypted);

console.log("SECRET_KEY: "+ SECRET_KEY);
console.log("SALT: "+ SALT);
console.log("originalString: "+ originalString);
console.log("Encrypted: "+ encrypted);
console.log("encryptedHex: "+ encryptedHex);
console.log("Decrypted: "+ decrypted.toString(CryptoJS.enc.Utf8) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/aes.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/enc-hex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/md5.min.js"></script>

java encryption aes cryptojs
1个回答
0
投票

哈希算法是密钥导出函数的一部分。因此,在 CryptoJS 代码中,哈希算法必须在密钥派生函数中指定,而不是在

encrypt()
/
decrypt()
调用中指定。通过此更改,CryptoJS 代码生成与 Java 代码相同的密文(比较 Base64 编码的密文):

var SECRET_KEY = "431.396";
var SALT = "d413321c765f563c8288ce281ae6808d";
var originalString = "test123_XXX";

var iv = '00000000000000000000000000000000'; // 16 bytes correspons to 32 hex digits
iv = CryptoJS.enc.Hex.parse(iv);

var iterations = 65536;
var keySize = 256;

function encrypt (msg) {
  
  var key = CryptoJS.PBKDF2(SECRET_KEY, SALT, {
      keySize: keySize/32,
      iterations: iterations,
      hasher: CryptoJS.algo.SHA256 // the hash algorithm is part of the key derivation
    });

  var encrypted = CryptoJS.AES.encrypt(msg, key, { 
    iv: iv, 
    padding: CryptoJS.pad.Pkcs7, // default
    mode: CryptoJS.mode.CBC // default
  });
  
  return encrypted;
}

function decrypt (encrypted) {
  
  var key = CryptoJS.PBKDF2(SECRET_KEY, SALT, {
      keySize: keySize/32,
      iterations: iterations,
      hasher: CryptoJS.algo.SHA256 // the hash algorithm is part of the key derivation
    });

  var decrypted = CryptoJS.AES.decrypt(encrypted, key, { 
    iv: iv, 
    padding: CryptoJS.pad.Pkcs7, // default
    mode: CryptoJS.mode.CBC // default
  });
  
  return decrypted;
}

var encrypted = encrypt(originalString);
var encryptedHex = encrypted.ciphertext.toString(CryptoJS.enc.Hex).toUpperCase();
var decrypted = decrypt(encrypted);

console.log("SECRET_KEY: "+ SECRET_KEY);
console.log("SALT: "+ SALT);
console.log("originalString: "+ originalString);
console.log("Encrypted: "+ encrypted);
console.log("encryptedHex: "+ encryptedHex);
console.log("Decrypted: "+ decrypted.toString(CryptoJS.enc.Utf8) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>


十六进制编码的密文仍然不同,因为在 Java 代码中 Base64 编码的密文是十六进制编码的。这种双重编码是不必要的,应该避免。例如。对 Java 代码进行以下更改:

String encryptedStringHex = Hex.encodeHexString(Base64.getDecoder().decode(encryptedString.getBytes(StandardCharsets.US_ASCII)));

这些值也匹配(大写/小写除外)。


另请注意,AES 的 IV 大小为 16 个字节,这意味着十六进制编码值由 32 个十六进制数字组成。在 CryptoJS 代码中,您仅使用 16 个十六进制数字,这仅起作用,因为应用了零 IV,并且 CryptoJS 隐式地将太短的 IV 用 0x00 值填充到正确的长度。


关于安全性:静态盐和IV是漏洞。正确的方法是在加密过程中生成随机 IV 和 salt,并将两者与密文一起传递到解密方,通常是串联的(IV 和 salt 不是秘密)。

© www.soinside.com 2019 - 2024. All rights reserved.