NodeJS 3DES ECB 加密不等于 C# 加密
NodeJS 3DES ECB encryption does not equal C# encryption
我正在尝试将 C# 代码转换为使用 3DES ECB 加密文本
(可以复制粘贴到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));
}
}
输出:
Ihs2jX9fWXhn9SWXHyj/dQ== <- md5 secret key
wHL9J7vhm9LZI2W5DQJGKw== <- encrypt result
所以我在 NodeJS 中重写了上面的代码以使用加密
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);
输出:
Ihs2jX9fWXhn9SWXHyj/dQ== <- md5 secret key
VNa9fDYgPus5IMhUZRI+jQ== <- encrypt result
我认为问题在于使用 3DES ECB 的 C# 和 NodeJS 加密方法。知道如何在 NodeJS 中复制 C# 代码 behaviour 吗?
好的,只需使用 https://www.npmjs.com/package/nod3des 复制与 C# 相同的行为。如果您想知道它是如何工作的
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()) );
}
三重 DES 仅针对 192 位密钥定义。 MD5 哈希仅提供 128 位。有多种方法可以将潜在的 128 位密钥扩展为 192 位密钥。如果我们假设 128 位密钥由两个 64 位子密钥 k1 和 k2 组成,那么 C# 将创建一个 192 位密钥,其中包含k1、k2 和 k1。
这是有效的代码:
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);
您遇到的另一个问题是使用 crypto#createCipher
而不是 crypto#createCipheriv
。前者有一个额外的 "key" 散列,在这种情况下你不需要。
其他潜在问题:
切勿使用 ECB mode. It's deterministic and therefore not semantically secure. You should at the very least use a randomized mode like CBC or 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 方案。
现在不要使用 Triple DES。 即使您使用 192 位的最大密钥大小,它也最多只能提供 112 位的安全性。如果使用较短的密钥大小,则它仅提供 56 或 57 位的安全性。 AES 会更快(处理器有一个特殊的 AES-NI 指令集)并且更安全,最小密钥大小为 128 位。 3DES 的最大密文大小也有实际限制。见 Security comparison of 3DES and AES.
您永远不应使用简单的哈希函数来保护您的用户密码。您需要使用强大的哈希方案,如 PBKDF2、bcrypt、scrypt 和 Argon2。请务必使用高成本 factor/iteration 计数。通常选择成本以使单次迭代至少花费 100 毫秒。查看更多:How to securely hash passwords?
我有一个不同的要求 (CBC),我想在这里添加它,以防它帮助任何寻找其他解决方案的人。代码如下,但如果需要有关上下文的更多详细信息,请查看此要点: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;
}
}
我正在尝试将 C# 代码转换为使用 3DES ECB 加密文本 (可以复制粘贴到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));
}
}
输出:
Ihs2jX9fWXhn9SWXHyj/dQ== <- md5 secret key
wHL9J7vhm9LZI2W5DQJGKw== <- encrypt result
所以我在 NodeJS 中重写了上面的代码以使用加密
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);
输出:
Ihs2jX9fWXhn9SWXHyj/dQ== <- md5 secret key
VNa9fDYgPus5IMhUZRI+jQ== <- encrypt result
我认为问题在于使用 3DES ECB 的 C# 和 NodeJS 加密方法。知道如何在 NodeJS 中复制 C# 代码 behaviour 吗?
好的,只需使用 https://www.npmjs.com/package/nod3des 复制与 C# 相同的行为。如果您想知道它是如何工作的
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()) );
}
三重 DES 仅针对 192 位密钥定义。 MD5 哈希仅提供 128 位。有多种方法可以将潜在的 128 位密钥扩展为 192 位密钥。如果我们假设 128 位密钥由两个 64 位子密钥 k1 和 k2 组成,那么 C# 将创建一个 192 位密钥,其中包含k1、k2 和 k1。
这是有效的代码:
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);
您遇到的另一个问题是使用 crypto#createCipher
而不是 crypto#createCipheriv
。前者有一个额外的 "key" 散列,在这种情况下你不需要。
其他潜在问题:
切勿使用 ECB mode. It's deterministic and therefore not semantically secure. You should at the very least use a randomized mode like CBC or 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 方案。
现在不要使用 Triple DES。 即使您使用 192 位的最大密钥大小,它也最多只能提供 112 位的安全性。如果使用较短的密钥大小,则它仅提供 56 或 57 位的安全性。 AES 会更快(处理器有一个特殊的 AES-NI 指令集)并且更安全,最小密钥大小为 128 位。 3DES 的最大密文大小也有实际限制。见 Security comparison of 3DES and AES.
您永远不应使用简单的哈希函数来保护您的用户密码。您需要使用强大的哈希方案,如 PBKDF2、bcrypt、scrypt 和 Argon2。请务必使用高成本 factor/iteration 计数。通常选择成本以使单次迭代至少花费 100 毫秒。查看更多:How to securely hash passwords?
我有一个不同的要求 (CBC),我想在这里添加它,以防它帮助任何寻找其他解决方案的人。代码如下,但如果需要有关上下文的更多详细信息,请查看此要点: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;
}
}