Rijndael 加密 VB 到 Javascript

Rijndael encryption VB to Javascript

我有一个有趣的任务要为我的一位客户做... 我想在我为他构建的应用程序上使用此应用程序之一的帐户。 之前的应用程序在 VB 中写入并使用以下函数对密码数据进行加密:

Public Function Encrypt(ByVal plainText As String) As String
   Dim passPhrase As String = "minePassPhrase"
   Dim saltValue As String = "mySaltValue"
   Dim hashAlgorithm As String = "SHA1"
   Dim passwordIterations As Integer = 2
   Dim initVector As String = "@1B2c3D4e5F6g7H8"
   Dim keySize As Integer = 256
   Dim initVectorBytes As Byte() = Encoding.ASCII.GetBytes(initVector)
   Dim saltValueBytes As Byte() = Encoding.ASCII.GetBytes(saltValue)
   Dim plainTextBytes As Byte() = Encoding.UTF8.GetBytes(plainText)
   Dim password As New PasswordDeriveBytes(passPhrase, saltValueBytes, hashAlgorithm, passwordIterations)
   Dim keyBytes As Byte() = password.GetBytes(keySize \ 8)
   Dim symmetricKey As New RijndaelManaged()
   symmetricKey.Mode = CipherMode.CBC
   Dim encryptor As ICryptoTransform = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes)
   Dim memoryStream As New MemoryStream()
   Dim cryptoStream As New CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)
   cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length)
   cryptoStream.FlushFinalBlock()
   Dim cipherTextBytes As Byte() = memoryStream.ToArray()
   memoryStream.Close()
   cryptoStream.Close()
   Dim cipherText As String = Convert.ToBase64String(cipherTextBytes)
   Return cipherText
End Function

我不是节点密码专家,但经过一番研究,我得出了这个结论:

const crypto = require('crypto');

const encrypt = function (data) {

    const passPhrase = "minePassPhrase";
    const hashAlgorithm = "SHA1";
    const passwordIterations = 2;
    const keySize = 256;

    const initVector = "@1B2c3D4e5F6g7H8";
    const saltValue = "mySaltValue";

    const initVectorBytes = Buffer.from(initVector.substring(0, 32),"binary");
    const saltValueBytes = Buffer.from(saltValue.substring(0, 32),"binary");

    const derivedBytes = crypto.pbkdf2Sync(passPhrase, saltValueBytes, passwordIterations, keySize, hashAlgorithm);
    const key = derivedBytes.slice(0, Math.floor( keySize / 8 ));

    const cipher = crypto.createCipheriv('aes-256-cbc', key, initVectorBytes);
    const output = Buffer.concat([cipher.update(data, 'utf8'), cipher.final()]);

    return output.toString('base64');
}

为了帮助我,客户给了我一个用 VB 加密的代码编码的字符串。 使用 VB:“密码”=>“IMY4Jo6BkjDPYXR6DK+rhw==”

如果我尝试使用 javascript 函数: “密码”=>“dkofR4Us7O8+rdeIRsg78w==”

为了获得与 VB 函数相同的结果,我显然遗漏了一些东西。但是经过几天的研究,我一个人就失去了理智。你有什么想法吗?

问题是密钥派生函数不同导致的。在 VB 代码中 PasswordDeriveBytes is used, which is based on PBKDF1. PBKDF1 is defined in RFC 2898, section 5.1。生成的密钥的长度对应于使用的摘要的长度,即 SHA1 为 20 个字节。 MS 扩展了算法,因此也可以生成更长的密钥,例如32 个字节,如发布的代码中所示。然而,这意味着 MS 实现不再符合标准,因此并不总能找到跨平台的对应物。幸运的是,这在 NodeJS 的情况下是不同的,这里有一个实现,参见例如 jheys / derive-password-bytes .

以下节点实现再现了 VB 代码的结果:

const derp = require('derive-password-bytes');
const crypto = require('crypto');

const encrypt = function (data) {

    const passPhrase = "minePassPhrase";
    const hashAlgorithm = "SHA1";
    const passwordIterations = 2;
    const keySize = 256;
    const saltValue = "mySaltValue";
    const key = derp(passPhrase, saltValue, passwordIterations, hashAlgorithm, 32);

    const initVector = "@1B2c3D4e5F6g7H8";
    const initVectorBytes = Buffer.from(initVector, "binary");
    
    const cipher = crypto.createCipheriv('aes-256-cbc', key, initVectorBytes);
    const output = Buffer.concat([cipher.update(data, 'utf8'), cipher.final()]);

    return output.toString('base64');
}

const plaintext = "Password"
var encrypted = encrypt(plaintext);

console.log(encrypted);

在新代码中,应使用 PBKDF2 而不是 PBKDF1(或 PasswordDeriveBytes)。 PBKDF2 也在 RFC 2898, section 5.2. Here MS provides an implementation with Rfc2898DeriveBytes that conforms to the standard. In the posted NodeJS code PBKDF2 is used, which is the more secure implementation, but just not compatible with PBKDF1 used in the (presumably legacy) VB code. A detailed and enlightening discussion about both algorithms can be found in this MS Blog 中从 04/2004 开始定义。