2016-01-19 1 views
3

Я сделал небольшое приложение для шифрования и расшифровки некоторого текста. Все в порядке, пока я использую массив байтов прямо из шифрования. Но как только я делаю копию массива, чтобы имитировать процесс отправки зашифрованного текста в виде файла, дешифрование не будет выполняться.Невозможно расшифровать с помощью CryptEncrypt/CryptDecrypt в C#

Почему я не могу запустить дешифрование с использованием скопированного массива?

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

using System.Runtime.InteropServices.WindowsRuntime; 
using System.Runtime.InteropServices; 
using System.IO; 


namespace EncryptDecryptApplication 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      //Text to encrypt 
      string plaintext = "Text to encrypt and decrypt!"; 

      // Password 
      const string password = "password"; 

      // Constants used in cryptography functions 
      const string MS_ENH_RSA_AES_PROV = "Microsoft Enhanced RSA and AES Cryptographic Provider"; //Name of provider. Same as "Microsoft AES Cryptographic Provider". 
      const byte PROV_RSA_AES = 24;   //Type of provider 
      const string KeyContainer = null;  //Name of the key container to be used, if NULL then a default key container name is used. Must be a null-terminated string. 
      const uint ALG_CLASS_HASH = (4 << 13); //32768 = 4*2^13; //Samma tror jag för alla hashalgoritmer 
      const uint ALG_TYPE_ANY = (0);  //Samma tror jag för alla hashalgoritmer 
      const uint ALG_SID_SHA_256 = 12; //ALG_SID_MD5 = 3, ALG_SID_SHA = 4, ALG_SID_SHA_256 = 12, ALG_SID_SHA_384 = 13, ALG_SID_SHA_512 = 14 
      const uint CALG_SHA_256 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256); 
      const int ALG_CLASS_DATA_ENCRYPT = 24576; 
      const int ALG_TYPE_BLOCK = 1536;  //used in all types of AES, and in RC2 
      const int ALG_SID_AES_256 = 16; //ALG_SID_AES_128 = 14, ALG_SID_AES_192 = 15, ALG_SID_AES_256 = 16 
      const int CALG_AES_256 = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_256); 
      const int ENCRYPT_ALGORITHM = CALG_AES_256; 

      // Obtain handle to Cryptographic Service Provider (CSP) 
      string ProviderCSP = MS_ENH_RSA_AES_PROV + null;  //name of the CSP to be used. Must be a null-terminated string. 
      IntPtr CSPhandle = new IntPtr(); 
      Crypt32.CryptAcquireContext(ref CSPhandle, KeyContainer, ProviderCSP, PROV_RSA_AES, 0); 

      //Create hash object 
      IntPtr handleHashObj = new IntPtr(); 
      Crypt32.CryptCreateHash(CSPhandle, CALG_SHA_256, IntPtr.Zero, 0, ref handleHashObj); 

      //Hash password 
      byte[] pwByteArray = Encoding.Unicode.GetBytes(password); 
      uint pwByteAmt = (uint)ASCIIEncoding.Unicode.GetByteCount(password); 
      Crypt32.CryptHashData(handleHashObj, pwByteArray, pwByteAmt, 0); 

      //Dervie session key from the hashed password 
      IntPtr handleSessionKey = new IntPtr(); 
      Crypt32.CryptDeriveKey(CSPhandle, ENCRYPT_ALGORITHM, handleHashObj, 0, ref handleSessionKey); 

      //CryptEncrypt iteration no 1 - Obtain buffer size (output ByteAmt_Itr1) 
      byte[] byteArray = new byte[plaintext.Length * sizeof(char)]; 
      System.Buffer.BlockCopy(plaintext.ToCharArray(), 0, byteArray, 0, byteArray.Length); 

      uint byteAmt_Itr1 = (uint)byteArray.Length; //No of bytes, i.e. the size, of the plaintext. 
      uint bufferSize_Itr1 = byteAmt_Itr1; //Set buffer size to input data size for now 

      Crypt32.CryptEncrypt(handleSessionKey, IntPtr.Zero, 1, 0, null, ref byteAmt_Itr1, 0); 

      //CryptEncrypt iteration no 2 - Encryption 
      uint byteAmt_Itr2 = (uint)byteArray.Length; //No of bytes, i.e. the size, of the plaintext. 
      uint bufferSize_Itr2 = byteAmt_Itr1; //Output from iteration no 1 - size of output data, i.e. correct buffer size 

      Crypt32.CryptEncrypt(handleSessionKey, IntPtr.Zero, 1, 0, byteArray, ref byteAmt_Itr2, bufferSize_Itr2); 

      Console.WriteLine("Encrypted: " + Encoding.Default.GetString(byteArray)); 

      // Text encrypted as byteArray, try to decrypt it! // 

      //CryptDecrypt - with input from CryptEncrypt". 
      Console.WriteLine(Crypt32.CryptDecrypt(handleSessionKey, IntPtr.Zero, 1, 0, byteArray, ref byteAmt_Itr2)); 

      //Convert decrypted byte array to string 
      char[] chars = new char[byteArray.Length/sizeof(char)]; 
      System.Buffer.BlockCopy(byteArray, 0, chars, 0, byteArray.Length); 
      string decryptedText = new string(chars); 
      Console.WriteLine("Decrypted: " + decryptedText); 
     } 
    } 

    public class Crypt32 
    { 
     [DllImport("advapi32.dll", SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptAcquireContext(
      ref IntPtr hProv, 
      string pszContainer, 
      string pszProvider, 
      uint dwProvType, 
      uint dwFlags); 

     [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptCreateHash(
      IntPtr hProv, 
      uint algId, 
      IntPtr hKey, 
      uint dwFlags, 
      ref IntPtr phHash); 

     [DllImport("advapi32.dll", SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptHashData(
      IntPtr hHash, 
      byte[] pbData, 
      uint dataLen, 
      uint flags); 

     [DllImport("advapi32.dll", SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptDeriveKey(
      IntPtr hProv, 
      int Algid, 
      IntPtr hBaseData, 
      int flags, 
      ref IntPtr phKey); 

     [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptEncrypt(
      IntPtr hKey, 
      IntPtr hHash, 
      int Final, 
      uint dwFlags, 
      byte[] pbData, 
      ref uint pdwDataLen, 
      uint dwBufLen); 

     [DllImport("advapi32.dll", SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptDecrypt(
      IntPtr hKey, 
      IntPtr hHash, 
      int Final, 
      uint dwFlags, 
      byte[] pbData, 
      ref uint pdwDataLen); 
    } 
} 

Выход:

Encrypted: 
B'♦tt'sô?*ý¢┼àò⌂9Z▼?£'$'¥«çæOÆà[/ë?·UÛÙªÄ2?┼[É{&IâínaÇe 
True 
Decrypted: Text to encrypt and decrypt! 

Я разобрал другие входы CryptDecrypt (сделал еще один handleSessionKey, а также другой переменным вместо byteAtm_Itr2), они не являются источником проблемы. Проблема, похоже, связана с массивом байтов.

Если я использую этот код (или Buffer.BlockCopy), чтобы скопировать массив дешифровка не удастся:

byte[] byteArray2 = new byte[byteArray.Length]; 
byteArray.CopyTo(byteArray2, 0); 

Массивы являются одинаковыми, но, конечно, не тот же объект. Не могу понять, почему это не сработает. Если я запускаю код с скопированного массива, вывод:

Encrypted: 
B'♦tt'sô?*ý¢┼àò⌂9Z▼?£'$'¥«çæOÆà[/ë?·UÛÙªÄ2?┼[É{&IâínaÇe 
False 
Decrypted: Text to encrypt and decr???? 
+0

Похоже, вы используете кодировку Юникода со стороны ввода, но пытаетесь напрямую наложить байты на символы на стороне вывода. Вам нужно использовать 'Encoding.Unicode.GetString (byteArray)', чтобы иметь любую надежду получить реальную строку из этих байтов. –

+1

С шифрованием не используйте кодировку Unicode, всегда используйте UTF8. – jdweng

+1

Полностью несвязанный, но вы _really_ должны использовать этот византийский API? В 'System.Cryptography' есть много хорошего. –

ответ

2

Поскольку используется Unicode, который использует два байта на символ, вы не оставляя достаточно места для оригинального byteArray. Вам нужно выделить в два раза больше места, чем Вы в данный момент делаете:

byte[] byteArray = new byte[2*plaintext.Length*sizeof (char)]; 

После изменения коды, вы можете спокойно скопировать массив и расшифровать скопированный массив. Кажется, что он может расшифровать исходный массив, поскольку библиотека Marshalled правильно распознала длину массива (которая была создана в первую очередь). Однако при использовании Array.Copy эти дополнительные байты не передаются кодом .NET.

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

using System.Runtime.InteropServices.WindowsRuntime; 
using System.Runtime.InteropServices; 
using System.IO; 


namespace EncryptDecryptApplication 
{ 
    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      //Text to encrypt 
      string plaintext = "Text to encrypt and decrypt!"; 

      // Password 
      const string password = "password"; 

      // Constants used in cryptography functions 
      const string MS_ENH_RSA_AES_PROV = "Microsoft Enhanced RSA and AES Cryptographic Provider"; 
       //Name of provider. Same as "Microsoft AES Cryptographic Provider". 
      const byte PROV_RSA_AES = 24; //Type of provider 
      const string KeyContainer = null; 
       //Name of the key container to be used, if NULL then a default key container name is used. Must be a null-terminated string. 
      const uint ALG_CLASS_HASH = (4 << 13); //32768 = 4*2^13; //Samma tror jag för alla hashalgoritmer 
      const uint ALG_TYPE_ANY = (0); //Samma tror jag för alla hashalgoritmer 
      const uint ALG_SID_SHA_256 = 12; 
       //ALG_SID_MD5 = 3, ALG_SID_SHA = 4, ALG_SID_SHA_256 = 12, ALG_SID_SHA_384 = 13, ALG_SID_SHA_512 = 14 
      const uint CALG_SHA_256 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256); 
      const int ALG_CLASS_DATA_ENCRYPT = 24576; 
      const int ALG_TYPE_BLOCK = 1536; //used in all types of AES, and in RC2 
      const int ALG_SID_AES_256 = 16; //ALG_SID_AES_128 = 14, ALG_SID_AES_192 = 15, ALG_SID_AES_256 = 16 
      const int CALG_AES_256 = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_256); 
      const int ENCRYPT_ALGORITHM = CALG_AES_256; 

      // Obtain handle to Cryptographic Service Provider (CSP) 
      string ProviderCSP = MS_ENH_RSA_AES_PROV + null; 
       //name of the CSP to be used. Must be a null-terminated string. 
      IntPtr CSPhandle = new IntPtr(); 
      Crypt32.CryptAcquireContext(ref CSPhandle, KeyContainer, ProviderCSP, PROV_RSA_AES, 0); 

      //Create hash object 
      IntPtr handleHashObj = new IntPtr(); 
      Crypt32.CryptCreateHash(CSPhandle, CALG_SHA_256, IntPtr.Zero, 0, ref handleHashObj); 

      //Hash password 
      byte[] pwByteArray = Encoding.Unicode.GetBytes(password); 
      uint pwByteAmt = (uint) ASCIIEncoding.Unicode.GetByteCount(password); 
      Crypt32.CryptHashData(handleHashObj, pwByteArray, pwByteAmt, 0); 

      //Dervie session key from the hashed password 
      IntPtr handleSessionKey = new IntPtr(); 
      Crypt32.CryptDeriveKey(CSPhandle, ENCRYPT_ALGORITHM, handleHashObj, 0, ref handleSessionKey); 

      //CryptEncrypt iteration no 1 - Obtain buffer size (output ByteAmt_Itr1) 
      byte[] byteArray = new byte[2*plaintext.Length*sizeof (char)]; 
      System.Buffer.BlockCopy(plaintext.ToCharArray(), 0, byteArray, 0, byteArray.Length/2); 

      uint byteAmt_Itr1 = (uint) byteArray.Length; //No of bytes, i.e. the size, of the plaintext. 
      uint bufferSize_Itr1 = byteAmt_Itr1; //Set buffer size to input data size for now 

      Crypt32.CryptEncrypt(handleSessionKey, IntPtr.Zero, 1, 0, null, ref byteAmt_Itr1, 0); 

      //CryptEncrypt iteration no 2 - Encryption 
      uint byteAmt_Itr2 = (uint) byteArray.Length; //No of bytes, i.e. the size, of the plaintext. 
      uint bufferSize_Itr2 = byteAmt_Itr1; 
       //Output from iteration no 1 - size of output data, i.e. correct buffer size 

      Crypt32.CryptEncrypt(handleSessionKey, IntPtr.Zero, 1, 0, byteArray, ref byteAmt_Itr2, bufferSize_Itr2); 

      Console.WriteLine("Encrypted: " + Encoding.Default.GetString(byteArray)); 

      // Text encrypted as byteArray, try to decrypt it! // 

      byte[] byteArray2 = new byte[byteArray.Length]; 
      byteArray.CopyTo(byteArray2, 0); 

      //CryptDecrypt - with input from CryptEncrypt". 
      Console.WriteLine(Crypt32.CryptDecrypt(handleSessionKey, IntPtr.Zero, 1, 0, byteArray2, ref byteAmt_Itr2)); 

      //Convert decrypted byte array to string 
      string decryptedText = Encoding.Unicode.GetString(byteArray2).Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries)[0]; 
      //char[] chars = new char[byteArray.Length/sizeof(char)]; 
      //System.Buffer.BlockCopy(byteArray, 0, chars, 0, byteArray.Length); 
      //string decryptedText = new string(chars); 
      Console.WriteLine("Decrypted: " + decryptedText); 
     } 
    } 

    public class Crypt32 
    { 
     [DllImport("advapi32.dll", SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptAcquireContext(
      ref IntPtr hProv, 
      string pszContainer, 
      string pszProvider, 
      uint dwProvType, 
      uint dwFlags); 

     [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptCreateHash(
      IntPtr hProv, 
      uint algId, 
      IntPtr hKey, 
      uint dwFlags, 
      ref IntPtr phHash); 

     [DllImport("advapi32.dll", SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptHashData(
      IntPtr hHash, 
      byte[] pbData, 
      uint dataLen, 
      uint flags); 

     [DllImport("advapi32.dll", SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptDeriveKey(
      IntPtr hProv, 
      int Algid, 
      IntPtr hBaseData, 
      int flags, 
      ref IntPtr phKey); 

     [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptEncrypt(
      IntPtr hKey, 
      IntPtr hHash, 
      int Final, 
      uint dwFlags, 
      byte[] pbData, 
      ref uint pdwDataLen, 
      uint dwBufLen); 

     [DllImport("advapi32.dll", SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool CryptDecrypt(
      IntPtr hKey, 
      IntPtr hHash, 
      int Final, 
      uint dwFlags, 
      byte[] pbData, 
      ref uint pdwDataLen); 
    } 
} 
+0

Я добавил полный код, включая копирование в byteArray2. Это правильно расшифровывает входную строку. – Jaco

+1

Поскольку вы используете UniCode, вы не резервируете достаточно места для 'byteArray'. Библиотеки Marshalled могут справиться с этим, но не Array.Copy – Jaco

+0

Возвращаемое значение по-прежнему остается ложным ... С немного большим количеством тестов я обнаружил, что результат кажется «ОК» для шифрования и дешифрования! но не с «Это текст для шифрования и дешифрования!». Почему это? – Oakgrove