CryptoJs的decrypt方法返回空字符串

mar*_*390 5 javascript encryption sha1 aes pbkdf2

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

\n\n

这是java中的代码:

\n\n
public byte[] encrypt(String plainText) throws Exception {        \n    //get salt\n    salt = generateSalt();      \n    byte[] saltBytes = salt.getBytes("UTF-8");\n\n    // Derive the key\n    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");\n    PBEKeySpec spec = new PBEKeySpec(\n            password.toCharArray(), \n            saltBytes, \n            pswdIterations, \n            keySize\n            );\n\n    SecretKey secretKey = factory.generateSecret(spec);\n    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");\n\n    //encrypt the message\n    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");\n    cipher.init(Cipher.ENCRYPT_MODE, secret);\n    AlgorithmParameters params = cipher.getParameters();\n    ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();\n    byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));\n    return Base64.encode(encryptedTextBytes);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
// the password that user provides\nvar userPass = document.getElementById("password").value; \nconsole.log("user pass : " + userPass);\n// hash contains 5 bytes\nvar hashedPass = CryptoJS.SHA1(userPass);\nconsole.log("hashed pass : " + hashedPass.toString(CryptoJS.enc.Base64) + " | array length " + hashedPass.words.length + " | " + typeof(hashedPass));\n// use only 4 bytes (128 bits) from the hashed pass\n// (same as used in java when encrypting)\n/////////////////////////var hashed4bytes =  CryptoJS.lib.WordArray.create(hashedPass.words.slice(0,4));\n//console.log( "hashed4bytes encoded 64 = " + hashed4bytes.toString(CryptoJS.enc.Base64));\n\n// get the encrypted msg \nvar encMsg64 = document.getElementById("themessage").innerHTML;\nencMsg64 = encMsg64.toString( CryptoJS.enc.Base64);\n//var encMsg = CryptoJS.enc.Base64.parse(encMsg64);\nvar salt =CryptoJS.enc.Base64.parse("EAWnOgxUDuvhWqrSUsugq1umMpI=");\nvar iv =CryptoJS.enc.Base64.parse("xWpmXNbmbFjmWBUajuWYXQ==");\n//var salt = "EAWnOgxUDuvhWqrSUsugq1umMpI=";\n//var iv = "xWpmXNbmbFjmWBUajuWYXQ==";\n console.log(\'salt  \'+ salt );\n console.log(\'iv  \'+ iv );\n\n\n var key = CryptoJS.PBKDF2(hashedPass, salt, { keySize: 256/32, iterations: 1000 });\nconsole.log( \'key \'+ key);\n\nvar decText = \'\';\nvar ok = true;\ntry {\n    debugger;\n    var decMsg = CryptoJS.AES.decrypt( encMsg64, key, {\n        iv:iv,\n        mode: CryptoJS.mode.CBC,\n        padding: CryptoJS.pad.Pkcs7\n        } );\n    console.log( "decryptedData = " + decMsg );\n\n    // convert to UTF8 string\n    decText = decMsg.toString( CryptoJS.enc.Utf8 );\n    console.log( "decryptedText = " + decText );\n\n    if (decText == \'\') {\n        ok = false;\n    }   \n}\ncatch (e) {\n    //console.log("Error when decrypting: " + e.message)\n    ok = false;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

在 mafe 后,更改后的问题仍然存在\n这里是更改后的完整代码

\n\n

爪哇

\n\n
public class AES256EncryptionServiceBean implements EncryptionService {\n\n    private static final Logger LOGGER = LoggerFactory\n            .getLogger(AES256EncryptionServiceBean.class);\n    private String salt = null; //get bytes out of UTF-8 for decryption\n    private static final int PSWDITERATIONS = 1000;//65536;\n    private static final int KEYSIZE = 256;\n    private static final String AES_ALGO = "AES";\n    private static final String SHA1_ALGO = "PBKDF2WithHmacSHA1";\n    private static final String AES_CBC_PKCS5_TRANSFORM = "AES/CBC/PKCS5Padding";\n    private byte[] Iv;\n\n    /**\n     * Encrypts the data with AES-256 algorithm Encrypted data will be encoded\n     * with base64 algorithm and the returned. Initial vector is being used\n     * during encryption along with CBC encryption mode.\n     * \n     * output format: [algo indicator(1char)][Initialization vector()][salt()][encoded data(variable size)]\n     */\n    @Override\n    public byte[] encrypt(String password, byte[] data) throws PibException {\n        byte[] encodedData = null;\n        try {\n            byte[] encryptedData = encryptCBC256Bits(password, data);\n            encodedData = Base64.encodeBase64(encryptedData);\n            /*String finalStr=null;\n            String algo256 = "2";\n            String datastr = Base64.encodeBase64String(encryptedData);\n            String ivstr = new String(Iv);\n            finalStr = algo256 +ivstr+salt+datastr;\n\n            encodedData = finalStr.getBytes();\n             */\n        } catch (Exception e) {\n            throw ExceptionFactory.createPibException(\n                    MessageCodes.PIB_ENCRYPTION_FAILED, e, LOGGER);\n        }\n        return encodedData;\n    }\n\n    /**\n     * Encrypts the input data with AES CBC transformation using 256 bits (32\n     * bytes) Key is generated based on the provided password and random salt.\n     * Salt is the extra bits added to the password to ensure every key is\n     * unique SHA1 hashing is also participate in key generation.\n     * \n     * @throws PibException\n     * \n     */\n    private byte[] encryptCBC256Bits(String password, byte[] data)\n            throws PibException {\n\n        salt = generateSalt();\n        byte[] saltBytes = salt.getBytes(StandardCharsets.UTF_8);\n        byte[] encryptedTextBytes = null;\n\n        // Derive the key\n\n        try {\n            SecretKeyFactory factory = SecretKeyFactory.getInstance(SHA1_ALGO);\n            // Password based key specification\n            PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes,\n                    PSWDITERATIONS, KEYSIZE);\n            SecretKey secretKey = factory.generateSecret(spec);\n            SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(),\n                    AES_ALGO);\n\n            // encrypt the data\n            Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_TRANSFORM);\n            // SecureRandom random = new SecureRandom();\n            // byte[] ivTemp = new byte[16];\n            // random.nextBytes(ivTemp);\n            cipher.init(Cipher.ENCRYPT_MODE, secret);\n            AlgorithmParameters params = cipher.getParameters();\n            Iv = params.getParameterSpec(IvParameterSpec.class).getIV();\n            encryptedTextBytes = cipher.doFinal(data);\n\n        } catch (NoSuchAlgorithmException | InvalidKeySpecException\n                | NoSuchPaddingException | InvalidKeyException\n                | InvalidParameterSpecException | IllegalBlockSizeException\n                | BadPaddingException e) {\n            throw ExceptionFactory.createPibException(\n                    MessageCodes.PIB_ENCRYPTION_FAILED, e, LOGGER);\n        }\n\n        return encryptedTextBytes;\n    }\n\n    private String generateSalt() {\n\n        SecureRandom random = new SecureRandom();\n        byte bytes[] = new byte[20];\n        random.nextBytes(bytes);\n        String s = new String(bytes);\n        return s;\n\n    }\n\n    public String getSalt() {\n        return salt;\n    }\n\n    public byte[] getIv() {\n        return Iv;\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

JavaScript

\n\n
function decryptMsg256() {\n\n                // the password that user provides\n                var userPass = document.getElementById("password").value; \n                console.log("user pass : " + userPass);\n\n\n                // get the encrypted msg \n                var encMsg64 = document.getElementById("themessage").innerHTML;\n                var encMsg = CryptoJS.enc.Base64.parse(encMsg64);\n                var salt =CryptoJS.enc.Utf8.parse("?E\xe2\x82\xacO5?\xe2\x80\xa6\xc2\xb0\xc2\xaeI^y??O:n");\n                var iv =CryptoJS.enc.Utf8.parse("S;Ui?\xc2\xa8=ENzI\xe2\x80\x94$");\n\n                 console.log(\'salt  \'+ salt );\n                 console.log(\'iv  \'+ iv );\n\n\n                 var key = CryptoJS.PBKDF2("password", salt, { keySize: 256/32, iterations: 1000 });\n                console.log( \'key \'+ key);\n\n                var decText = \'\';\n                var ok = true;\n                try {\n                    debugger;\n                    var decMsg = CryptoJS.AES.decrypt( encMsg, key, {\n                        iv:iv,\n                        mode: CryptoJS.mode.CBC,\n                        padding: CryptoJS.pad.Pkcs7\n                        } );\n                    console.log( "decryptedData = " + decMsg );\n\n                    // convert to UTF8 string\n                    decText = decMsg.toString( CryptoJS.enc.Utf8 );\n                    console.log( "decryptedText = " + decText );\n\n                    if (decText == \'\') {\n                        ok = false;\n                    }   \n                }\n                catch (e) {\n                    //console.log("Error when decrypting: " + e.message)\n                    ok = false;\n                }\n
Run Code Online (Sandbox Code Playgroud)\n\n

我不明白出了什么问题,请帮忙\nCipherText、Salt 和 Iv 的检索如下:

\n\n
public void testEncryption_WriteToFile() throws Exception {\n\n        byte[] data = IOUtils.toByteArray(this.getClass().getClassLoader()\n                .getResourceAsStream(SOME_FILE_NAME));\n\n        byte[] encryptedData = this.encryptionService.encrypt(PASSWORD, data);\n        byte[] initial_vector = ((AES256EncryptionServiceBean) encryptionService)\n        .getIv();\n        String salt = ((AES256EncryptionServiceBean) encryptionService)\n                .getSalt();\n\n\n        IOUtils.write(encryptedData, new FileOutputStream(\n                "C:\\\\Temp\\\\data.encrypted"));\n        /*IOUtils.write(new String(encryptedData), new FileOutputStream(\n                "C:\\\\Temp\\\\data[byte32string].encrypted"));\n        */\n        IOUtils.write(Base64.encodeBase64String(salt.getBytes(StandardCharsets.UTF_8)), new FileOutputStream(\n                "C:\\\\Temp\\\\salt.encrypted"));\n        /*IOUtils.write(salt.getBytes(StandardCharsets.UTF_8), new FileOutputStream(\n                "C:\\\\Temp\\\\salt.encrypted"));\n        */\n        IOUtils.write(Base64.encodeBase64String(initial_vector), new FileOutputStream(\n                "C:\\\\Temp\\\\iv.encrypted"));\n        /*IOUtils.write(initial_vector, new FileOutputStream(\n                "C:\\\\Temp\\\\iv.encrypted"));*/\n\n\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

Art*_* B. 5

CryptoJS.PBKDF2默认使用 SHA1。因此,只要提供相同的密码、salt、密钥大小和迭代次数,就会产生相同的密钥。问题在于,在 JavaScript 中,您还使用 SHA1 对密码进行哈希处理。不要这样做,而是像在 Java 中那样将密码直接传递到 PBKDF2 中。

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

var encMsg = CryptoJS.enc.Base64.parse(encMsg64);
Run Code Online (Sandbox Code Playgroud)

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


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