CryptoJs 的 decrypt 方法返回一个空字符串

CryptoJs's decrypt method returns an empty string

本文关键字:一个 字符串 decrypt 方法 返回 CryptoJs      更新时间:2023-09-26

我正在尝试使用AES256进行加密/解密,使用Java进行加密,使用CryptoJS进行解密。在 Java 中测试的加密工作正常,但 JavaScript 中的解密方法返回一个空字符串。请注意,为了测试JavaScript,我在tmp文件中打印出数据,IV和盐的值,然后用JS硬编码。(注意:文件中的格式为:data (byte[] base64) , iv(string base64) 和 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);
}

下面JavaScript中的解密代码有什么问题?

// 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;
}

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

.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;
    }
}

爪哇语

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

我不明白出了什么问题,请帮忙密文,盐和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。因此,只要提供相同的密码、盐、密钥大小和迭代计数,它就会产生相同的密钥。问题是在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 解码它们。