AES 256 Nodejs 加密和在 C#.Net 中解密

Min*_*Con 2 .net c# encryption cryptography node.js

我尝试在 C# 中解密的数据在 Nodejs 中使用 AES-256 算法进行加密,代码如下。

const crypto = require('crypto');
const validator = require('validator');
const algorithm = 'aes256';
const inputEncoding = 'utf8';
const outputEncoding = 'hex';
const iv = crypto.randomBytes(16)

function encrypt(key,text) {
key = processKey(key);
let cipher = crypto.createCipheriv(algorithm, key, iv);
let ciphered = cipher.update(text, inputEncoding, outputEncoding);
ciphered += cipher.final(outputEncoding);
return ciphered;
}
Run Code Online (Sandbox Code Playgroud)

现在,我获得了长度为 32 的加密数据(如“1234567304e07a5d2e93fbeefd0e417e”)和长度为 32 的密​​钥(如“123456673959499f9d37623168b2c977”)。

我尝试使用下面的 C# 代码解密相同的内容,但收到错误“要解密的数据长度无效”。请提供建议。

public static string Decrypt(string combinedString, string keyString)
{
    string plainText;
    byte[] combinedData = StringToByteArray(combinedString);
    Aes aes = Aes.Create();
    aes.Key = Encoding.UTF8.GetBytes(keyString);
    byte[] iv = new byte[aes.BlockSize / 8];
    byte[] cipherText = new byte[combinedData.Length - iv.Length];
    Array.Copy(combinedData, iv, iv.Length);
    Array.Copy(combinedData, iv.Length, cipherText, 0, cipherText.Length);
    aes.IV = iv;
    aes.Mode = CipherMode.CBC;
    ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);

    using (MemoryStream ms = new MemoryStream(cipherText))
    {
        using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
        {
            using (StreamReader sr = new StreamReader(cs))
            {
                plainText = sr.ReadToEnd();                 
            }
        }

        return plainText;
    }
}
public static byte[] StringToByteArray(string hex) {
return Enumerable.Range(0, hex.Length)
                 .Where(x => x % 2 == 0)
                 .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                 .ToArray();
}
Run Code Online (Sandbox Code Playgroud)

下面是 Node.js 中的解密代码,运行良好

const crypto = require('../functions/crypto');
const assert = require('assert');
const { v4: uuidv4 } = require('uuid');
describe('crypto module', function() {
it('should work', function(done) {
    const toHash = 'Octomate';
    const hashKey = uuidv4();

    const hash = crypto.encrypt(hashKey, toHash);
    const decrypted = crypto.decrypt(hashKey, hash);

    assert.strictEqual(toHash, decrypted);
    done();
});
});
Run Code Online (Sandbox Code Playgroud)

Top*_*aco 5

发布的 NodeJS 在 CBC 模式下使用 AES-256 执行加密。明文采用UTF8编码,密文采用十六进制编码。此外,还会生成随机 IV 并用于加密。由于方法processKey没有贴出来,就不做进一步的考虑,所以使用下面的NodeJS代码衍生出一个C#代码进行解密:

const crypto = require('crypto');
const validator = require('validator');
const algorithm = 'aes256';
const inputEncoding = 'utf8';
const outputEncoding = 'hex';
const iv = crypto.randomBytes(16)

function encrypt(key,text) {
    //key = processKey(key);    // not posted
    let cipher = crypto.createCipheriv(algorithm, key, iv);
    let ciphered = cipher.update(text, inputEncoding, outputEncoding);
    ciphered += cipher.final(outputEncoding);
    return ciphered;
}

const key = '123456673959499f9d37623168b2c977';
const text = 'The quick brown fox jumps over the lazy dog'
const encrypted = encrypt(key, text);

console.log("IV (hex):         " + iv.toString('hex'));
console.log("Ciphertext (hex): " + encrypted);
Run Code Online (Sandbox Code Playgroud)

使用发布的密钥,123456673959499f9d37623168b2c977明文The quick brown fox jumps over the lazy dog会产生以下输出:

IV (hex):         850bd88afd08c4ea14e75276277644f0
Ciphertext (hex): 5167ac87ebc79d5240255ff687c6bc8981c8791c353367a2e238a10e0983bf16e230ccf0511096f60c224b99927b3364
Run Code Online (Sandbox Code Playgroud)

请注意,由于随机 IV,每次加密都会生成不同的密文。

C#中的解密可以如下完成:

string ciphertext = "5167ac87ebc79d5240255ff687c6bc8981c8791c353367a2e238a10e0983bf16e230ccf0511096f60c224b99927b3364";
string key = "123456673959499f9d37623168b2c977";
string iv = "850bd88afd08c4ea14e75276277644f0";
string decryptedText = Decrypt(ciphertext, key, iv);
Console.WriteLine("Decrypted text: " + decryptedText);
Run Code Online (Sandbox Code Playgroud)

public static string Decrypt(string ciphertextHex, string keyUtf8, string ivHex)
{
    byte[] ciphertext = StringToByteArray(ciphertextHex);
    byte[] iv = StringToByteArray(ivHex);

    string plaintext = "";
    using (Aes aes = Aes.Create())
    {
        aes.Key = Encoding.UTF8.GetBytes(keyUtf8);
        aes.IV = iv;
        aes.Mode = CipherMode.CBC;          // default
        aes.Padding = PaddingMode.PKCS7;    // default

        ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);

        using (MemoryStream ms = new MemoryStream(ciphertext))
        {
            using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
            {
                using (StreamReader sr = new StreamReader(cs, Encoding.UTF8)) // UTF8: default
                {
                    plaintext = sr.ReadToEnd();
                }
            }
        }
    }
    return plaintext;
}
Run Code Online (Sandbox Code Playgroud)

// from /sf/answers/22498311/
public static byte[] StringToByteArray(string hex)
{
    return Enumerable.Range(0, hex.Length)
                     .Where(x => x % 2 == 0)
                     .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                     .ToArray();
}
Run Code Online (Sandbox Code Playgroud)

请注意以下事项:

  • 解密和加密必须使用相同的 IV。由于 IV 的大小是已知的(与块大小相同)并且 IV 不是秘密的,因此它通常放在字节级别的密文前面(未加密且没有分隔符),并且结果是 Base64 编码的。该数据被发送到接收器,接收器对接收到的数据进行 Base64 解码,然后分离。

    在发布的 NodeJS 代码中不会发生这种情况。然而,在您发布的C# 代码中,这种连接正是预期的,因此执行了 IV 和密文的分离。在我的评论中,我描述了 NodeJS 代码中连接 IV 和密文的更改,以便可以使用 C# 代码解密结果。

    由于显然 NodeJS 代码是主要代码并且它不会连接,因此我的答案中发布的 C# 代码也不会连接。然而,在这种情况下,IV 必须作为参数传递,否则,如已经提到的,解密是不可能的。

  • 不幸的是,发布的示例不是很有帮助,因为您只发布了密钥和密文(顺便说一句,它只有 16 个字节长,因为它是十六进制编码的),而不是随机生成的 IV。所以解密是不可能的。该示例也不能用于密文的比较,因为由于随机 IV,每次都会生成不同的密文。

  • 由于 NodeJS 代码使用 AES-256,因此密钥大小必须为 32 字节。因此密钥可能是用 UTF8 编码的。根据这些值,也可以进行十六进制编码,但这只会产生 16 字节的密钥。由于该方法processKey未发布,因此不能排除对密钥进行进一步处理,此处不予考虑。

  • 不幸的是,发布的用于解密的 NodeJS 代码也没有多大帮助,因为没有定义几种方法(例如crypto.encryptcrypto.decrypt),并且至少我看不到与发布的用于加密的 NodeJS 代码有任何有用的关系。