using System.ComponentModel; using System.Runtime.InteropServices; namespace MusicBridge.Agent.Network; // Windows DPAPI, scoped to the signed-in user. No UI or machine-wide key. internal static class UserProtection { public static byte[] Protect(byte[] bytes) => Transform(bytes, true); public static byte[] Unprotect(byte[] bytes) => Transform(bytes, false); private static byte[] Transform(byte[] bytes, bool protect) { var input = new Blob { Size = bytes.Length, Data = Marshal.AllocHGlobal(bytes.Length) }; var output = new Blob(); try { Marshal.Copy(bytes, 0, input.Data, bytes.Length); var ok = protect ? CryptProtectData(ref input, null, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 1, out output) : CryptUnprotectData(ref input, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 1, out output); if (!ok) throw new Win32Exception(Marshal.GetLastWin32Error(), "Не удалось открыть защищённое хранилище Windows."); var result = new byte[output.Size]; Marshal.Copy(output.Data, result, 0, output.Size); return result; } finally { Marshal.Copy(new byte[input.Size], 0, input.Data, input.Size); Marshal.FreeHGlobal(input.Data); if (output.Data != IntPtr.Zero) { Marshal.Copy(new byte[output.Size], 0, output.Data, output.Size); LocalFree(output.Data); } } } [StructLayout(LayoutKind.Sequential)] private struct Blob { public int Size; public IntPtr Data; } [DllImport("crypt32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CryptProtectData(ref Blob input, string? description, IntPtr entropy, IntPtr reserved, IntPtr prompt, uint flags, out Blob output); [DllImport("crypt32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CryptUnprotectData(ref Blob input, IntPtr description, IntPtr entropy, IntPtr reserved, IntPtr prompt, uint flags, out Blob output); [DllImport("kernel32.dll")] private static extern IntPtr LocalFree(IntPtr memory); }