///<summary> /// Creates the structure that holds byte[] data to be encrypted. ///</summary> ///<param name="data">Data to be encrypted.</param> ///<returns>Structure that holds byte[] data to be encrypted.</returns> ///<exception cref="MemberAccessException">Unable to allocate data buffer for BLOB structure</exception> public static DPAPINativeDATABLOB Init(byte[] data) { // Use empty array for null parameter. if (data == null) { data = new byte[0]; } var blob = new DPAPINativeDATABLOB { DataPointer = Marshal.AllocHGlobal(data.Length), DataLength = data.Length }; // Make sure that memory allocation was successful. // With the null check on the data parameter, I don't think this is needed. //if (blob.pbData == IntPtr.Zero) // throw new MemberAccessException("Unable to allocate data buffer for BLOB structure."); // Copy data from original source to the BLOB structure. Marshal.Copy(data, 0, blob.DataPointer, data.Length); return blob; }
///<summary> /// Performs encryption on the data in a <see cref="DPAPINativeDATABLOB"/> structure ///</summary> ///<param name="plainText">Structure that contains the plaintext to be encrypted.</param> ///<param name="description">A readable description of the data to be encrypted.</param> ///<param name="entropy">Structure that contains a password or other additional entropy used to encrypt the data.</param> ///<param name="reserved">Reserved for future use and must be set to NULL.</param> ///<param name="prompt">Structure that provides information about where and when prompts are to be displayed and what the content of those prompts should be.</param> ///<param name="flags">Crypt Protection</param> ///<param name="cipherText">Structure that receives the encrypted data.</param> ///<returns>If the function succeeds, then <c>TRUE</c> else <c>FALSE</c>.</returns> private static bool ProtectData(ref DPAPINativeDATABLOB plainText, string description, ref DPAPINativeDATABLOB entropy, IntPtr reserved, ref DPAPINativeCRYPTPROTECTPROMPTSTRUCT prompt, int flags, ref DPAPINativeDATABLOB cipherText) { return CryptProtectData(ref plainText, description, ref entropy, reserved, ref prompt, flags, ref cipherText); }
private static extern bool CryptUnprotectData(ref DPAPINativeDATABLOB cipherText, ref string description, ref DPAPINativeDATABLOB entropy, IntPtr reserved, ref DPAPINativeCRYPTPROTECTPROMPTSTRUCT prompt, int flags, ref DPAPINativeDATABLOB plainText);
private static byte[] DPAPIDecrypt(byte[] cipherText, byte[] entropy) { // Create BLOBs to hold data. var plainTextBlob = new DPAPINativeDATABLOB(); var cipherTextBlob = new DPAPINativeDATABLOB(); var entropyBlob = new DPAPINativeDATABLOB(); // We only need prompt structure because it is a required parameter. var prompt = DPAPINativeCRYPTPROTECTPROMPTSTRUCT.Default(); byte[] plainTextBytes; try { // Convert ciphertext bytes into a BLOB structure. cipherTextBlob = DPAPINativeDATABLOB.Init(cipherText); // Convert entropy bytes into a BLOB structure. entropyBlob = DPAPINativeDATABLOB.Init(entropy); // Initialize description string. string description = String.Empty; // Call DPAPI to decrypt data. bool success = UnprotectData(ref cipherTextBlob, ref description, ref entropyBlob, IntPtr.Zero, ref prompt, CRYPTPROTECT_UI_FORBIDDEN, 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 CryptographicException("CryptUnprotectData failed.", new Win32Exception(errCode)); } // Allocate memory to hold plaintext. plainTextBytes = new byte[plainTextBlob.DataLength]; // Copy ciphertext from the BLOB to a byte array. Marshal.Copy(plainTextBlob.DataPointer, plainTextBytes, 0, plainTextBlob.DataLength); } catch (Exception ex) { throw new CryptographicException("DPAPI was unable to decrypt data.", ex); } finally { // Free all memory allocated for BLOBs. plainTextBlob.Dispose(); cipherTextBlob.Dispose(); entropyBlob.Dispose(); prompt.Dispose(); } // Return the result. return plainTextBytes; }