如何解决AES 256 Nodejs在C#.Net中进行加密和解密
我尝试在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;
}
现在,我获得了长度为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,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();
}
下面是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();
});
});
解决方法
发布的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);
使用发布的键123456673959499f9d37623168b2c977
和纯文本The quick brown fox jumps over the lazy dog
会产生以下输出:
IV (hex): 850bd88afd08c4ea14e75276277644f0
Ciphertext (hex): 5167ac87ebc79d5240255ff687c6bc8981c8791c353367a2e238a10e0983bf16e230ccf0511096f60c224b99927b3364
请注意,由于随机IV,每次加密都会生成不同的密文。
可以使用C#进行解密,如下所示:
string ciphertext = "5167ac87ebc79d5240255ff687c6bc8981c8791c353367a2e238a10e0983bf16e230ccf0511096f60c224b99927b3364";
string key = "123456673959499f9d37623168b2c977";
string iv = "850bd88afd08c4ea14e75276277644f0";
string decryptedText = Decrypt(ciphertext,iv);
Console.WriteLine("Decrypted text: " + decryptedText);
使用
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;
}
和
// from https://stackoverflow.com/a/321404/9014097
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();
}
请注意以下几点:
-
解密必须使用与加密相同的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.encrypt
,crypto.decrypt
),至少我看不到任何方法与发布的用于加密的NodeJS代码的有用关系。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。