首页 > 解决方案 > Java中的AES加密工作,但JavaScript中的解密不起作用?

问题描述

我正在尝试加密价值服务器端,然后从浏览器端解密。这是我用来加密的 Java 代码,它工作正常:

package aes;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
 
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class AesEncryption {

    private static SecretKeySpec secretKey;
    private static byte[] key;
 
    public static void setKey(String myKey) {
        MessageDigest sha = null;
        try {
            key = myKey.getBytes("UTF-8");
            sha = MessageDigest.getInstance("SHA-1");
            key = sha.digest(key);
            key = Arrays.copyOf(key, 16); 
            secretKey = new SecretKeySpec(key, "AES");
        } 
        catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } 
        catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public static String encrypt(String strToEncrypt, String secret) {
        try {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
        } 
        catch (Exception e) 
        {
            System.out.println("Error while encrypting: " + e.toString());
        }
        return null;
    }

然后,我调用了一个 JavaScript 方法来解密加密值:

  aesDecrypt(encryptedValue) {
    console.log("TESTING ENCRYPTED VALUE : " , encryptedValue)
    var bytes = CryptoJS.AES.decrypt(encryptedValue, secretAesKey, {
      mode: CryptoJS.mode.ECB,
      padding: CryptoJS.pad.Pkcs7
    })
    console.log("TESTING BYTES : " , bytes)
    var originalValue = bytes.toString(CryptoJS.enc.Utf8);
    console.log("TESTING ORIGINAL VALUE : " , originalValue)
    return originalValue
  }

在记录解密的每个步骤时,在尝试打印原始值时,它只是空白。我知道 encryptedValue 必须以与最初加密相同的方式转换回字节,然后必须用 UTF-8 编码才能返回原始字符串。我还能错过什么导致这种情况?

标签: javascriptjavaencryptionaes

解决方案


CryptoJS 可以通过 SHA-1 导出密钥,例如:

var passphrase = '<your passphrase>';
var sha1Hash = CryptoJS.SHA1(passphrase);                                           // Create SHA1 hash
var secretAesKey = CryptoJS.lib.WordArray.create(sha1Hash.words.slice(0, 16 / 4));  // Use the first 4 words (= 16 bytes) of the hash

使用 Java 代码生成的密文进行测试:

function aesDecrypt(encryptedValue) {
    console.log("TESTING ENCRYPTED VALUE : " , encryptedValue);
    var bytes = CryptoJS.AES.decrypt(
        encryptedValue, 
        secretAesKey, 
        {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        }
    );
    console.log("TESTING BYTES : " , bytes);
    var originalValue = bytes.toString(CryptoJS.enc.Utf8);
    console.log("TESTING ORIGINAL VALUE : " , originalValue);
    return originalValue;
  }

var passphrase = 'my secret passphrase';
var sha1Hash = CryptoJS.SHA1(passphrase)
var secretAesKey = CryptoJS.lib.WordArray.create(sha1Hash.words.slice(0, 16 / 4));
var ciphertext = 'hjC0d3sEGJ/3SBTPyV5RrbiIi5jCfhj9T2ZbeWNyBHqmCrVaKLYN94ouiMOz8d4x';
aesDecrypt(ciphertext);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

它正确地将密文解密为原始明文。


请注意,正如评论中所提到的,ECB 模式是不安全的,而且通过摘要从密码派生的密钥也是不安全的(即使它不会被破坏)。相反,应该使用可靠的密钥派生函数,如 Argon2 或 PBKDF2。


推荐阅读