/// <summary>
        /// Protect
        /// </summary>
        public static byte[] Protect(byte[] userData, byte[] optionalEntropy, DataProtectionScope scope)
        {
            if (userData == null)
            {
                throw new ArgumentNullException("userData");
            }

            GCHandle pbDataIn         = new GCHandle();
            GCHandle pOptionalEntropy = new GCHandle();

            CAPI.CRYPTOAPI_BLOB blob = new CAPI.CRYPTOAPI_BLOB();

            try
            {
                pbDataIn = GCHandle.Alloc(userData, GCHandleType.Pinned);
                CAPI.CRYPTOAPI_BLOB dataIn = new CAPI.CRYPTOAPI_BLOB();
                dataIn.cbData = (uint)userData.Length;
                dataIn.pbData = pbDataIn.AddrOfPinnedObject();
                CAPI.CRYPTOAPI_BLOB entropy = new CAPI.CRYPTOAPI_BLOB();
                if (optionalEntropy != null)
                {
                    pOptionalEntropy = GCHandle.Alloc(optionalEntropy, GCHandleType.Pinned);
                    entropy.cbData   = (uint)optionalEntropy.Length;
                    entropy.pbData   = pOptionalEntropy.AddrOfPinnedObject();
                }
                uint dwFlags = CAPI.CRYPTPROTECT_UI_FORBIDDEN;
                if (scope == DataProtectionScope.LocalMachine)
                {
                    dwFlags |= CAPI.CRYPTPROTECT_LOCAL_MACHINE;
                }
                unsafe
                {
                    if (!CAPI.CryptProtectData(new IntPtr(&dataIn),
                                               String.Empty,
                                               new IntPtr(&entropy),
                                               IntPtr.Zero,
                                               IntPtr.Zero,
                                               dwFlags,
                                               new IntPtr(&blob)))
                    {
                        int lastWin32Error = Marshal.GetLastWin32Error();

                        // One of the most common reasons that DPAPI operations fail is that the user
                        // profile is not loaded (for instance in the case of impersonation or running in a
                        // service.  In those cases, throw an exception that provides more specific details
                        // about what happened.
                        if (CAPI.ErrorMayBeCausedByUnloadedProfile(lastWin32Error))
                        {
                            throw new CryptographicException("Cryptography_DpApi_ProfileMayNotBeLoaded");
                        }
                        else
                        {
                            throw new CryptographicException(lastWin32Error);
                        }
                    }
                }

                // In some cases, the API would fail due to OOM but simply return a null pointer.
                if (blob.pbData == IntPtr.Zero)
                {
                    throw new OutOfMemoryException();
                }

                byte[] encryptedData = new byte[(int)blob.cbData];
                Marshal.Copy(blob.pbData, encryptedData, 0, encryptedData.Length);

                return(encryptedData);
            }
            finally
            {
                if (pbDataIn.IsAllocated)
                {
                    pbDataIn.Free();
                }
                if (pOptionalEntropy.IsAllocated)
                {
                    pOptionalEntropy.Free();
                }
                if (blob.pbData != IntPtr.Zero)
                {
                    CAPI.ZeroMemory(blob.pbData, blob.cbData);
                    CAPI.LocalFree(blob.pbData);
                }
            }
        }
        /// <summary>
        /// Unprotect
        /// </summary>
        public static byte[] Unprotect(byte[] encryptedData, byte[] optionalEntropy, DataProtectionScope scope)
        {
#if UNIX
            throw new PlatformNotSupportedException(Serialization.DeserializeSecureStringNotSupported);
#else
            if (encryptedData == null)
            {
                throw new ArgumentNullException("encryptedData");
            }

            GCHandle            pbDataIn         = new GCHandle();
            GCHandle            pOptionalEntropy = new GCHandle();
            CAPI.CRYPTOAPI_BLOB userData         = new CAPI.CRYPTOAPI_BLOB();

            try
            {
                pbDataIn = GCHandle.Alloc(encryptedData, GCHandleType.Pinned);
                CAPI.CRYPTOAPI_BLOB dataIn = new CAPI.CRYPTOAPI_BLOB();
                dataIn.cbData = (uint)encryptedData.Length;
                dataIn.pbData = pbDataIn.AddrOfPinnedObject();
                CAPI.CRYPTOAPI_BLOB entropy = new CAPI.CRYPTOAPI_BLOB();
                if (optionalEntropy != null)
                {
                    pOptionalEntropy = GCHandle.Alloc(optionalEntropy, GCHandleType.Pinned);
                    entropy.cbData   = (uint)optionalEntropy.Length;
                    entropy.pbData   = pOptionalEntropy.AddrOfPinnedObject();
                }
                uint dwFlags = CAPI.CRYPTPROTECT_UI_FORBIDDEN;
                if (scope == DataProtectionScope.LocalMachine)
                {
                    dwFlags |= CAPI.CRYPTPROTECT_LOCAL_MACHINE;
                }
                unsafe
                {
                    if (!CAPI.CryptUnprotectData(new IntPtr(&dataIn),
                                                 IntPtr.Zero,
                                                 new IntPtr(&entropy),
                                                 IntPtr.Zero,
                                                 IntPtr.Zero,
                                                 dwFlags,
                                                 new IntPtr(&userData)))
                    {
                        throw new CryptographicException(Marshal.GetLastWin32Error());
                    }
                }

                // In some cases, the API would fail due to OOM but simply return a null pointer.
                if (userData.pbData == IntPtr.Zero)
                {
                    throw new OutOfMemoryException();
                }

                byte[] data = new byte[(int)userData.cbData];
                Marshal.Copy(userData.pbData, data, 0, data.Length);

                return(data);
            }
            finally
            {
                if (pbDataIn.IsAllocated)
                {
                    pbDataIn.Free();
                }
                if (pOptionalEntropy.IsAllocated)
                {
                    pOptionalEntropy.Free();
                }
                if (userData.pbData != IntPtr.Zero)
                {
                    CAPI.ZeroMemory(userData.pbData, userData.cbData);
                    CAPI.LocalFree(userData.pbData);
                }
            }
#endif
        }
        /// <summary>
        /// Unprotect.
        /// </summary>
        public static byte[] Unprotect(byte[] encryptedData, byte[] optionalEntropy, DataProtectionScope scope)
        {
            if (encryptedData is null)
            {
                throw new ArgumentNullException(nameof(encryptedData));
            }

            GCHandle pbDataIn         = new GCHandle();
            GCHandle pOptionalEntropy = new GCHandle();

            CAPI.CRYPTOAPI_BLOB userData = new CAPI.CRYPTOAPI_BLOB();

            try
            {
                pbDataIn = GCHandle.Alloc(encryptedData, GCHandleType.Pinned);
                CAPI.CRYPTOAPI_BLOB dataIn = new CAPI.CRYPTOAPI_BLOB();
                dataIn.cbData = (uint)encryptedData.Length;
                dataIn.pbData = pbDataIn.AddrOfPinnedObject();
                CAPI.CRYPTOAPI_BLOB entropy = new CAPI.CRYPTOAPI_BLOB();
                if (optionalEntropy != null)
                {
                    pOptionalEntropy = GCHandle.Alloc(optionalEntropy, GCHandleType.Pinned);
                    entropy.cbData   = (uint)optionalEntropy.Length;
                    entropy.pbData   = pOptionalEntropy.AddrOfPinnedObject();
                }

                uint dwFlags = CAPI.CRYPTPROTECT_UI_FORBIDDEN;
                if (scope == DataProtectionScope.LocalMachine)
                {
                    dwFlags |= CAPI.CRYPTPROTECT_LOCAL_MACHINE;
                }

                unsafe
                {
                    if (!CAPI.CryptUnprotectData(
                            pDataIn: new IntPtr(&dataIn),
                            ppszDataDescr: IntPtr.Zero,
                            pOptionalEntropy: new IntPtr(&entropy),
                            pvReserved: IntPtr.Zero,
                            pPromptStruct: IntPtr.Zero,
                            dwFlags: dwFlags,
                            pDataBlob: new IntPtr(&userData)))
                    {
                        throw new CryptographicException(Marshal.GetLastWin32Error());
                    }
                }

                // In some cases, the API would fail due to OOM but simply return a null pointer.
                if (userData.pbData == IntPtr.Zero)
                {
                    throw new OutOfMemoryException();
                }

                byte[] data = new byte[(int)userData.cbData];
                Marshal.Copy(userData.pbData, data, 0, data.Length);

                return(data);
            }
            finally
            {
                if (pbDataIn.IsAllocated)
                {
                    pbDataIn.Free();
                }
                if (pOptionalEntropy.IsAllocated)
                {
                    pOptionalEntropy.Free();
                }
                if (userData.pbData != IntPtr.Zero)
                {
                    CAPI.ZeroMemory(userData.pbData, userData.cbData);
                    CAPI.LocalFree(userData.pbData);
                }
            }
        }