簡體   English   中英

NodeJS和C#中的AES256加密/解密

[英]AES256 Encryption/Decryption in both NodeJS and C#

我對以下問題的結果采取了一些自由:

並創建了以下類文件......

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace T1.CoreUtils.Utilities
{
  public static class CryptoUtility
  {
    public static string Encrypt(string input, string passphrase = null)
    {
      byte[] key, iv;
      DeriveKeyAndIV(Encoding.ASCII.GetBytes(passphrase), null, 1, out key, out iv);

      return Convert.ToBase64String(EncryptStringToBytes(input, key, iv));
    }

    public static string Decrypt(string inputBase64, string passphrase = null)
    {
      byte[] key, iv;
      DeriveKeyAndIV(Encoding.ASCII.GetBytes(passphrase), null, 1, out key, out iv);

      return DecryptStringFromBytes(Convert.FromBase64String(inputBase64), key, iv);
    }

    private static void DeriveKeyAndIV(byte[] data, byte[] salt, int count, out byte[] key, out byte[] iv)
    {
      List<byte> hashList = new List<byte>();
      byte[] currentHash = new byte[0];

      int preHashLength = data.Length + ((salt != null) ? salt.Length : 0);
      byte[] preHash = new byte[preHashLength];

      System.Buffer.BlockCopy(data, 0, preHash, 0, data.Length);
      if (salt != null)
        System.Buffer.BlockCopy(salt, 0, preHash, data.Length, salt.Length);

      MD5 hash = MD5.Create();
      currentHash = hash.ComputeHash(preHash);

      for (int i = 1; i < count; i++)
      {
        currentHash = hash.ComputeHash(currentHash);
      }

      hashList.AddRange(currentHash);

      while (hashList.Count < 48) // for 32-byte key and 16-byte iv
      {
        preHashLength = currentHash.Length + data.Length + ((salt != null) ? salt.Length : 0);
        preHash = new byte[preHashLength];

        System.Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length);
        System.Buffer.BlockCopy(data, 0, preHash, currentHash.Length, data.Length);
        if (salt != null)
          System.Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + data.Length, salt.Length);

        currentHash = hash.ComputeHash(preHash);

        for (int i = 1; i < count; i++)
        {
          currentHash = hash.ComputeHash(currentHash);
        }

        hashList.AddRange(currentHash);
      }
      hash.Clear();
      key = new byte[32];
      iv = new byte[16];
      hashList.CopyTo(0, key, 0, 32);
      hashList.CopyTo(32, iv, 0, 16);
    }

    static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
    {
      // Check arguments. 
      if (plainText == null || plainText.Length <= 0)
        throw new ArgumentNullException("plainText");
      if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
      if (IV == null || IV.Length <= 0)
        throw new ArgumentNullException("Key");
      byte[] encrypted;
      // Create an RijndaelManaged object 
      // with the specified key and IV. 
      using (RijndaelManaged rijAlg = new RijndaelManaged())
      {
        rijAlg.Key = Key;
        rijAlg.IV = IV;

        // Create a decrytor to perform the stream transform.
        ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

        // Create the streams used for encryption. 
        using (MemoryStream msEncrypt = new MemoryStream())
        {
          using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
          {
            using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
            {

              //Write all data to the stream.
              swEncrypt.Write(plainText);
            }
            encrypted = msEncrypt.ToArray();
          }
        }
      }


      // Return the encrypted bytes from the memory stream. 
      return encrypted;

    }

    static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
    {
      // Check arguments. 
      if (cipherText == null || cipherText.Length <= 0)
        throw new ArgumentNullException("cipherText");
      if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
      if (IV == null || IV.Length <= 0)
        throw new ArgumentNullException("Key");

      // Declare the string used to hold 
      // the decrypted text. 
      string plaintext = null;

      // Create an RijndaelManaged object 
      // with the specified key and IV. 
      using (RijndaelManaged rijAlg = new RijndaelManaged())
      {
        rijAlg.Key = Key;
        rijAlg.IV = IV;

        // Create a decrytor to perform the stream transform.
        ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

        // Create the streams used for decryption. 
        using (MemoryStream msDecrypt = new MemoryStream(cipherText))
        {
          using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
          {
            using (StreamReader srDecrypt = new StreamReader(csDecrypt))
            {

              // Read the decrypted bytes from the decrypting stream 
              // and place them in a string.
              plaintext = srDecrypt.ReadToEnd();
            }
          }
        }

      }

      return plaintext;

    }
  }
}

從這里,我生成了以下via節點:

var crypto = require('crypto');
var input = "This is î╥≤ what it is.";
var passkey= "This is my password.";
var cipher = crypto.createCipher('aes-256-cbc', passkey);
var encrypted = cipher.update(input, 'utf8', 'base64') + cipher.final('base64');
encrypted
// '9rTbNbfJkYVE2m5d8g/8b/qAfeCU9rbk09Na/Pw0bak='

input = "I am the walrus, coo coo cachoo!";
passkey = "I am a ≥ò'ÿ boy baby!";
cipher = crypto.createCipher('aes-256-cbc', passkey);
encrypted = cipher.update(input, 'utf8', 'base64') + cipher.final('base64');
// 'j/e+f5JU5yerSvO7FBJzR1tGro0Ie3L8sWYaupRW1JJhraGqBfQ9z+h85VhSzEjD'

var decipher = crypto.createDecipher('aes-256-cbc', passkey);
var plain = decipher.update(encrypted, 'base64', 'utf8') + decipher.final('utf8');
plain
// 'I am the walrus, coo coo cachoo!'

由此,我創建了以下測試用例:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace T1.CoreUtils.Test.Utilities.Tests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void EncryptReturnsExpectedValue1_unicode_in_plaintext()
        {
            var passkey = "This is my password.";
            var plain = "This is î╥≤ what it is.";
            var encrypted = "9rTbNbfJkYVE2m5d8g/8b/qAfeCU9rbk09Na/Pw0bak=";

            var actual = T1.CoreUtils.Utilities.CryptoUtility.Encrypt(plain, passkey);
            Assert.AreEqual(encrypted, actual);
        }

        [TestMethod]
        public void EncryptReturnsExpectedValue2_unicode_in_passkey()
        {
            var passkey = "I am a ≥ò'ÿ boy baby!";
            var plain = "I am the walrus, coo coo cachoo!";
            var encrypted = "j/e+f5JU5yerSvO7FBJzR1tGro0Ie3L8sWYaupRW1JJhraGqBfQ9z+h85VhSzEjD";

            var actual = T1.CoreUtils.Utilities.CryptoUtility.Encrypt(plain, passkey);
            Assert.AreEqual(encrypted, actual);
        }

        [TestMethod]
        public void DecryptReturnsExpectedValue1()
        {
            var passkey = "This is my password.";
            var plain = "This is î╥≤ what it is.";
            var encrypted = "9rTbNbfJkYVE2m5d8g/8b/qAfeCU9rbk09Na/Pw0bak=";

            var actual = T1.CoreUtils.Utilities.CryptoUtility.Decrypt(encrypted, passkey);
            Assert.AreEqual(plain, actual);
        }

        [TestMethod]
        public void DecryptReturnsExpectedValue2()
        {
            var passkey = "I am a ≥ò'ÿ boy baby!";
            var plain = "I am the walrus, coo coo cachoo!";
            var encrypted = "j/e+f5JU5yerSvO7FBJzR1tGro0Ie3L8sWYaupRW1JJhraGqBfQ9z+h85VhSzEjD";

            var actual = T1.CoreUtils.Utilities.CryptoUtility.Decrypt(encrypted, passkey);
            Assert.AreEqual(plain, actual);
        }
    }
}

關:

  • EncryptReturnsExpectedValue1_unicode_in_plaintext
  • DecryptReturnsExpectedValue1

失敗:

  • EncryptReturnsExpectedValue2_unicode_in_passkey
  • DecryptReturnsExpectedValue2

我只能猜測問題出在DeriveKeyAndIV方法中。 如果我自己找到它,會嘗試一些不同的方法並回答。

好的,在檢查了node.js的加密源時,我確定編碼是使用了一個新的Buffer(passkey,'binary'),它只使用原始值x和0xFF表示所用的字節,所以我創建了一個匹配C#中的方法......這是有問題的方法......

private static byte[] RawBytesFromString(string input)
{
  var ret = new List<Byte>();

  foreach (char x in input)
  {
    var c = (byte)((ulong)x & 0xFF);
    ret.Add(c);
  }

  return ret.ToArray();
}

和更新/工作的CryptoUtil.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace T1.CoreUtils.Utilities
{
  public static class CryptoUtility
  {
    /*  Wanting to stay compatible with NodeJS
     *  http://stackoverflow.com/questions/18502375/aes256-encryption-decryption-in-both-nodejs-and-c-sharp-net/
     *  http://stackoverflow.com/questions/12261540/decrypting-aes256-encrypted-data-in-net-from-node-js-how-to-obtain-iv-and-key
     *  http://stackoverflow.com/questions/8008253/c-sharp-version-of-openssl-evp-bytestokey-method
     *  
     * var cipher = crypto.createCipher('aes-256-cbc', 'passphrase');
     * var encrypted = cipher.update("test", 'utf8', 'base64') + cipher.final('base64');
     * 
     * var decipher = crypto.createDecipher('aes-256-cbc', 'passphrase');
     * var plain = decipher.update(encrypted, 'base64', 'utf8') + decipher.final('utf8');
     */

    public static string Encrypt(string input, string passphrase = null)
    {
      byte[] key, iv;
      DeriveKeyAndIV(RawBytesFromString(passphrase), null, 1, out key, out iv);

      return Convert.ToBase64String(EncryptStringToBytes(input, key, iv));
    }

    public static string Decrypt(string inputBase64, string passphrase = null)
    {
      byte[] key, iv;
      DeriveKeyAndIV(RawBytesFromString(passphrase), null, 1, out key, out iv);

      return DecryptStringFromBytes(Convert.FromBase64String(inputBase64), key, iv);
    }

    private static byte[] RawBytesFromString(string input)
    {
      var ret = new List<Byte>();

      foreach (char x in input)
      {
        var c = (byte)((ulong)x & 0xFF);
        ret.Add(c);
      }

      return ret.ToArray();
    }

    private static void DeriveKeyAndIV(byte[] data, byte[] salt, int count, out byte[] key, out byte[] iv)
    {
      List<byte> hashList = new List<byte>();
      byte[] currentHash = new byte[0];

      int preHashLength = data.Length + ((salt != null) ? salt.Length : 0);
      byte[] preHash = new byte[preHashLength];

      System.Buffer.BlockCopy(data, 0, preHash, 0, data.Length);
      if (salt != null)
        System.Buffer.BlockCopy(salt, 0, preHash, data.Length, salt.Length);

      MD5 hash = MD5.Create();
      currentHash = hash.ComputeHash(preHash);

      for (int i = 1; i < count; i++)
      {
        currentHash = hash.ComputeHash(currentHash);
      }

      hashList.AddRange(currentHash);

      while (hashList.Count < 48) // for 32-byte key and 16-byte iv
      {
        preHashLength = currentHash.Length + data.Length + ((salt != null) ? salt.Length : 0);
        preHash = new byte[preHashLength];

        System.Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length);
        System.Buffer.BlockCopy(data, 0, preHash, currentHash.Length, data.Length);
        if (salt != null)
          System.Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + data.Length, salt.Length);

        currentHash = hash.ComputeHash(preHash);

        for (int i = 1; i < count; i++)
        {
          currentHash = hash.ComputeHash(currentHash);
        }

        hashList.AddRange(currentHash);
      }
      hash.Clear();
      key = new byte[32];
      iv = new byte[16];
      hashList.CopyTo(0, key, 0, 32);
      hashList.CopyTo(32, iv, 0, 16);
    }

    static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
    {
      // Check arguments. 
      if (plainText == null || plainText.Length <= 0)
        throw new ArgumentNullException("plainText");
      if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
      if (IV == null || IV.Length <= 0)
        throw new ArgumentNullException("Key");
      byte[] encrypted;
      // Create an RijndaelManaged object 
      // with the specified key and IV. 
      using (RijndaelManaged cipher = new RijndaelManaged())
      {
        cipher.Key = Key;
        cipher.IV = IV;
        //cipher.Mode = CipherMode.CBC;
        //cipher.Padding = PaddingMode.PKCS7;

        // Create a decrytor to perform the stream transform.
        ICryptoTransform encryptor = cipher.CreateEncryptor(cipher.Key, cipher.IV);

        // Create the streams used for encryption. 
        using (MemoryStream msEncrypt = new MemoryStream())
        {
          using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
          {
            using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
            {

              //Write all data to the stream.
              swEncrypt.Write(plainText);
            }
            encrypted = msEncrypt.ToArray();
          }
        }
      }


      // Return the encrypted bytes from the memory stream. 
      return encrypted;

    }

    static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
    {
      // Check arguments. 
      if (cipherText == null || cipherText.Length <= 0)
        throw new ArgumentNullException("cipherText");
      if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
      if (IV == null || IV.Length <= 0)
        throw new ArgumentNullException("Key");

      // Declare the string used to hold 
      // the decrypted text. 
      string plaintext = null;

      // Create an RijndaelManaged object 
      // with the specified key and IV. 
      using (var cipher = new RijndaelManaged())
      {
        cipher.Key = Key;
        cipher.IV = IV;
        //cipher.Mode = CipherMode.CBC;
        //cipher.Padding = PaddingMode.PKCS7;

        // Create a decrytor to perform the stream transform.
        ICryptoTransform decryptor = cipher.CreateDecryptor(cipher.Key, cipher.IV);

        // Create the streams used for decryption. 
        using (MemoryStream msDecrypt = new MemoryStream(cipherText))
        {
          using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
          {
            using (StreamReader srDecrypt = new StreamReader(csDecrypt))
            {

              // Read the decrypted bytes from the decrypting stream 
              // and place them in a string.
              plaintext = srDecrypt.ReadToEnd();
            }
          }
        }

      }

      return plaintext;

    }
  }
}

注意:一些與此相關的代碼......

這些不是分別在nugetnpm因為他們真的不屬於那里......它主要是為了想法和參考。 我確實需要更好地清除節點端,以便匹配。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM