[英]NodeJS 3DES ECB encryption does not equal C# encryption
I'm trying to convert the C# code to encrypt text using 3DES ECB (You can copy and paste it on https://dotnetfiddle.net/ to run it)我正在尝试使用 3DES ECB 将 C# 代码转换为加密文本(您可以将其复制并粘贴到https://dotnetfiddle.net/ 上以运行它)
using System;
using System.Configuration;
using System.Security.Cryptography;
using System.Text;
public class Program
{
public static void Main()
{
string toEncrypt = "testtext";
string key = "testkey";
bool useHashing = true;
byte[] keyArray;
byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
System.Configuration.AppSettingsReader settingsReader =
new AppSettingsReader();
key = string.IsNullOrEmpty(key) ? (string)settingsReader.GetValue("SecurityKey", typeof(String)) : key;
if (useHashing)
{
MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
hashmd5.Clear();
}
else
{
keyArray = UTF8Encoding.UTF8.GetBytes(key);
}
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
key = Convert.ToBase64String(keyArray, 0, keyArray.Length);
Console.WriteLine(key);
tdes.Key = keyArray;
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tdes.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
tdes.Clear();
Console.Write(Convert.ToBase64String(resultArray, 0, resultArray.Length));
}
}
OUTPUT:输出:
Ihs2jX9fWXhn9SWXHyj/dQ== <- md5 secret key
wHL9J7vhm9LZI2W5DQJGKw== <- encrypt result
So I rewrite the above code in NodeJS to use crypto所以我在NodeJS中重写了上面的代码来使用crypto
const crypto = require('crypto');
const md5 = text => {
return crypto
.createHash('md5')
.update(text)
.digest('base64');
}
const encrypt = (text, secretKey) => {
secretKey = md5(secretKey);
console.log(secretKey);
const cipher = crypto.createCipher('des-ede3', secretKey);
const encrypted = cipher.update(text, 'utf8', 'base64');
return encrypted + cipher.final('base64');
};
const encrypted = encrypt('testtext', 'testkey');
console.log(encrypted);
OUTPUT:输出:
Ihs2jX9fWXhn9SWXHyj/dQ== <- md5 secret key
VNa9fDYgPus5IMhUZRI+jQ== <- encrypt result
I think the problem lies in C# and NodeJS Crypto approach in using 3DES ECB.我认为问题在于使用 3DES ECB 的 C# 和 NodeJS 加密方法。 Any idea how to replicate the C# code behaviour in NodeJS?
知道如何在 NodeJS 中复制 C# 代码行为吗?
Triple DES is only defined for 192 bit keys.三重 DES 仅针对 192 位密钥定义。 An MD5 hash only provides 128 bit.
MD5 哈希仅提供 128 位。 There are multiple ways of expanding a potential 128 bit key into a 192 bit key.
有多种方法可以将潜在的 128 位密钥扩展为 192 位密钥。 If we assume that the 128 bit key is made up of two 64 bit sub keys k1 and k2 , then C# will create a 192 bit key consisting of k1 , k2 and k1 again.
如果我们假设 128 位密钥由两个 64 位子密钥k1和k2 组成,那么 C# 将再次创建一个由k1 、 k2和k1组成的 192 位密钥。
Here is the code that works:这是有效的代码:
const crypto = require('crypto');
const md5 = text => {
return crypto
.createHash('md5')
.update(text)
.digest();
}
const encrypt = (text, secretKey) => {
secretKey = md5(secretKey);
console.log(secretKey.toString('base64'));
secretKey = Buffer.concat([secretKey, secretKey.slice(0, 8)]); // properly expand 3DES key from 128 bit to 192 bit
const cipher = crypto.createCipheriv('des-ede3', secretKey, '');
const encrypted = cipher.update(text, 'utf8', 'base64');
return encrypted + cipher.final('base64');
};
const encrypted = encrypt('testtext', 'testkey');
console.log(encrypted);
The other issue that you had was using crypto#createCipher
instead of crypto#createCipheriv
.您遇到的另一个问题是使用
crypto#createCipher
而不是crypto#createCipheriv
。 The former has an additional hashing of the "key" which you don't want in this case.前者有一个额外的“密钥”散列,在这种情况下你不需要。
Other potential problems:其他潜在问题:
Never use ECB mode .永远不要使用ECB 模式。 It's deterministic and therefore not semantically secure.
它是确定性的,因此在语义上不安全。 You should at the very least use a randomized mode like CBC or CTR .
您至少应该使用像CBC或CTR这样的随机模式。 It is better to authenticate your ciphertexts so that attacks like a padding oracle attack are not possible.
最好验证您的密文,以便像填充预言机攻击这样的攻击是不可能的。 This can be done with authenticated modes like GCM or EAX, or with an encrypt-then-MAC scheme.
这可以通过 GCM 或 EAX 等身份验证模式或使用encrypt-then-MAC方案来完成。
Don't use Triple DES nowadays.现在不要使用三重 DES。 It only provides at best 112 bit of security even if you use the largest key size of 192 bit.
即使您使用最大的 192 位密钥,它也最多只能提供 112 位的安全性。 If a shorter key size is used, then it only provides 56 or 57 bits of security.
如果使用较短的密钥大小,则它仅提供 56 或 57 位的安全性。 AES would be faster (processors have a special AES-NI instruction set) and even more secure with the lowest key size of 128 bit.
AES 会更快(处理器有一个特殊的 AES-NI 指令集),并且使用 128 位的最低密钥大小甚至更安全。 There is also a practical limit on the maximum ciphertext size with 3DES.
3DES 的最大密文大小也有实际限制。 See Security comparison of 3DES and AES .
请参阅3DES 和 AES 的安全性比较。
You should never use a simple hash function to protect your user's passwords.永远不要使用简单的哈希函数来保护用户的密码。 You need to use a strong hashing scheme like PBKDF2, bcrypt, scrypt and Argon2.
您需要使用强大的散列方案,如 PBKDF2、bcrypt、scrypt 和 Argon2。 Be sure to use a high cost factor/iteration count.
确保使用高成本因子/迭代次数。 It is common to choose the cost so that a single iteration takes at least 100ms.
通常选择成本使得单次迭代至少需要 100 毫秒。 See more: How to securely hash passwords?
查看更多:如何安全地散列密码?
Ok, just use https://www.npmjs.com/package/nod3des to replicate the same behavior as C#.好的,只需使用https://www.npmjs.com/package/nod3des来复制与 C# 相同的行为。 In case you're wondering how it works
如果你想知道它是如何工作的
https://github.com/4y0/nod3des/blob/master/index.js#L30 https://github.com/4y0/nod3des/blob/master/index.js#L30
var CryptoJS = require('crypto-js');
var forge = require('node-forge');
var utf8 = require('utf8');
...
_3DES.encrypt = function (key, text){
key = CryptoJS.MD5(utf8.encode(key)).toString(CryptoJS.enc.Latin1);
key = key + key.substring(0, 8);
var cipher = forge.cipher.createCipher('3DES-ECB', forge.util.createBuffer(key));
cipher.start({iv:''});
cipher.update(forge.util.createBuffer(text, 'utf-8'));
cipher.finish();
var encrypted = cipher.output;
return ( forge.util.encode64(encrypted.getBytes()) );
}
I had a different requirement (CBC) and I thought of adding it here in case it helps anyone looking for another solution.我有一个不同的要求(CBC),我想在这里添加它,以防万一它有助于寻找其他解决方案的人。 The code is below, but if needs more details on the context, check this gist: gist
代码如下,但如果需要有关上下文的更多详细信息,请检查此要点: gist
import * as crypto from 'crypto';
/**
* This class is an implementation to encrypt/decrypt 3DES encrypted from .NET
*/
export class TripleDESCryptoHelper {
// Encryption algorithm
private static readonly algorithm = 'des-ede-cbc';
/**
* Decrypts a value encrypted using 3DES Algorithm.
*
* @param encryptionKey Key used for encryption
* @param encryptedValue Value to be decrypted
* @returns string containing the value (ascii)
*/
static decrypt(encryptionKey: string, encryptedValue: string): string {
const keyHash = crypto
.createHash('md5')
.update(encryptionKey)
.digest();
const iv = keyHash.slice(0, 8);
const encrypted = Buffer.from(encryptedValue, 'base64');
const decipher = crypto.createDecipheriv(TripleDESCryptoHelper.algorithm, keyHash, iv);
const decoded = decipher.update(encrypted, undefined, 'ascii') + decipher.final('ascii');
return decoded;
}
/**
* Encrypts a value using 3DES Algorithm.
*
* @param encryptionKey Key used for encryption
* @param encryptedText The text to be encrypted
*/
static encrypt(encryptionKey: string, encryptedText: string): string {
const keyHash = crypto
.createHash('md5')
.update(encryptionKey)
.digest();
const iv = keyHash.slice(0, 8);
const encrypted = Buffer.from(encryptedText);
const cipher = crypto.createCipheriv(TripleDESCryptoHelper.algorithm, keyHash, iv);
const encoded = Buffer.concat([cipher.update(encrypted), cipher.final()]);
const encodedAsBase64 = encoded.toString('base64');
return encodedAsBase64;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.