private byte[] ProtectWithDpapi(byte *pbSecret, uint cbSecret, bool fLocalMachine = false) { byte dummy; // provides a valid memory address if the secret or entropy has zero length var dataIn = new DATA_BLOB { cbData = cbSecret, pbData = pbSecret != null ? pbSecret : &dummy }; var dataOut = default(DATA_BLOB); RuntimeHelpers.PrepareConstrainedRegions(); try { var success = Crypt32.CryptProtectData( &dataIn, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, Crypt32.CRYPTPROTECT_UI_FORBIDDEN | (fLocalMachine ? Crypt32.CRYPTPROTECT_LOCAL_MACHINE : 0), out dataOut); if (!success) { throw new CryptographicException(Marshal.GetLastWin32Error()); } var dataLength = checked ((int)dataOut.cbData); var buffer = new byte[dataLength]; Marshal.Copy((IntPtr)dataOut.pbData, buffer, 0, dataLength); return(buffer); } finally { if (dataOut.pbData != null) { Marshal.FreeHGlobal((IntPtr)dataOut.pbData); } } }
internal static void SaveProtectedData([NotNull] SecureString secureString, [NotNull] string fileName) { if (string.IsNullOrWhiteSpace(fileName)) { throw new ArgumentException(@"File name is not valid", nameof(fileName)); } // Generate entropy var entropy = new byte[20]; using (var rng = new RNGCryptoServiceProvider()) { rng.GetBytes(entropy); } IntPtr entropyPtr = Marshal.AllocHGlobal(entropy.Length); Marshal.Copy(entropy, 0, entropyPtr, entropy.Length); // Get a BSTR from the SecureString // NOTE: A BSTR is a Unicode (UTF-16) string, which could contain multiple NULL characters IntPtr unmanagedString = Marshal.SecureStringToBSTR(secureString); try { var dataBlob = new DataBlob { cbData = secureString.Length * 2, pbData = unmanagedString }; var entropyBlob = new DataBlob { cbData = entropy.Length, pbData = entropyPtr }; var outBlob = new DataBlob(); // Crypt bool success = Crypt32.CryptProtectData(ref dataBlob, null, ref entropyBlob, IntPtr.Zero, IntPtr.Zero, CryptProtectFlags.CRYPTPROTECT_LOCAL_MACHINE, ref outBlob); if (!success) { int error = Marshal.GetLastWin32Error(); logger.Error($"CryptProtectData failed with error code {error}!"); } // Save to file try { var cryptedData = new byte[outBlob.cbData]; Marshal.Copy(outBlob.pbData, cryptedData, 0, cryptedData.Length); SaveProtectedDataInternal(cryptedData, entropy, fileName); } finally { Marshal.FreeHGlobal(outBlob.pbData); } } finally { Marshal.FreeHGlobal(entropyPtr); Marshal.ZeroFreeBSTR(unmanagedString); } }