using System.ComponentModel;
using System.Runtime.InteropServices;
namespace HaloPixelToolBox.Backend.Utilities;
/// <summary>
/// 使用 Windows DPAPI 加密仅允许当前 Windows 用户解密的本地数据。
/// </summary>
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);
}
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace HaloPixelToolBox.Backend.Utilities;
/// <summary>
/// 使用 Windows DPAPI 加密仅允许当前 Windows 用户解密的本地数据。
/// </summary>
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);
}