// http://msdn.microsoft.com/en-us/library/windows/desktop/aa380882(v=vs.85).aspx
 internal static extern bool CryptUnprotectData(
     [In] DATA_BLOB *pDataIn,
     [In] IntPtr ppszDataDescr,
     [In] DATA_BLOB *pOptionalEntropy,
     [In] IntPtr pvReserved,
     [In] IntPtr pPromptStruct,
     [In] uint dwFlags,
     [Out] out DATA_BLOB pDataOut);
        internal static byte[] ProtectWithDpapiCore(byte* pbSecret, uint cbSecret, byte* pbOptionalEntropy, uint cbOptionalEntropy, bool fLocalMachine = false)
        {
            byte dummy; // provides a valid memory address if the secret or entropy has zero length

            DATA_BLOB dataIn = new DATA_BLOB()
            {
                cbData = cbSecret,
                pbData = (pbSecret != null) ? pbSecret : &dummy
            };
            DATA_BLOB entropy = new DATA_BLOB()
            {
                cbData = cbOptionalEntropy,
                pbData = (pbOptionalEntropy != null) ? pbOptionalEntropy : &dummy
            };
            DATA_BLOB dataOut = default(DATA_BLOB);

#if !DOTNET5_4
            RuntimeHelpers.PrepareConstrainedRegions();
#endif
            try
            {
                bool success = UnsafeNativeMethods.CryptProtectData(
                    pDataIn: &dataIn,
                    szDataDescr: IntPtr.Zero,
                    pOptionalEntropy: &entropy,
                    pvReserved: IntPtr.Zero,
                    pPromptStruct: IntPtr.Zero,
                    dwFlags: CRYPTPROTECT_UI_FORBIDDEN | ((fLocalMachine) ? CRYPTPROTECT_LOCAL_MACHINE : 0),
                    pDataOut: out dataOut);
                if (!success)
                {
                    int errorCode = Marshal.GetLastWin32Error();
                    throw new CryptographicException(errorCode);
                }

                int dataLength = checked((int)dataOut.cbData);
                byte[] retVal = new byte[dataLength];
                Marshal.Copy((IntPtr)dataOut.pbData, retVal, 0, dataLength);
                return retVal;
            }
            finally
            {
                // Free memory so that we don't leak.
                // FreeHGlobal actually calls LocalFree.
                if (dataOut.pbData != null)
                {
                    Marshal.FreeHGlobal((IntPtr)dataOut.pbData);
                }
            }
        }
        internal static Secret UnprotectWithDpapiCore(byte* pbProtectedData, uint cbProtectedData, byte* pbOptionalEntropy, uint cbOptionalEntropy)
        {
            byte dummy; // provides a valid memory address if the secret or entropy has zero length

            DATA_BLOB dataIn = new DATA_BLOB()
            {
                cbData = cbProtectedData,
                pbData = (pbProtectedData != null) ? pbProtectedData : &dummy
            };
            DATA_BLOB entropy = new DATA_BLOB()
            {
                cbData = cbOptionalEntropy,
                pbData = (pbOptionalEntropy != null) ? pbOptionalEntropy : &dummy
            };
            DATA_BLOB dataOut = default(DATA_BLOB);

#if !DOTNET5_4
            RuntimeHelpers.PrepareConstrainedRegions();
#endif
            try
            {
                bool success = UnsafeNativeMethods.CryptUnprotectData(
                    pDataIn: &dataIn,
                    ppszDataDescr: IntPtr.Zero,
                    pOptionalEntropy: &entropy,
                    pvReserved: IntPtr.Zero,
                    pPromptStruct: IntPtr.Zero,
                    dwFlags: CRYPTPROTECT_UI_FORBIDDEN,
                    pDataOut: out dataOut);
                if (!success)
                {
                    int errorCode = Marshal.GetLastWin32Error();
                    throw new CryptographicException(errorCode);
                }

                return new Secret(dataOut.pbData, checked((int)dataOut.cbData));
            }
            finally
            {
                // Zero and free memory so that we don't leak secrets.
                // FreeHGlobal actually calls LocalFree.
                if (dataOut.pbData != null)
                {
                    UnsafeBufferUtil.SecureZeroMemory(dataOut.pbData, dataOut.cbData);
                    Marshal.FreeHGlobal((IntPtr)dataOut.pbData);
                }
            }
        }