【问题标题】:Spring 4 Encryptors vs cryptojsSpring 4 加密器与 cryptojs
【发布时间】:2015-04-28 18:41:31
【问题描述】:

我正在以这种方式使用 Spring 4 Encryptors 类处理加密:

String salt = KeyGenerators.string().generateKey();
TextEncryptor textEncryptor = Encryptors.text("MY_SECRET_KEY", salt);
textEncryptor.encrypt(json);

在客户端 javascript 中,我尝试使用 CryptoJS 进行解密

var uncrypted = CryptoJS.AES.decrypt(serverEncrypted, "MY_SECRET_KEY");
$("#encrypted-data").val(uncrypted);

我猜我需要还原十六进制编码部分,但我不明白需要做什么。

我尝试在解密之前进行解析,但没有任何好处。

CryptoJS.enc.Hex.parse(serverEncrypted)

spring 文档说 text 方法将使用“标准”加密方法中的十六进制值进行加密,这是使用 PKCS #5 的 PBKDF2 的 256 位 AES

[搜索进度]

这是 Spring 创建密钥的方式:

PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 1024, 256);
SecretKey secretKey = newSecretKey("PBKDF2WithHmacSHA1", keySpec);
SecretKeySpec secretKey = new SecretKeySpec(secretKey.getEncoded(), "AES");

还有加密:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));

iv 是一个 8 字节的安全随机数组,被添加到加密数据之前。

【问题讨论】:

  • 注意所有的JS字符串都是unicode...
  • 那么这意味着什么?
  • 这意味着加密非unicode字符串的结果将不同于以非unicode格式编码相同字符串的结果。我注意到在使用非 js 后端和 js 前端时,这是一个很大的兼容性问题。也许你的java字符串已经是Unicode,在这种情况下我浪费了你的灰质......
  • 我应该使用不同的编码方法吗?
  • 你需要尝试几个,直到你找到一个在每一端产生相同输出的。我曾经为 AES 找到了 php+js 的匹配项,但我没有寻找 java。我确信外面的东西可以做到这一点,如果你可以强制使用 unicode 或转换为一组 char 代码,那么你所拥有的可能会工作。

标签: java javascript spring encryption cryptojs


【解决方案1】:

经过一番研究,我找到了一个解决方案,如何结合 Spring 4 Encryptors 和 CryptoJS。 Spring 使用文档中提供的 PKCS #5 的 PBKDF2 在其加密器中使用 256 位 AES - Spring Crypto docs,IV 大小等于 16 字节。在 CryptoJS 中创建代码时,该信息至关重要。要记住的另一件事是在加密/解密一条消息的整个过程中使用相同的盐和iv。有了这些信息,我们就可以开始编码了(在这个例子中,我在 CryptoJS 中加密消息并在 Spring 中解密): CryptoJS

const keySize = 256;
const ivSize = 128;
const iterations = 1024;

function encrypt(msg, pass) {
    const salt = CryptoJS.lib.WordArray.random(128 / 8);

    const key = CryptoJS.PBKDF2(pass, salt, {
        keySize: keySize / 32,
        iterations: iterations
    });

    const iv = CryptoJS.lib.WordArray.random(ivSize / 8);

    const encrypted = CryptoJS.AES.encrypt(msg, key, {
        iv: iv,
        padding: CryptoJS.pad.Pkcs7,
        mode: CryptoJS.mode.CBC

    });

    // salt, iv will be hex 32 in length
    // append them to the ciphertext for use  in decryption
    return salt.toString() + iv.toString() + encrypted.ciphertext.toString();
}

如您所见,所有加密的结果是 salt + iv + encryptedMessage 的串联。同样重要的是,在 Spring 代码中,我发现密钥的创建长度为 256,迭代次数为 1024:

PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), Hex.decode(salt), 1024, 256);

现在我们可以将这个准备好的结果带到我们的 Spring 代码中并解密:

private static String decrypt(String encryptedData, String keyStr) {
        final String salt = encryptedData.substring(0, 32);
        final String encryptedPart = encryptedData.substring(32);
        final TextEncryptor textEncryptor = Encryptors.text(keyStr, salt);
        return textEncryptor.decrypt(encryptedPart);
}

Spring AES 加密器将切断 IV 的一部分,并自行将其用于解密过程。当然,一切都必须是 Hex 编码,但这就是 Spring TextEncrytpors 的工作原理。

相反的过程(在 Spring 中加密和在 CryptoJS 中解密)几乎相同。在 CryptoJS 中,我们必须提取 saltiv

const keySize = 256;

function decrypt (encryptedMessage, pass) {
  var salt = CryptoJS.enc.Hex.parse(encryptedMessage.substr(0, 32));
  var iv = CryptoJS.enc.Hex.parse(encryptedMessage.substr(32, 32))
  var encrypted = encryptedMessage.substring(64);

  var key = CryptoJS.PBKDF2(pass, salt, {
      keySize: keySize / 32,
      iterations: iterations
    });

  var decrypted = CryptoJS.AES.decrypt(encrypted, key, { 
    iv: iv, 
    padding: CryptoJS.pad.Pkcs7,
    mode: CryptoJS.mode.CBC

  })
  return decrypted;
}

我希望它会帮助别人:)

干杯!

【讨论】:

    猜你喜欢
    • 2020-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-13
    相关资源
    最近更新 更多