You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CryptoJS与Java/Scala AES加密结果不一致的问题及适配咨询

CryptoJS与Java/Scala AES加密结果不一致的问题及适配咨询

我来帮你梳理下问题的核心原因,以及对应的解决方案:

一、CryptoJS.AES.encrypt()的默认设置

CryptoJS的AES加密默认遵循OpenSSL的标准规范,和你当前Java/Scala实现的逻辑差异很大,具体默认参数如下:

  • 密钥派生方式:默认采用OpenSSL的EVP_BytesToKey算法,不是直接对传入的secret做哈希。它会生成一个随机8字节的盐,以secret为密码,通过MD5哈希(默认迭代1次)来生成AES密钥(256位对应32字节)和IV(16字节)。
  • 加密模式:默认使用CBC模式
  • 填充方式:默认是PKCS7填充(和Java中的PKCS5Padding在AES场景下完全等价,因为AES的块大小是16字节,两者的填充规则一致)
  • 输出格式:默认生成OpenSSL兼容的Base64字符串,结构为:Salted__(固定8字节标识) + 8字节随机盐 + 加密后的密文,而非你代码中直接拼接的IV+密文。

二、修改Java/Scala代码匹配CryptoJS的加密格式

要让你的Java/Scala实现和CryptoJS兼容,需要实现EVP_BytesToKey算法来生成密钥和IV,并按照OpenSSL的格式拼接最终内容。以下是修改后的Scala代码:

import javax.crypto.{Cipher, SecretKeySpec}
import javax.crypto.spec.IvParameterSpec
import java.security.MessageDigest
import java.util.Base64

def encryptWithCryptoJSFormat(data: String, secret: String): String = {
    // 生成CryptoJS默认的8字节随机盐
    val salt = new Array[Byte](8)
    new java.security.SecureRandom().nextBytes(salt)
    
    // 实现OpenSSL EVP_BytesToKey算法,生成AES-256密钥和IV
    val passwordBytes = secret.getBytes("UTF-8")
    var keyAndIv = Array[Byte]()
    var lastHash = Array[Byte]()
    // 需生成32字节密钥 + 16字节IV,总计48字节
    while (keyAndIv.length < 48) {
        val digest = MessageDigest.getInstance("MD5")
        if (lastHash.nonEmpty) digest.update(lastHash)
        digest.update(passwordBytes)
        digest.update(salt)
        lastHash = digest.digest()
        keyAndIv = keyAndIv ++ lastHash
    }
    val keyBytes = keyAndIv.take(32) // 提取AES-256密钥
    val ivBytes = keyAndIv.slice(32, 48) // 提取IV
    
    // 初始化CBC模式加密器,PKCS5Padding等价于CryptoJS的PKCS7填充
    val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
    val keySpec = new SecretKeySpec(keyBytes, "AES")
    val ivSpec = new IvParameterSpec(ivBytes)
    cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
    
    // 执行加密
    val encryptedBytes = cipher.doFinal(data.getBytes("UTF-8"))
    
    // 按照OpenSSL格式拼接内容:Salted__标识 + 盐 + 密文
    val saltedPrefix = "Salted__".getBytes("UTF-8")
    val combined = saltedPrefix ++ salt ++ encryptedBytes
    
    // Base64编码输出,和CryptoJS默认输出格式一致
    Base64.getEncoder.encodeToString(combined)
}

代码修改说明:

  1. 生成了8字节随机盐,和CryptoJS的默认盐长度匹配
  2. 用EVP_BytesToKey算法生成密钥和IV,替代了你原来直接SHA256哈希secret的逻辑
  3. 最终拼接的内容遵循OpenSSL标准格式,这样服务端就能和CryptoJS的加密结果一样被正常解密了

备注:内容来源于stack exchange,提问作者Michael

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 08:40:31