Node.js 加密密钥和 iv 匹配 java SecretKeySpec / IvParameterSpec
Node.js crypto key and iv to match java SecretKeySpec / IvParameterSpec
我正在尝试将 Java(简单)加密算法移植到 Node JS。我需要能够从 Java 方面 decrypt/encrypt 东西 encrypted/decrypted。
我卡在了最开始,密码的初始化。
在 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 buffer,但是,我不知道如何从头开始计算它们:
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的匹配算法字符串是什么?
假设您拥有与 Java 代码中相同的密码字符串,您可以在节点中创建这样的密钥缓冲区:
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 模式,因此您必须在节点中执行相同的操作。请注意,根据您的 "password" 长度 select 加密字符串时,您必须 select 正确的密钥大小:
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(连接)然后拆分它们。现在,你只需要在密文前面加盐即可。
身份验证!
根据您的系统,您可能容易受到填充 oracle 攻击等攻击。最好的防御方法是验证密文。因此,您可以使用具有强 MAC 的加密然后 MAC 方案,例如 HMAC-SHA256,或者使用经过身份验证的操作模式,例如 GCM 或 EAX。 Java 和 node 都支持 GCM,但是需要做更多的工作。
我正在尝试将 Java(简单)加密算法移植到 Node JS。我需要能够从 Java 方面 decrypt/encrypt 东西 encrypted/decrypted。
我卡在了最开始,密码的初始化。
在 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 buffer,但是,我不知道如何从头开始计算它们:
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的匹配算法字符串是什么?
假设您拥有与 Java 代码中相同的密码字符串,您可以在节点中创建这样的密钥缓冲区:
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 模式,因此您必须在节点中执行相同的操作。请注意,根据您的 "password" 长度 select 加密字符串时,您必须 select 正确的密钥大小:
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(连接)然后拆分它们。现在,你只需要在密文前面加盐即可。
身份验证!
根据您的系统,您可能容易受到填充 oracle 攻击等攻击。最好的防御方法是验证密文。因此,您可以使用具有强 MAC 的加密然后 MAC 方案,例如 HMAC-SHA256,或者使用经过身份验证的操作模式,例如 GCM 或 EAX。 Java 和 node 都支持 GCM,但是需要做更多的工作。