Шифрование учетных данных в приложении WPF

В приложении WPF я хотел бы предоставить типичную опцию «Запомнить меня», чтобы помнить учетные данные и использовать их автоматически при следующем запуске приложения.

Использование одностороннего hashа явно не является опцией, и, хотя я могу хранить учетные данные в изолированном хранилище или в реестре , есть одна проблема, с которой приходится иметь дело при шифровании учетных данных.

Если я использую алгоритм шифрования с симметричным ключом, мне нужно будет где-то хранить ключ. И если ключ, например, жестко закодирован в памяти, то я предполагаю, что было бы легко разобрать сборки .NET и найти его.

Каков наилучший способ шифрования учетных данных в .NET и обеспечения их безопасности, сохраняя ключ шифрования полностью недоступным?

Вы можете использовать API защиты данных и его реализацию .NET ( ProtectedData ) для шифрования пароля. Вот пример:

public static string Protect(string str) { byte[] entropy = Encoding.ASCII.GetBytes(Assembly.GetExecutingAssembly().FullName); byte[] data = Encoding.ASCII.GetBytes(str); string protectedData = Convert.ToBase64String(ProtectedData.Protect(data, entropy, DataProtectionScope.CurrentUser)); return protectedData; } public static string Unprotect(string str) { byte[] protectedData = Convert.FromBase64String(str); byte[] entropy = Encoding.ASCII.GetBytes(Assembly.GetExecutingAssembly().FullName); string data = Encoding.ASCII.GetString(ProtectedData.Unprotect(protectedData, entropy, DataProtectionScope.CurrentUser)); return data; } 

Или вы можете использовать диспетчер учетных данных Windows (так я предпочитаю, потому что он позволяет пользователям делать резервные копии / восстанавливать / редактировать свои учетные данные, даже если ваше приложение не имеет таких функций). Я создал пакет NuGet Meziantou.Framework.Win32.CredentialManager . Как это использовать:

 CredentialManager.WriteCredential("ApplicationName", "username", "Pa$$w0rd", CredentialPersistence.Session); var cred = CredentialManager.ReadCredential("ApplicationName"); Assert.AreEqual("username", cred.UserName); Assert.AreEqual("Pa$$w0rd", cred.Password); CredentialManager.DeleteCredential("ApplicationName"); 

Оригинальный ответ с собственной оболочкой API:

 using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; using System.Text; using System.ComponentModel; public static class CredentialManager { public static Credential ReadCredential(string applicationName) { IntPtr nCredPtr; bool read = CredRead(applicationName, CredentialType.Generic, 0, out nCredPtr); if (read) { using (CriticalCredentialHandle critCred = new CriticalCredentialHandle(nCredPtr)) { CREDENTIAL cred = critCred.GetCredential(); return ReadCredential(cred); } } return null; } private static Credential ReadCredential(CREDENTIAL credential) { string applicationName = Marshal.PtrToStringUni(credential.TargetName); string userName = Marshal.PtrToStringUni(credential.UserName); string secret = null; if (credential.CredentialBlob != IntPtr.Zero) { secret = Marshal.PtrToStringUni(credential.CredentialBlob, (int)credential.CredentialBlobSize / 2); } return new Credential(credential.Type, applicationName, userName, secret); } public static int WriteCredential(string applicationName, string userName, string secret) { byte[] byteArray = Encoding.Unicode.GetBytes(secret); if (byteArray.Length > 512) throw new ArgumentOutOfRangeException("secret", "The secret message has exceeded 512 bytes."); CREDENTIAL credential = new CREDENTIAL(); credential.AttributeCount = 0; credential.Attributes = IntPtr.Zero; credential.Comment = IntPtr.Zero; credential.TargetAlias = IntPtr.Zero; credential.Type = CredentialType.Generic; credential.Persist = (UInt32)CredentialPersistence.Session; credential.CredentialBlobSize = (UInt32)Encoding.Unicode.GetBytes(secret).Length; credential.TargetName = Marshal.StringToCoTaskMemUni(applicationName); credential.CredentialBlob = Marshal.StringToCoTaskMemUni(secret); credential.UserName = Marshal.StringToCoTaskMemUni(userName ?? Environment.UserName); bool written = CredWrite(ref credential, 0); int lastError = Marshal.GetLastWin32Error(); Marshal.FreeCoTaskMem(credential.TargetName); Marshal.FreeCoTaskMem(credential.CredentialBlob); Marshal.FreeCoTaskMem(credential.UserName); if (written) return 0; throw new Exception(string.Format("CredWrite failed with the error code {0}.", lastError)); } public static IReadOnlyList EnumerateCrendentials() { List result = new List(); int count; IntPtr pCredentials; bool ret = CredEnumerate(null, 0, out count, out pCredentials); if (ret) { for (int n = 0; n < count; n++) { IntPtr credential = Marshal.ReadIntPtr(pCredentials, n * Marshal.SizeOf(typeof(IntPtr))); result.Add(ReadCredential((CREDENTIAL)Marshal.PtrToStructure(credential, typeof(CREDENTIAL)))); } } else { int lastError = Marshal.GetLastWin32Error(); throw new Win32Exception(lastError); } return result; } [DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)] static extern bool CredRead(string target, CredentialType type, int reservedFlag, out IntPtr credentialPtr); [DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)] static extern bool CredWrite([In] ref CREDENTIAL userCredential, [In] UInt32 flags); [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)] static extern bool CredEnumerate(string filter, int flag, out int count, out IntPtr pCredentials); [DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)] static extern bool CredFree([In] IntPtr cred); private enum CredentialPersistence : uint { Session = 1, LocalMachine, Enterprise } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct CREDENTIAL { public UInt32 Flags; public CredentialType Type; public IntPtr TargetName; public IntPtr Comment; public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten; public UInt32 CredentialBlobSize; public IntPtr CredentialBlob; public UInt32 Persist; public UInt32 AttributeCount; public IntPtr Attributes; public IntPtr TargetAlias; public IntPtr UserName; } sealed class CriticalCredentialHandle : CriticalHandleZeroOrMinusOneIsInvalid { public CriticalCredentialHandle(IntPtr preexistingHandle) { SetHandle(preexistingHandle); } public CREDENTIAL GetCredential() { if (!IsInvalid) { CREDENTIAL credential = (CREDENTIAL)Marshal.PtrToStructure(handle, typeof(CREDENTIAL)); return credential; } throw new InvalidOperationException("Invalid CriticalHandle!"); } protected override bool ReleaseHandle() { if (!IsInvalid) { CredFree(handle); SetHandleAsInvalid(); return true; } return false; } } } public enum CredentialType { Generic = 1, DomainPassword, DomainCertificate, DomainVisiblePassword, GenericCertificate, DomainExtended, Maximum, MaximumEx = Maximum + 1000, } public class Credential { private readonly string _applicationName; private readonly string _userName; private readonly string _password; private readonly CredentialType _credentialType; public CredentialType CredentialType { get { return _credentialType; } } public string ApplicationName { get { return _applicationName; } } public string UserName { get { return _userName; } } public string Password { get { return _password; } } public Credential(CredentialType credentialType, string applicationName, string userName, string password) { _applicationName = applicationName; _userName = userName; _password = password; _credentialType = credentialType; } public override string ToString() { return string.Format("CredentialType: {0}, ApplicationName: {1}, UserName: {2}, Password: {3}", CredentialType, ApplicationName, UserName, Password); } } 

Применение:

 WriteCredential("ApplicationName", "Meziantou", "Passw0rd"); Console.WriteLine(ReadCredential("Demo")); 
  • Почему при использовании функции Non-Random IV с режимом CBC существует уязвимость?
  • Какая оптимальная длина для паролей пользователей?
  • Фундаментальное различие между алгоритмами Hashing и Encryption
  • Безопасность сообщений WCF без сертификата и windows auth
  • Как безопасно хранить ключи шифрования в java?
  • Interesting Posts

    Есть ли какая-либо функция в oracle, аналогичная group_concat в mysql?

    Подключение к Интернету Mac Mini происходит очень медленно, подключение любого другого устройства прекрасное. (ПК, iPhone, Xbox 360)

    Swift2 извлечения изображений из Firebase

    Удалите большое дополнение после значка гамбургера (меню) на панели инструментов Android?

    Обработка частичного возврата из recv () TCP в C

    Тень тени для изображения PNG в CSS

    Как получить только новые данные?

    Как вы сопоставляете enum как значение int с белым NHibernate?

    Определите, активировано ли текущее приложение (имеет фокус)

    Отключение / выключение из общего сетевого ресурса Windows без перезагрузки службы рабочей станции

    Что произойдет, если я установлю гостевые дополнения VirtualBox на хост?

    Где mongodb стоит в теореме CAP?

    Размер изображения для всех экранных устройств

    Что такое микробиблиотека?

    Android, как получить доступ к исходным ресурсам, которые я установил в папку res?

    Давайте будем гением компьютера.