gpt4 book ai didi

c# - 在 WPF 应用程序中加密凭据

转载 作者:行者123 更新时间:2023-11-30 22:41:12 28 4
gpt4 key购买 nike

在 WPF 应用程序中,我想提供典型的“记住我”选项来记住凭据并在下次启动应用程序时自动使用它们。

Using a one-way hash显然不是一个选项,虽然我可以存储凭据 in isolated storagein the registry , 加密凭据时需要处理一个问题。

如果我使用对称 key 加密算法,我需要将 key 存储在某个地方。例如,如果 key 硬编码在内存中,那么我想反汇编 .NET 程序集并找到它会很容易。

在 .NET 中加密凭据并确保它们安全、使加密 key 完全无法访问的最佳方法是什么?

最佳答案

这是我的博文摘要:How to store a password on Windows?

您可以使用数据保护 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");

使用 native API 包装器的原始答案(较新的版本是 available on GitHub ):

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<Credential> EnumerateCrendentials()
{
List<Credential> result = new List<Credential>();

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"));

关于c# - 在 WPF 应用程序中加密凭据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31103352/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com