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

如何在Java中解密Node.js加密的字符串?

问题拆解与解决方案

嘿,你的问题核心在于Node.js里用的crypto.createCipher是个已经被废弃的旧API,它的底层行为和你想的完全不一样——它根本没直接把encKey当AES密钥用,而是通过OpenSSL的EVP_BytesToKey算法自动生成了AES密钥和CBC模式必须的初始化向量(IV)。而你的Java代码不仅用错了加密模式(ECB而非CBC),还没实现对应的密钥/IV派生逻辑,自然解密失败。

一、先搞懂Node.js侧的加密逻辑

当你调用createCipher('aes-256-cbc', encKey)时,它偷偷做了这些事:

  • 用MD5哈希(默认算法),以你传的encKey作为密码,空盐值,迭代1次,生成32字节的AES-256密钥和16字节的CBC IV。
  • 加密用的是标准AES-256-CBC,填充方式是PKCS#7(和Java的PKCS5Padding其实是一回事,因为AES块大小是16字节,两者兼容)。

二、Java侧的修正步骤

你需要在Java里实现对应的EVP_BytesToKey逻辑,然后改用CBC模式解密,具体如下:

1. 实现EVP_BytesToKey派生密钥和IV

这个方法会严格按照Node.js的逻辑生成对应的密钥和IV:

private static void deriveKeyAndIV(String password, byte[] keyBytes, byte[] ivBytes) throws Exception {
    MessageDigest md5 = MessageDigest.getInstance("MD5");
    byte[] passwordBytes = password.getBytes("UTF-8");
    byte[] combined = new byte[0];
    byte[] currentDigest = new byte[0];

    // EVP_BytesToKey核心逻辑:不断哈希直到凑够密钥+IV的总长度
    while (combined.length < keyBytes.length + ivBytes.length) {
        // 拼接上一次的摘要(第一次为空)和原始密码
        byte[] temp = new byte[currentDigest.length + passwordBytes.length];
        System.arraycopy(currentDigest, 0, temp, 0, currentDigest.length);
        System.arraycopy(passwordBytes, 0, temp, currentDigest.length, passwordBytes.length);
        
        currentDigest = md5.digest(temp);
        // 把新生成的摘要追加到结果集合中
        byte[] newCombined = new byte[combined.length + currentDigest.length];
        System.arraycopy(combined, 0, newCombined, 0, combined.length);
        System.arraycopy(currentDigest, 0, newCombined, combined.length, currentDigest.length);
        combined = newCombined;
    }

    // 拆分出密钥和IV
    System.arraycopy(combined, 0, keyBytes, 0, keyBytes.length);
    System.arraycopy(combined, keyBytes.length, ivBytes, 0, ivBytes.length);
}

2. 修正后的解密方法

注意:你之前的Hex解码逻辑错了!应该直接把Hex字符串解码成字节数组,而不是转成String再调用getBytes()(这会导致编码混乱,破坏密文结构)。

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import org.apache.commons.codec.binary.Hex; // 确保引入commons-codec依赖

private static final String encKey = "FOO";

public static String decrypt(String encryptedHexStr) throws Exception {
    // 1. 把Hex格式的密文解码成原始字节数组
    byte[] encryptedBytes = Hex.decodeHex(encryptedHexStr.toCharArray());
    
    // 2. 派生32字节的AES-256密钥和16字节的CBC IV
    byte[] key = new byte[32];
    byte[] iv = new byte[16];
    deriveKeyAndIV(encKey, key, iv);
    
    // 3. 初始化CBC模式的解密器
    SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
    IvParameterSpec ivSpec = new IvParameterSpec(iv);
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
    
    // 4. 解密并返回明文
    byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
    return new String(decryptedBytes, "UTF-8");
}

三、重要建议:升级Node.js加密代码

createCipher和createDecipher已经被Node.js官方标记为废弃,推荐改用**createCipheriv和createDecipheriv**,手动指定密钥和IV——这样不仅更安全(可以用随机IV,避免重复IV带来的安全风险),还能大幅降低跨语言兼容的难度。

比如升级后的Node.js代码可以写成这样:

const crypto = require('crypto');
// 注意:这里要用32字节的密钥(AES-256要求),可以从密码派生或者随机生成后存储
const encKey = Buffer.from('your-32-byte-secret-key-here', 'utf8');

function encrypt(str) {
    // 生成随机IV(每次加密都不一样,提高安全性)
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv('aes-256-cbc', encKey, iv);
    let crypted = cipher.update(str, 'utf8', 'hex');
    crypted += cipher.final('hex');
    // 把IV和密文一起返回(比如用冒号分隔,解密时拆分即可)
    return `${iv.toString('hex')}:${crypted}`;
}

function decrypt(str) {
    const [ivHex, cryptedHex] = str.split(':');
    const iv = Buffer.from(ivHex, 'hex');
    const decipher = crypto.createDecipheriv('aes-256-cbc', encKey, iv);
    let decrypted = decipher.update(cryptedHex, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    return decrypted;
}

对应的Java代码只需要按约定拆分IV和密文,直接用指定的密钥和IV解密就行,再也不用处理复杂的派生逻辑了。

内容的提问来源于stack exchange,提问作者RP31

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:51:37