无法使用C#中的CryptEncrypt / CryptDecrypt解密

时间:2016-01-19 14:16:43

标签: c# encryption bytearray

我做了一个小应用程序来加密和解密一些文本。只要我直接使用加密的字节数组,一切都很好。但是,只要我复制数组以模仿将加密文本作为文件发送的过程,解密就不会运行。

为什么我无法使用复制的数组运行解密?

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????

1 个答案:

答案 0 :(得分:3)

因为您使用的是Unicode,每个字符使用两个字节,所以您没有为原始 byte[] byteArray = new byte[2*plaintext.Length*sizeof (char)]; 保留足够的空间。您需要分配两倍于当前的空间:

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);
    }
}

更改此代码后,您可以安全地复制阵列并解密复制的阵列。似乎可以解密原始数组,因为Marshalled库正确识别了数组长度(它首先创建的)。但是,使用Array.Copy时,这些额外的字节不会被.NET代码传输。

    function move(event) {
    var x = event.pageX,
      y = event.pageY,
      labelPadding = [5, 5, 5, 5],
      minX = chart.plotLeft,
      maxX = minX + chart.plotWidth,
      minY = chart.plotTop,
      maxY = minY + chart.plotHeight,
      path = ['M', chart.plotLeft, y,
        'L', chart.plotLeft + chart.plotWidth, y,
        'M', x, chart.plotTop,
        'L', x, chart.plotTop + chart.plotHeight
      ],
      bbox, bbox2;

    if (chart.crossLines && chart.crossLabel1 && chart.crossLabel2 && (x < minX || x > maxX || y > maxY || y < minY)) {
      chart.crossLines.hide();
      chart.crossLabel1.hide();
      chart.crossLabel2.hide();
      chart.crossLabel1bck.hide();
      chart.crossLabel2bck.hide();
    } else {

      if (chart.crossLines) {
        // update lines
        chart.crossLines.attr({
          d: path
        }).show();
      } else {
        // draw lines
        chart.crossLines = chart.renderer.path(path).attr({
          'stroke-width': 0.5,
          stroke: 'black',
          dashstyle: 'Dash',
          zIndex: 10
        }).add();
      }

      if (chart.crossLabel1) {
        // update label
        chart.crossLabel1.attr({
          zIndex: 10,
          y: y + 5,
          text: chart.yAxis[0].toValue(y).toFixed(2)
        }).show();

        bbox = chart.crossLabel1.getBBox();
        //background
        chart.crossLabel1bck.attr({
            x: bbox.x - labelPadding[3],
            y: bbox.y - labelPadding[0],
            width: bbox.width + labelPadding[1] + labelPadding[3],
            height: bbox.height + labelPadding[0] + labelPadding[2]
          })
          .show();

      } else {
        // draw label
        chart.crossLabel1 = chart.renderer.text(chart.yAxis[0].toValue(y).toFixed(2), chart.plotLeft - 56, y + 5).css({
          color: 'white'
        }).add();

        bbox = chart.crossLabel1.getBBox();

        //background
        chart.crossLabel1bck = chart.renderer.rect(bbox.x - labelPadding[3], y + 5 - labelPadding[0], bbox.width + labelPadding[1], bbox.height + labelPadding[2]).attr({
            fill: 'black',
            'stroke': 'black',
            'stroke-width': 1,
            zIndex: 9
          })
          .add();
      }

      if (chart.crossLabel2) {
        // update label
        chart.crossLabel2.attr({
          zIndex: 10,
          x: x - 13,
          text: chart.xAxis[0].toValue(x).toFixed(2)
        }).show();

        bbox2 = chart.crossLabel2.getBBox();
        //background
        chart.crossLabel2bck.attr({
          x: bbox2.x - labelPadding[3],
          y: bbox2.y - labelPadding[0],
          width: bbox2.width + labelPadding[1] + labelPadding[3],
          height: bbox2.height + labelPadding[0] + labelPadding[2]
        }).show();
      } else {
        // draw label
        chart.crossLabel2 = chart.renderer.text(chart.xAxis[0].toValue(x).toFixed(2), chart.plotBottom, chart.plotTop + chart.plotHeight + 12 + labelPadding[0]).css({
          color: 'white'
        }).add();

        bbox2 = chart.crossLabel2.getBBox();
        //background
        chart.crossLabel2bck = chart.renderer.rect(bbox.x - labelPadding[3], chart.plotTop + chart.plotHeight - labelPadding[0], bbox.width + labelPadding[1], bbox.height + labelPadding[2]).attr({
            fill: 'black',
            'stroke': 'black',
            'stroke-width': 1,
            zIndex: 9
          })
          .add();
      }
    }
  }