【问题标题】:Converting C# Cryptography.Rijndael encryption to Javascript (pref. crypto-js)将 C# Cryptography.Rijndael 加密转换为 Javascript (pref. crypto-js)
【发布时间】:2016-07-11 19:42:55
【问题描述】:

我有两个系统共享一个用户数据库,所以认证需要相同。

密码当前使用 C# 的 Cryptography.Rijndael(注:not RijndaelManaged)加密。使用自定义键和 iv(初始化向量)。 (CBC 模式和 Pkcs7 填充)

C#加密如下:

Rijndael alg = Rijndael.Create();
alg.Key = key;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(clearData, 0, clearData.Length);
cs.Close();
byte[] encryptedData = ms.ToArray();

key 是 256 位(32 字节),iv(初始化向量)是 128 位(16 字节)。块大小为 128 位(16 字节)。

key 和 iv 是来自 base64 字符串的字节数组,通过:

byte[] key = Convert.FromBase64String(base64Key);
byte[] iv = Convert.FromBase64String(base64IV);

注意我无法控制 C# 代码(旧系统)。

在基于 javascript 的系统上,我必须以完全相同的方式加密密码。我试过使用节点crypto-js 无济于事。 我的代码如下所示:

var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(password), keyCodeWords, {
    iv: ivCodeWords,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});

keyCodeWords 和 ivCodeWords 是来自相同 base64 密钥和 iv 的 CryptoJS 代码字,如下所示:

var keyCodeWords = CryptoJS.enc.Base64.parse(base64Key);
var ivCodeWords = CryptoJS.enc.Base64.parse(base64IV);

加密确实有效(从某种意义上说,我可以加密然后解密以获得相同的值)。但是,问题是加密值(encrypted.ciphertext)与 C# 不同,所以我无法进行身份验证。

比较是通过base64加密的输出字符串进行比较。

如何使 crypto-js(或任何其他 javascript 加密库)与 C# Rijndael 一致?

【问题讨论】:

  • Rijndael 在 C# 中有上百万个选项。哪个是你的?你能给我们看一下代码吗?您是否检查过编码是否合适?请记住,AES 只是 Rijndael 的一个子集。如果您在 C# 中使用 128 位以外的块大小,则不能使用 CryptoJS。
  • 您永远不应该加密您的用户密码。您需要使用散列代替一些强大的散列,例如 PBKDF2、bcrypt、scrypt 和 Argon2。由于散列函数是单向函数,因此您将无法“解密”散列。为了验证您的用户,您可以再次通过哈希函数运行密码,以便与存储在数据库中的哈希值进行比较。查看更多:How to securely hash passwords?
  • @ArtjomB。 , C# 加密:Rijndael alg = Rijndael.Create(); alg.Key = key; alg.IV = IV; CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(clearData, 0, clearData.Length); cs.Close(); byte[] encryptedData = ms.ToArray();key 是 256 位,`iv' 是 128 位。块大小为 128。不幸的是,我无法控制 C# 实现(旧系统)——我也只会使用散列。
  • @ArtjomB.,很抱歉我编辑了我的问题。请检查一下。谢谢。
  • 好的,看起来不错。所以一定是编码问题。您如何将密码字符串编码为双方的字节?您如何在 CryptoJS 中提供来自 C# 的密钥和 IV?能提供对应的代码吗?您如何比较结果?

标签: javascript c# cryptojs rijndael


【解决方案1】:

代码正常运行...您的问题可能在于 C# 或 Javascript 中密码/IV 的处理...在 C# 中:https://ideone.com/APC4MM 和在 Javascript 中:https://jsfiddle.net/jjwy5472/

我在这两种情况下都使用了相同的密码:

byte[] key = new byte[] 
{ 
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
};

byte[] iv = new byte[] 
{ 
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
    0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
};

明文是

"Hello world! This is a wonderful day! àèéìòù"

在这两种情况下,生成的加密文本都是

aa429aa5c1c928b86d81b43ff3fb6cc46f24cc73957bc7c00829357bf441eb3be9cf8aef2ff6f819f9b95c69886b169b6959c4f7ece0620c6a28f849516adee9

但请注意,Javascript 中的encrypted 变量是一个复杂对象,包含各种属性以及各种版本的加密数据。 encrypted.toString()返回加密数据的base64版本,encrypted.ciphertextWordArray(相当于byte[]的CryptoJS),encrypted.ciphertext.toString()是它的十六进制版本(相当于在C#中做BitConverter.ToString(encryptedData).Replace("-", "").ToLowerInvariant() )。

【讨论】:

  • 这对我帮助很大。我能够看到问题出在哪里。这是编码,确实当你像你一样使用utf8编码时,结果是一样的。但是,我刚刚注意到,在我拥有的 C# 版本中,Unicode 被用作编码标准。现在我正在努力在 CryptoJS 上找到 Unicode 编码。一旦我能找到并确认我有相同的结果,我会接受你的回答。
  • @Kholofelo 您必须使用CryptoJS.enc.Utf16LE。查看示例:ideone.com/lRG126jsfiddle.net/am216ywd
  • @Kholofelo 请注意,CryptoJS.enc.Utf16LE 不能包含在您使用 CryptoJS 的“基础”库中。它在一个额外的包中。
  • 这绝对有帮助,我不知道 Utf16LE 与 Unicode 相同。非常感谢@xanatos,我接受了这个答案。经过这么久,这就是我必须改变的!
  • @Kholofelo 请注意“奇怪”CryptoJS.enc.Utf16 == CryptoJS.enc.Utf16BE,因此 Utf16 的默认编码是 Big Endian。这很“奇怪”,因为现在 99% 的计算机都是小端(而 Windows 是小端)。我在“奇怪”周围使用双引号,因为从技术上讲,CryptoJS 行为是更正确的行为(en.wikipedia.org/wiki/UTF-16#Byte_order_encoding_schemes),但有时“正确性”和“人们假设的”是不同的东西 :-)
猜你喜欢
  • 2021-05-11
  • 1970-01-01
  • 1970-01-01
  • 2012-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-12
  • 1970-01-01
相关资源
最近更新 更多