【问题标题】:NodeJS 3DES ECB encryption does not equal C# encryptionNodeJS 3DES ECB 加密不等于 C# 加密
【发布时间】:2020-01-26 19:04:44
【问题描述】:

我正在尝试将 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 Crypto 方法。知道如何在 NodeJS 中复制 C# 代码 behaviour 吗?

【问题讨论】:

    标签: c# node.js encryption 3des ecb


    【解决方案1】:

    三重 DES 仅针对 192 位密钥定义。 MD5 哈希仅提供 128 位。有多种方法可以将潜在的 128 位密钥扩展为 192 位密钥。如果我们假设 128 位密钥由两个 64 位子密钥 k1k2 组成,那么 C# 将创建一个由 k1、k2k1

    这是有效的代码:

    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。前者有一个额外的“密钥”散列,在这种情况下你不需要。


    其他潜在问题:

    • 切勿使用ECB mode。它是确定性的,因此在语义上不安全。您至少应该使用像CBCCTR 这样的随机模式。最好对您的密文进行身份验证,以免像padding oracle attack 这样的攻击是不可能的。这可以通过 GCM 或 EAX 等经过身份验证的模式或encrypt-then-MAC 方案来完成。

    • 现在不要使用三重 DES。 即使您使用 192 位的最大密钥大小,它也最多只能提供 112 位的安全性。如果使用较短的密钥大小,则它仅提供 56 或 57 位的安全性。 AES 会更快(处理器具有特殊的 AES-NI 指令集),并且使用 128 位的最低密钥大小甚至更安全。 3DES 的最大密文大小也有实际限制。见Security comparison of 3DES and AES

    • 永远不要使用简单的哈希函数来保护用户的密码。您需要使用强散列方案,如 PBKDF2、bcrypt、scrypt 和 Argon2。确保使用高成本因数/迭代次数。通常选择成本以使单次迭代至少需要 100 毫秒。查看更多:How to securely hash passwords?

    【讨论】:

    • 您好,感谢您的回答,是的,关于 ECB 模式,这是我雇主业务伙伴的要求。我告诉他们使用 AES,但他们更喜欢使用 ECB,原因我无法在此说明。
    • 现在如何解密?
    • @JaikeySarraf 基本相同的代码,只是将createCipheriv 更改为createDecipheriv,但正如我所说,这只会在没有IV 的情况下执行ECB 模式,这是非常不安全的。
    • 正确,我们正在更改,但我们不想通知客户更改机密详细信息或重新输入,所以首先我们解密所有内容,然后我们将使用 PKBDF2,谢谢
    【解决方案2】:

    好的,只需使用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()) );
    
    }
    

    【讨论】:

    • 所以您已经能够完成您的任务,但是这篇文章是否回答您实际提出的问题?
    • @AakashM 是的,确实如此,但我不能在发布日期后的 2 天内接受我自己的答案。我认为问题是“如何在 NodeJS 中复制 C# 的行为”而不是如何“将 C# 代码转换为 NodeJS”,我将更新问题
    • 请发布解密功能。​​
    【解决方案3】:

    我有一个不同的要求 (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;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-11
      • 1970-01-01
      • 2016-12-22
      • 1970-01-01
      • 2012-11-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多