CryptoJs 的解密方法 returns 一个空字符串

CryptoJs's decrypt method returns an empty string

我正在尝试 encrypt/decrypt 使用 AES256,使用 Java 进行加密,使用 CryptoJS 进行解密。 加密Java中测试工作正常但是[=45中的解密方法=]脚本返回一个空字符串。请注意,为了测试 JavaScript,我在 tmp 文件中打印了数据、IV 和盐的值,然后在 JS 中进行了硬编码。 (注意:文件格式为:data (byte[] base64) , Iv(string base64) and salt(string base64) )。

这是java中的代码:

public byte[] encrypt(String plainText) throws Exception {        
    //get salt
    salt = generateSalt();      
    byte[] saltBytes = salt.getBytes("UTF-8");

    // Derive the key
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(), 
            saltBytes, 
            pswdIterations, 
            keySize
            );

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    //encrypt the message
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret);
    AlgorithmParameters params = cipher.getParameters();
    ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
    byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
    return Base64.encode(encryptedTextBytes);
}

下面Java脚本中的解密代码有什么问题?

// the password that user provides
var userPass = document.getElementById("password").value; 
console.log("user pass : " + userPass);
// hash contains 5 bytes
var hashedPass = CryptoJS.SHA1(userPass);
console.log("hashed pass : " + hashedPass.toString(CryptoJS.enc.Base64) + " | array length " + hashedPass.words.length + " | " + typeof(hashedPass));
// use only 4 bytes (128 bits) from the hashed pass
// (same as used in java when encrypting)
/////////////////////////var hashed4bytes =  CryptoJS.lib.WordArray.create(hashedPass.words.slice(0,4));
//console.log( "hashed4bytes encoded 64 = " + hashed4bytes.toString(CryptoJS.enc.Base64));

// get the encrypted msg 
var encMsg64 = document.getElementById("themessage").innerHTML;
encMsg64 = encMsg64.toString( CryptoJS.enc.Base64);
//var encMsg = CryptoJS.enc.Base64.parse(encMsg64);
var salt =CryptoJS.enc.Base64.parse("EAWnOgxUDuvhWqrSUsugq1umMpI=");
var iv =CryptoJS.enc.Base64.parse("xWpmXNbmbFjmWBUajuWYXQ==");
//var salt = "EAWnOgxUDuvhWqrSUsugq1umMpI=";
//var iv = "xWpmXNbmbFjmWBUajuWYXQ==";
 console.log('salt  '+ salt );
 console.log('iv  '+ iv );


 var key = CryptoJS.PBKDF2(hashedPass, salt, { keySize: 256/32, iterations: 1000 });
console.log( 'key '+ key);

var decText = '';
var ok = true;
try {
    debugger;
    var decMsg = CryptoJS.AES.decrypt( encMsg64, key, {
        iv:iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
        } );
    console.log( "decryptedData = " + decMsg );

    // convert to UTF8 string
    decText = decMsg.toString( CryptoJS.enc.Utf8 );
    console.log( "decryptedText = " + decText );

    if (decText == '') {
        ok = false;
    }   
}
catch (e) {
    //console.log("Error when decrypting: " + e.message)
    ok = false;
}

修改后的问题仍然存在 这是更改后的完整代码

JAVA

public class AES256EncryptionServiceBean implements EncryptionService {

    private static final Logger LOGGER = LoggerFactory
            .getLogger(AES256EncryptionServiceBean.class);
    private String salt = null; //get bytes out of UTF-8 for decryption
    private static final int PSWDITERATIONS = 1000;//65536;
    private static final int KEYSIZE = 256;
    private static final String AES_ALGO = "AES";
    private static final String SHA1_ALGO = "PBKDF2WithHmacSHA1";
    private static final String AES_CBC_PKCS5_TRANSFORM = "AES/CBC/PKCS5Padding";
    private byte[] Iv;

    /**
     * Encrypts the data with AES-256 algorithm Encrypted data will be encoded
     * with base64 algorithm and the returned. Initial vector is being used
     * during encryption along with CBC encryption mode.
     * 
     * output format: [algo indicator(1char)][Initialization vector()][salt()][encoded data(variable size)]
     */
    @Override
    public byte[] encrypt(String password, byte[] data) throws PibException {
        byte[] encodedData = null;
        try {
            byte[] encryptedData = encryptCBC256Bits(password, data);
            encodedData = Base64.encodeBase64(encryptedData);
            /*String finalStr=null;
            String algo256 = "2";
            String datastr = Base64.encodeBase64String(encryptedData);
            String ivstr = new String(Iv);
            finalStr = algo256 +ivstr+salt+datastr;

            encodedData = finalStr.getBytes();
             */
        } catch (Exception e) {
            throw ExceptionFactory.createPibException(
                    MessageCodes.PIB_ENCRYPTION_FAILED, e, LOGGER);
        }
        return encodedData;
    }

    /**
     * Encrypts the input data with AES CBC transformation using 256 bits (32
     * bytes) Key is generated based on the provided password and random salt.
     * Salt is the extra bits added to the password to ensure every key is
     * unique SHA1 hashing is also participate in key generation.
     * 
     * @throws PibException
     * 
     */
    private byte[] encryptCBC256Bits(String password, byte[] data)
            throws PibException {

        salt = generateSalt();
        byte[] saltBytes = salt.getBytes(StandardCharsets.UTF_8);
        byte[] encryptedTextBytes = null;

        // Derive the key

        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance(SHA1_ALGO);
            // Password based key specification
            PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes,
                    PSWDITERATIONS, KEYSIZE);
            SecretKey secretKey = factory.generateSecret(spec);
            SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(),
                    AES_ALGO);

            // encrypt the data
            Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_TRANSFORM);
            // SecureRandom random = new SecureRandom();
            // byte[] ivTemp = new byte[16];
            // random.nextBytes(ivTemp);
            cipher.init(Cipher.ENCRYPT_MODE, secret);
            AlgorithmParameters params = cipher.getParameters();
            Iv = params.getParameterSpec(IvParameterSpec.class).getIV();
            encryptedTextBytes = cipher.doFinal(data);

        } catch (NoSuchAlgorithmException | InvalidKeySpecException
                | NoSuchPaddingException | InvalidKeyException
                | InvalidParameterSpecException | IllegalBlockSizeException
                | BadPaddingException e) {
            throw ExceptionFactory.createPibException(
                    MessageCodes.PIB_ENCRYPTION_FAILED, e, LOGGER);
        }

        return encryptedTextBytes;
    }

    private String generateSalt() {

        SecureRandom random = new SecureRandom();
        byte bytes[] = new byte[20];
        random.nextBytes(bytes);
        String s = new String(bytes);
        return s;

    }

    public String getSalt() {
        return salt;
    }

    public byte[] getIv() {
        return Iv;
    }

}

Java脚本

function decryptMsg256() {

                // the password that user provides
                var userPass = document.getElementById("password").value; 
                console.log("user pass : " + userPass);


                // get the encrypted msg 
                var encMsg64 = document.getElementById("themessage").innerHTML;
                var encMsg = CryptoJS.enc.Base64.parse(encMsg64);
                var salt =CryptoJS.enc.Utf8.parse("?E€O5?…°®I^y??O:n");
                var iv =CryptoJS.enc.Utf8.parse("S;Ui?¨=ENzI—$");

                 console.log('salt  '+ salt );
                 console.log('iv  '+ iv );


                 var key = CryptoJS.PBKDF2("password", salt, { keySize: 256/32, iterations: 1000 });
                console.log( 'key '+ key);

                var decText = '';
                var ok = true;
                try {
                    debugger;
                    var decMsg = CryptoJS.AES.decrypt( encMsg, key, {
                        iv:iv,
                        mode: CryptoJS.mode.CBC,
                        padding: CryptoJS.pad.Pkcs7
                        } );
                    console.log( "decryptedData = " + decMsg );

                    // convert to UTF8 string
                    decText = decMsg.toString( CryptoJS.enc.Utf8 );
                    console.log( "decryptedText = " + decText );

                    if (decText == '') {
                        ok = false;
                    }   
                }
                catch (e) {
                    //console.log("Error when decrypting: " + e.message)
                    ok = false;
                }

我不明白哪里不对请帮忙 CipherText、Salt和Iv检索如下:

public void testEncryption_WriteToFile() throws Exception {

        byte[] data = IOUtils.toByteArray(this.getClass().getClassLoader()
                .getResourceAsStream(SOME_FILE_NAME));

        byte[] encryptedData = this.encryptionService.encrypt(PASSWORD, data);
        byte[] initial_vector = ((AES256EncryptionServiceBean) encryptionService)
        .getIv();
        String salt = ((AES256EncryptionServiceBean) encryptionService)
                .getSalt();


        IOUtils.write(encryptedData, new FileOutputStream(
                "C:\Temp\data.encrypted"));
        /*IOUtils.write(new String(encryptedData), new FileOutputStream(
                "C:\Temp\data[byte32string].encrypted"));
        */
        IOUtils.write(Base64.encodeBase64String(salt.getBytes(StandardCharsets.UTF_8)), new FileOutputStream(
                "C:\Temp\salt.encrypted"));
        /*IOUtils.write(salt.getBytes(StandardCharsets.UTF_8), new FileOutputStream(
                "C:\Temp\salt.encrypted"));
        */
        IOUtils.write(Base64.encodeBase64String(initial_vector), new FileOutputStream(
                "C:\Temp\iv.encrypted"));
        /*IOUtils.write(initial_vector, new FileOutputStream(
                "C:\Temp\iv.encrypted"));*/


    }

CryptoJS.PBKDF2 默认使用 SHA1。因此,只要提供相同的密码、salt、密钥大小和迭代次数,它就会生成相同的密钥。问题在于,在 JavaScript 中,您还使用 SHA1 散列密码。不要那样做,将密码直接传递给 PBKDF2,就像在 Java.

中那样

第二个问题是密文在尝试解密时应该是CryptoJS的原生格式。由于您从 Java 获得了 base 64 编码的密文,因此您必须对其进行解码。取消注释行:

var encMsg = CryptoJS.enc.Base64.parse(encMsg64);

并且不要encMsg64 = encMsg64.toString( CryptoJS.enc.Base64);,因为这将再次对已经编码的密文进行编码。


对于更新后的代码,您不能简单地将密钥和盐打印为字符串并期望它在 JavaScript 中工作。这些是 byte[] 是有原因的。它们包含不可打印的字符,当您尝试在 JavaScript 中解析这些字符时,这些字符将会丢失。您必须将要从 Java 传输到 JavaScript 的所有 byte[] 值编码为 Base64,然后在 JavaScript.

中解码它们