using System; using System.Runtime.InteropServices; using System.Text; using AxCopilot.Services; namespace AxCopilot.Handlers; public static class CredentialManager { private struct FILETIME { public uint dwLowDateTime; public uint dwHighDateTime; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct CREDENTIAL { public uint Flags; public uint Type; public nint TargetName; public nint Comment; public FILETIME LastWritten; public uint CredentialBlobSize; public nint CredentialBlob; public uint Persist; public uint AttributeCount; public nint Attributes; public nint TargetAlias; public nint UserName; } private const uint CRED_TYPE_GENERIC = 1u; private const uint CRED_PERSIST_LOCAL_MACHINE = 2u; [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool CredRead(string target, uint type, uint flags, out nint credential); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool CredWrite([In] ref CREDENTIAL userCredential, uint flags); [DllImport("advapi32.dll", SetLastError = true)] private static extern void CredFree(nint cred); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool CredDelete(string target, uint type, uint flags); public static string? GetToken(string key) { if (string.IsNullOrEmpty(key)) { return null; } try { if (CredRead(key, 1u, 0u, out var credential)) { try { CREDENTIAL cREDENTIAL = Marshal.PtrToStructure(credential); if (cREDENTIAL.CredentialBlobSize != 0 && cREDENTIAL.CredentialBlob != IntPtr.Zero) { return Marshal.PtrToStringUni(cREDENTIAL.CredentialBlob, (int)cREDENTIAL.CredentialBlobSize / 2); } } finally { CredFree(credential); } } } catch (Exception ex) { LogService.Warn("Windows Credential Manager 읽기 실패 (" + key + "): " + ex.Message); } return Environment.GetEnvironmentVariable(key.ToUpperInvariant()); } public static void SetToken(string key, string token) { if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(token)) { return; } byte[] bytes = Encoding.Unicode.GetBytes(token); nint num = Marshal.AllocHGlobal(bytes.Length); nint num2 = Marshal.StringToCoTaskMemUni(key); nint num3 = Marshal.StringToCoTaskMemUni(Environment.UserName); try { Marshal.Copy(bytes, 0, num, bytes.Length); CREDENTIAL userCredential = new CREDENTIAL { Type = 1u, TargetName = num2, UserName = num3, CredentialBlobSize = (uint)bytes.Length, CredentialBlob = num, Persist = 2u }; if (!CredWrite(ref userCredential, 0u)) { LogService.Error($"토큰 저장 실패: {key}, 오류 코드: {Marshal.GetLastWin32Error()}"); } else { LogService.Info("토큰 저장 완료: " + key); } } finally { Marshal.FreeHGlobal(num); Marshal.FreeCoTaskMem(num2); Marshal.FreeCoTaskMem(num3); } } public static bool DeleteToken(string key) { return !string.IsNullOrEmpty(key) && CredDelete(key, 1u, 0u); } }