Example #1
0
        /// <summary>
        ///     Initializes a BLOB structure from a byte array.
        /// </summary>
        /// <param name="data">Original data in a byte array format.</param>
        /// <param name="blob">Returned blob structure.</param>
        private static void InitBlob(byte[] data, ref NativeMethods.DATA_BLOB blob)
        {
            // Use empty array for null parameter.
            if (data == null)
            {
                data = new byte[0];
            }

            // Allocate memory for the BLOB data.
            blob.pbData = Marshal.AllocHGlobal(data.Length);

            // Make sure that memory allocation was successful.
            if (blob.pbData == IntPtr.Zero)
            {
                throw new Exception("Unable to allocate data buffer for BLOB structure.");
            }

            // Specify number of bytes in the BLOB.
            blob.cbData = data.Length;

            // Copy data from original source to the BLOB structure.
            Marshal.Copy(data, 0, blob.pbData, data.Length);
        }
Example #2
0
        /// <summary>
        ///     Calls DPAPI CryptUnprotectData to decrypt ciphertext bytes.
        /// </summary>
        /// <param name="cipherTextBytes">Encrypted data.</param>
        /// <param name="entropyBytes">
        ///     Optional entropy, which is required if it was specified during encryption.
        /// </param>
        /// <param name="description">Returned description of data specified during encryption.</param>
        /// <returns>Decrypted data bytes.</returns>
        /// <remarks>
        ///     When decrypting data, it is not necessary to specify which type of encryption key to
        ///     use: user-specific or machine-specific; DPAPI will figure it out by looking at the
        ///     signature of encrypted data.
        /// </remarks>
        public static byte[] Decrypt(byte[] cipherTextBytes, byte[] entropyBytes, out string description)
        {
            // Create BLOBs to hold data.
            NativeMethods.DATA_BLOB plainTextBlob  = new NativeMethods.DATA_BLOB();
            NativeMethods.DATA_BLOB cipherTextBlob = new NativeMethods.DATA_BLOB();
            NativeMethods.DATA_BLOB entropyBlob    = new NativeMethods.DATA_BLOB();

            // We only need prompt structure because it is a required parameter.
            NativeMethods.CRYPTPROTECT_PROMPTSTRUCT prompt =
                new NativeMethods.CRYPTPROTECT_PROMPTSTRUCT();
            InitPrompt(ref prompt);

            // Initialize description string.
            description = string.Empty;

            try
            {
                // Convert ciphertext bytes into a BLOB structure.
                try
                {
                    InitBlob(cipherTextBytes, ref cipherTextBlob);
                }
                catch (Exception ex)
                {
                    throw new Exception("Cannot initialize ciphertext BLOB.", ex);
                }

                // Convert entropy bytes into a BLOB structure.
                try
                {
                    InitBlob(entropyBytes, ref entropyBlob);
                }
                catch (Exception ex)
                {
                    throw new Exception("Cannot initialize entropy BLOB.", ex);
                }

                // Disable any types of UI. CryptUnprotectData does not mention
                // CryptprotectLocalMachine flag in the list of supported flags so we will not set it up.
                const int flags = CryptprotectUIForbidden;

                // Call DPAPI to decrypt data.
                bool success = NativeMethods.CryptUnprotectData(ref cipherTextBlob,
                                                                ref description,
                                                                ref entropyBlob,
                                                                IntPtr.Zero,
                                                                ref prompt,
                                                                flags,
                                                                ref plainTextBlob);

                // Check the result.
                if (!success)
                {
                    // If operation failed, retrieve last Win32 error.
                    int errCode = Marshal.GetLastWin32Error();

                    // Win32Exception will contain error message corresponding to the Windows error code.
                    throw new Exception("CryptUnprotectData failed.", new Win32Exception(errCode));
                }

                // Allocate memory to hold plaintext.
                byte[] plainTextBytes = new byte[plainTextBlob.cbData];

                // Copy ciphertext from the BLOB to a byte array.
                Marshal.Copy(plainTextBlob.pbData, plainTextBytes, 0, plainTextBlob.cbData);

                // Return the result.
                return(plainTextBytes);
            }
            catch (Exception ex)
            {
                throw new Exception("DPAPI was unable to decrypt data.", ex);
            }

            // Free all memory allocated for BLOBs.
            finally
            {
                if (plainTextBlob.pbData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(plainTextBlob.pbData);
                }

                if (cipherTextBlob.pbData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(cipherTextBlob.pbData);
                }

                if (entropyBlob.pbData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(entropyBlob.pbData);
                }
            }
        }
Example #3
0
        /// <summary>
        ///     Calls DPAPI CryptProtectData function to encrypt an array of plaintext bytes.
        /// </summary>
        /// <param name="keyType">
        ///     Defines type of encryption key to use. When user key is specified, any application
        ///     running under the same user account as the one making this call, will be able to decrypt
        ///     data. Machine key will allow any application running on the same computer where data were
        ///     encrypted to perform decryption.
        ///     Note: If optional entropy is specifed, it will be required for decryption.
        /// </param>
        /// <param name="plainTextBytes">Plaintext data to be encrypted.</param>
        /// <param name="entropyBytes">
        ///     Optional entropy which - if specified - will be required to perform decryption.
        /// </param>
        /// <param name="description">
        ///     Optional description of data to be encrypted. If this value is specified, it will be
        ///     stored along with encrypted data and returned as a separate value during decryption.
        /// </param>
        /// <returns>Encrypted value.</returns>
        public static byte[] Encrypt(KeyType keyType, byte[] plainTextBytes, byte[] entropyBytes, string description)
        {
            // Make sure that parameters are valid.
            if (plainTextBytes == null)
            {
                plainTextBytes = new byte[0];
            }
            if (entropyBytes == null)
            {
                entropyBytes = new byte[0];
            }
            if (description == null)
            {
                description = string.Empty;
            }

            // Create BLOBs to hold data.
            NativeMethods.DATA_BLOB plainTextBlob  = new NativeMethods.DATA_BLOB();
            NativeMethods.DATA_BLOB cipherTextBlob = new NativeMethods.DATA_BLOB();
            NativeMethods.DATA_BLOB entropyBlob    = new NativeMethods.DATA_BLOB();

            // We only need prompt structure because it is a required parameter.
            NativeMethods.CRYPTPROTECT_PROMPTSTRUCT prompt =
                new NativeMethods.CRYPTPROTECT_PROMPTSTRUCT();
            InitPrompt(ref prompt);

            try
            {
                // Convert plaintext bytes into a BLOB structure.
                try
                {
                    InitBlob(plainTextBytes, ref plainTextBlob);
                }
                catch (Exception ex)
                {
                    throw new Exception("Cannot initialize plaintext BLOB.", ex);
                }

                // Convert entropy bytes into a BLOB structure.
                try
                {
                    InitBlob(entropyBytes, ref entropyBlob);
                }
                catch (Exception ex)
                {
                    throw new Exception("Cannot initialize entropy BLOB.", ex);
                }

                // Disable any types of UI.
                int flags = CryptprotectUIForbidden;

                // When using machine-specific key, set up machine flag.
                if (keyType == KeyType.MachineKey)
                {
                    flags |= CryptprotectLocalMachine;
                }

                // Call DPAPI to encrypt data.
                bool success = NativeMethods.CryptProtectData(ref plainTextBlob,
                                                              description,
                                                              ref entropyBlob,
                                                              IntPtr.Zero,
                                                              ref prompt,
                                                              flags,
                                                              ref cipherTextBlob);

                // Check the result.
                if (!success)
                {
                    // If operation failed, retrieve last Win32 error.
                    int errCode = Marshal.GetLastWin32Error();

                    // Win32Exception will contain error message corresponding to the Windows error code.
                    throw new Exception("CryptProtectData failed.", new Win32Exception(errCode));
                }

                // Allocate memory to hold ciphertext.
                byte[] cipherTextBytes = new byte[cipherTextBlob.cbData];

                // Copy ciphertext from the BLOB to a byte array.
                Marshal.Copy(cipherTextBlob.pbData, cipherTextBytes, 0, cipherTextBlob.cbData);

                // Return the result.
                return(cipherTextBytes);
            }
            catch (Exception ex)
            {
                throw new Exception("DPAPI was unable to encrypt data.", ex);
            }

            // Free all memory allocated for BLOBs.
            finally
            {
                if (plainTextBlob.pbData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(plainTextBlob.pbData);
                }

                if (cipherTextBlob.pbData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(cipherTextBlob.pbData);
                }

                if (entropyBlob.pbData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(entropyBlob.pbData);
                }
            }
        }