/// <summary> /// Initializes empty prompt structure. /// </summary> /// <param name="ps">Prompt parameter (which we do not actually need).</param> private static void InitPrompt(ref NativeMethods.CRYPTPROTECT_PROMPTSTRUCT ps) { ps.cbSize = Marshal.SizeOf(typeof(NativeMethods.CRYPTPROTECT_PROMPTSTRUCT)); ps.dwPromptFlags = 0; ps.hwndApp = NullPtr; ps.szPrompt = null; }
/// <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); } } }
/// <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); } } }