简体   繁体   English

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

[英]Unable to decrypt with CryptEncrypt/CryptDecrypt in C#

I've made a small application to encrypt and decrypt some text. 我做了一个小应用程序来加密和解密一些文本。 All is fine as long as I use the byte array straight from the encryption. 只要我直接使用加密的字节数组,一切都很好。 But as soon as I make a copy of the array to mimic the process of sending the encrypted text as a file the decryption will not run. 但是,只要我复制数组以模仿将加密文本作为文件发送的过程,解密就不会运行。

Why am I unable the run the decryption using a copied array? 为什么我无法使用复制的数组运行解密?

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

Output: 输出:

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

I've sorted out the other inputs to CryptDecrypt (have made another handleSessionKey and also another variable instead of byteAtm_Itr2), they are not the source of the problem. 我已经整理了CryptDecrypt的其他输入(已经创建了另一个handleSessionKey以及另一个变量而不是byteAtm_Itr2),它们不是问题的根源。 The problem seems to be with the byte array. 问题似乎与字节数组有关。

If I use this code (or Buffer.BlockCopy) to copy the array the decryption will fail: 如果我使用此代码(或Buffer.BlockCopy)来复制数组,则解密将失败:

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

The arrays are the same but of course not the same object. 数组是相同的,但当然不是同一个对象。 Can't work out why this wouldn't work. 无法解决为什么这不起作用。 If I run the code with the copied array this is the output: 如果我用复制的数组运行代码,这是输出:

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

Because you are using Unicode, that uses two bytes per character, you are not reserving enough space for the original byteArray . 因为您使用的是Unicode,每个字符使用两个字节,所以您没有为原始byteArray保留足够的空间。 You need to allocate twice as much space as your currently doing: 您需要分配两倍于当前的空间:

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

After changing this code, you can safely copy the array and decrypt the copied array. 更改此代码后,您可以安全地复制数组并解密复制的数组。 It seems can decrypt the original array as the Marshalled library correctly recognised the array length (that it created in the first place). 似乎可以解密原始数组,因为Marshalled库正确识别了数组长度(它首先创建的)。 However, when using Array.Copy these additional bytes do not get transferred by the .NET code. 但是,使用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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM