使用CryptoJS解码(Base64)并解密(AES/CBC/PKCS5PADDING) - React

8
我正在开发一个基于React作为前端和Spring MVC作为后端的Web应用程序。我需要将一些用户信息存储在浏览器的本地存储中。我不想将这些信息以明文形式存储在本地存储中。因此,我考虑在服务器端使用AES加密,并将这些数据推送回JS端。为此,我需要客户端解密框架。我发现crypto-js非常适合所有这些事情。但是我无法理解客户端解密和解码方面的问题。
首先,我要解释我的Spring端加密代码,它完全没有问题:
public class EncryptDecrypt {

        private static final String SECRET_KEY_1 = "ssdkF$HUy2A#D%kd";
        private static final String SECRET_KEY_2 = "weJiSEvR5yAC5ftB";

        private IvParameterSpec ivParameterSpec;
        private SecretKeySpec secretKeySpec;
        private Cipher cipher;

        public EncryptDecrypt() throws UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException {
            ivParameterSpec = new IvParameterSpec(SECRET_KEY_1.getBytes("UTF-8"));
            secretKeySpec = new SecretKeySpec(SECRET_KEY_2.getBytes("UTF-8"), "AES");
            cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        }

        public String encrypt(String toBeEncrypt) throws NoSuchPaddingException, NoSuchAlgorithmException, 
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] encrypted = cipher.doFinal(toBeEncrypt.getBytes());
            return Base64.encodeBase64String(encrypted);
        }
}

在客户端,我无法使用简单的方法解码和解密代码。这是我的客户端代码:

var CryptoJS = require("crypto-js");

var data = "Ggydx4oA1+SKBw+unA8BUUm2tnvkQbp1terdF2PEGFYSEZL/ye08op/0b0BauGtIl1dBIodrlKXo2de3MykYmocd3ctxFtIIki01V+M8XeQj6B384o0G+H7NpVx5tCJjPDvdqVRObtxCTqu3r8QRzYTNcMM5bRhbYxCYl8/NRyPQJnmcJDlRBeVOoJiQNA7Qd5UJD/mNivoyMUfYGV7/DlpylQWWwEAHVdgcb865i8jnf3vqURehAXYoaD6Bgodi1EM4H007uv0o6NEOk3H4jQ==";

var key = "weJiSEvR5yAC5ftB";

// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = "ssdkF$HUy2A#D%kd";
var crypttext = rawData.substring(16);

console.log(rawData);

// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
  { ciphertext: CryptoJS.enc.Base64.parse(crypttext) },
  key,
  { iv: iv }
);

console.log(plaintextArray);

console.log(CryptoJS.enc.Base64.stringify(plaintextArray));

var decryptedData = JSON.parse(CryptoJS.enc.Base64.stringify(plaintextArray).toString(CryptoJS.enc.Utf8));

console.log(decryptedData);

P.S: 我已经将JSON发送到客户端,因此最后要对其进行解析。我对加密和解密是新手。我真的不知道我的客户端代码应该是什么样子的。请帮忙。


@rhashimoto,你能帮我解决这个问题吗? - samkit shah
  1. 在执行 rawData.substring(16); 之前,您需要进行 base64 解码。
  2. Spring 端可能会在密文前添加 IV。比较 Spring 加密的输入和输出大小。如果它们相同,则未添加 IV,整个数据是密文。如果相反,则解码后可以获得密文字节。
- kelalaka
@kelalaka,这就是我所做的。在rawData.substring(16)之前,我已经解码了字符串。 - samkit shah
你检查了rawData的前16个字节是否为IV,并且是否与你的IV匹配了吗? - kelalaka
1个回答

6

CryptoJS 中不应将字符串作为密钥传递。 在这种情况下,它将此字符串视为密码,并通过使用 PBKDF 从密码生成密钥。 下面是一个工作示例:

var data = "Ggydx4oA1+SKBw+unA8BUUm2tnvkQbp1terdF2PEGFYSEZL/ye08op/0b0BauGtIl1dBIodrlKXo2de3MykYmocd3ctxFtIIki01V+M8XeQj6B384o0G+H7NpVx5tCJjPDvdqVRObtxCTqu3r8QRzYTNcMM5bRhbYxCYl8/NRyPQJnmcJDlRBeVOoJiQNA7Qd5UJD/mNivoyMUfYGV7/DlpylQWWwEAHVdgcb865i8jnf3vqURehAXYoaD6Bgodi1EM4H007uv0o6NEOk3H4jQ==";
var rawData = CryptoJS.enc.Base64.parse(data);
var key = CryptoJS.enc.Latin1.parse("weJiSEvR5yAC5ftB");
var iv = CryptoJS.enc.Latin1.parse("ssdkF$HUy2A#D%kd");
var plaintextData = CryptoJS.AES.decrypt(
    { ciphertext: rawData },
    key,
    { iv: iv });
var plaintext = plaintextData.toString(CryptoJS.enc.Latin1);
console.log(plaintext);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"></script>

顺便提一下,您不应该每次都使用相同的IV。在这种情况下,您会错过IV和CBC模式的基本目的。您的整体安全性将变得等同于ECB模式。


非常感谢你。你是救星。是的,我会始终使用不同的IV,并且也会使用盐。这只是示例数据。再次非常感谢你。 - samkit shah
很棒的答案!如果你想支持非拉丁字符,可以使用 CryptoJS.enc.Utf8 - mhrabiee
@Zergatul 你知道在 CryptoJS 中如何使用 GCM 作为 mode,并使用 PKCS5Padding 作为 padding 进行 encrypt() 吗? - Compaq LE2202x
1
@CompaqLE2202x GCM 不使用填充。GCM 的作用类似于流密码,这意味着它可以处理任何消息长度。填充是块密码所必需的,当消息长度必须是某个值的倍数时。从技术上讲,在使用 GCM 之前可以使用填充,但这没有任何意义。 - Zergatul
@Zergatul 注意到 GCM 不使用填充。那么mode是什么,有任何想法吗?我在这里看到(https://cryptojs.gitbook.io/docs/#block-modes-and-padding)`GMS`没有列出。谢谢! - Compaq LE2202x
1
@CompaqLE2202x 这意味着 CryptoJS 不支持 GCM - Zergatul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接