using System.ComponentModel; using System.Runtime.InteropServices; namespace HaloPixelToolBox.Backend.Utilities; /// /// 使用 Windows DPAPI 加密仅允许当前 Windows 用户解密的本地数据。 /// internal static partial class WindowsDataProtection { private const int CryptProtectUiForbidden = 0x1; public static byte[] Protect(byte[] data, byte[] entropy) => Transform(data, entropy, protect: true); public static byte[] Unprotect(byte[] data, byte[] entropy) => Transform(data, entropy, protect: false); private static byte[] Transform(byte[] data, byte[] entropy, bool protect) { var inputBlob = CreateBlob(data); var entropyBlob = CreateBlob(entropy); DataBlob outputBlob = default; try { var succeeded = protect ? CryptProtectData(ref inputBlob, null, ref entropyBlob, IntPtr.Zero, IntPtr.Zero, CryptProtectUiForbidden, out outputBlob) : CryptUnprotectData(ref inputBlob, IntPtr.Zero, ref entropyBlob, IntPtr.Zero, IntPtr.Zero, CryptProtectUiForbidden, out outputBlob); if (!succeeded) throw new Win32Exception(Marshal.GetLastWin32Error()); var output = new byte[outputBlob.Size]; Marshal.Copy(outputBlob.Data, output, 0, output.Length); return output; } finally { FreeInputBlob(ref inputBlob); FreeInputBlob(ref entropyBlob); if (outputBlob.Data != IntPtr.Zero) LocalFree(outputBlob.Data); } } private static DataBlob CreateBlob(byte[] data) { var blob = new DataBlob { Size = data.Length }; if (data.Length == 0) return blob; blob.Data = Marshal.AllocHGlobal(data.Length); Marshal.Copy(data, 0, blob.Data, data.Length); return blob; } private static void FreeInputBlob(ref DataBlob blob) { if (blob.Data == IntPtr.Zero) return; Marshal.FreeHGlobal(blob.Data); blob = default; } [StructLayout(LayoutKind.Sequential)] private struct DataBlob { public int Size; public IntPtr Data; } [LibraryImport("Crypt32.dll", EntryPoint = "CryptProtectData", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] [return: MarshalAs(UnmanagedType.Bool)] private static partial bool CryptProtectData( ref DataBlob dataIn, string? description, ref DataBlob optionalEntropy, IntPtr reserved, IntPtr promptStruct, int flags, out DataBlob dataOut); [LibraryImport("Crypt32.dll", EntryPoint = "CryptUnprotectData", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static partial bool CryptUnprotectData( ref DataBlob dataIn, IntPtr description, ref DataBlob optionalEntropy, IntPtr reserved, IntPtr promptStruct, int flags, out DataBlob dataOut); [LibraryImport("Kernel32.dll", EntryPoint = "LocalFree")] private static partial IntPtr LocalFree(IntPtr memory); }