Node.js 加密密钥和 iv 匹配 java SecretKeySpec / IvParameterSpec

Posted

技术标签:

【中文标题】Node.js 加密密钥和 iv 匹配 java SecretKeySpec / IvParameterSpec【英文标题】:Node.js crypto key and iv to match java SecretKeySpec / IvParameterSpec 【发布时间】:2015-08-13 19:50:23 【问题描述】:

我正在尝试将 Java(简单)加密算法移植到 Node JS。我需要能够解密/加密从 Java 端加密/解密的内容。

我被困在最开始,密码的初始化。

在 Java 中,我使用 SecretKeySpec 获取密钥,使用 IvParameterSpec 获取初始化向量:

public CryptStuff(String password) throws zillion_exceptions 
    if (password==null) throw new InvalidKeyException("No encryption password is set!");
    key = new SecretKeySpec(password.getBytes("UTF-8"), "AES");
    cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    ivSpec=new IvParameterSpec(new byte[cipher.getBlockSize()]);
    cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);

NodeJS 需要一个 Key Buffer 和一个 IV 缓冲区,但是,我不知道如何从头开始计算它们:

var mCrypto = require('crypto'),
    key=[0,0,0,0,0,0,.......],
    iv=[0,0,0,0,0,.........];

function init (password) 

    // generate key from password
    // generate IV from blocksize?

    var aesCipher = mCrypto.createCipheriv("aes-????????", (new Buffer(key)), (new Buffer(iv)));
    .
    .
    .

另外,AES/CBC/PKCS5Padding 的匹配算法字符串是什么?

【问题讨论】:

【参考方案1】:

假设你有和 Java 代码中相同的密码字符串,你可以在 node 中创建一个这样的密钥缓冲区:

var key = new Buffer(password, "utf8");

由于您在 Java 中使用零填充 IV(不好!),因此这是节点中的等效代码:

var iv = new Buffer(16); // 16 byte buffer with random data
iv.fill(0); // fill with zeros

由于您在 Java 中使用 CBC 模式,因此您必须在 node.js 中执行相同的操作。请注意,在选择密码字符串时,您必须根据您的“密码”长度选择正确的密钥大小:

var aesCipher = mCrypto.createCipheriv("aes-128-cbc", key, iv);
// or
var aesCipher = mCrypto.createCipheriv("aes-192-cbc", key, iv);
// or
var aesCipher = mCrypto.createCipheriv("aes-256-cbc", key, iv);

节点将自动应用 PKCS#7 填充,这与 AES 的 PKCS#5 填充相同。

密码不是钥匙!

密码的长度通常不适合用作密钥(AES 的有效长度为 16 字节、24 字节和 32 字节),并且它仅包含可打印的字符,这可能使攻击者更容易破解强制键。

您需要从密码创建密钥是密钥派生功能。流行的是 PBKDF2、bcrypt 和 scrypt(成本越来越高)。

随机 IV!

您确实应该为您生成的每个密文生成一个新的随机 IV。如果您使用静态 IV,观察您的密文的攻击者可以确定您发送了相同甚至相似的消息。如果您使用随机 IV,那么密文差异很大,以至于攻击者无法确定两个不同的密文是否是从相同的明文创建的。这称为语义安全。

随机 IV 本身不必保密,因此您可以轻松地将其添加到密文中,并在解密前将其切掉。

您甚至可以将其与密钥派生函数 (KDF) 结合使用。只需为 KDF 生成一个随机盐。 KDF 通常能够导出可变数量的输出字节,因此只需让它导出密钥 || IV(连接)然后拆分它们。现在,您只需要在密文前面加上盐。

认证!

根据您的系统,您可能容易受到诸如填充预言攻击之类的攻击。对此的最佳防御是对密文进行身份验证。因此,您可以使用具有强 MAC(例如 HMAC-SHA256)或经过身份验证的操作模式(例如 GCM 或 EAX)的 encrypt-then-MAC 方案。 Java 和 node 都支持 GCM,但需要做更多的工作。

【讨论】:

感谢您的详尽回答,我非常重视您的建议,特别是随机 IV,老实说,我不知道我使用的是 000000 IV,这听起来绝对是错误的。关于身份验证,我已经做了一个 HMAC 并将其存储到文本中,所以当我解码字符串时,我检查存储的 hmac 是否与计算的 HMAC 匹配。我不知道这是否符合你所说的身份验证? 您所描述的似乎是 MAC-then-encrypt 可能没问题,但有它自己的一系列问题。密码学家通常更喜欢先加密后 MAC:Should we MAC-then-encrypt or encrypt-then-MAC?

以上是关于Node.js 加密密钥和 iv 匹配 java SecretKeySpec / IvParameterSpec的主要内容,如果未能解决你的问题,请参考以下文章

AES 加密 - 密钥与 IV

什么是 openssl iv,为什么我需要密钥和 iv?

加密/解密:HMAC 标签在解密方法中不匹配

RSA Java 加密和 Node.js 解密不起作用

Node.js 内置模块crypto加密模块 AES

Crypto.js 用字节数组中的密钥和 iv(向量)解密