Rijndael加密VB到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,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,"binary");

    const derivedBytes = crypto.pbkdf2Sync(passphrase,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:“ Password” =>“ IMY4Jo6BkjDPYXR6DK + rhw ==”

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

我显然缺少与VB函数获得相同结果的东西。但是经过几天的研究,我对此一无所知。你有什么主意吗?

解决方法

此问题是由不同的密钥派生功能引起的。在VB代码PasswordDeriveBytes中使用,该代码基于PBKDF1。 PBKDF1在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中定义。 MS在此提供了符合标准的Rfc2898DeriveBytes的实现。在发布的NodeJS代码中,使用了PBKDF2,这是更安全的实现,但与(可能是旧版)VB代码中使用的PBKDF1不兼容。在2004年4月4日发布的MS Blog中,可以找到有关这两种算法的详细而有启发性的讨论。