加密后无法将密码保存到凭据管理器
Cannot save password to credential manager when encrypted
我正在尝试将加密密码保存到 Windows 凭据管理器,下面的代码可以正常读取,并且我可以生成正确加密的字符串,但是永远不会保留密码的加密形式.
为了完整起见,我需要加密密码,因为客户端应用程序需要这样做 (Office 2010)。如果我通过 Office 2010 保存密码,我就可以正确读取它。
凭证结构
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Credential
{
public UInt32 flags;
public UInt32 type;
public string targetName;
public string comment;
public System.Runtime.InteropServices.ComTypes.FILETIME lastWritten;
public UInt32 credentialBlobSize;
public IntPtr credentialBlob;
public UInt32 persist;
public UInt32 attributeCount;
public IntPtr credAttribute;
public string targetAlias;
public string userName;
}
阅读:
IntPtr credPtr;
if (!Win32.CredRead(target, settings.Type, 0, out credPtr))
{
Trace.TraceError("Could not find a credential with the given target name");
return;
}
var passwordBytes = new byte[blobSize];
Marshal.Copy(blob, passwordBytes, 0, (int)blobSize);
var decrypted = ProtectedData.Unprotect(passwordBytes, null, DataProtectionScope.CurrentUser);
return Encoding.Unicode.GetString(decrypted);
写入:
var bytes = Encoding.Unicode.GetBytes(password);
var encypted = ProtectedData.Protect(bytes, null, DataProtectionScope.CurrentUser);
//construct and set all the other properties on Credential...
credential.credentialBlobSize = (uint)bytes.Length;
credential.credentialBlob = GetPtrToArray(bytes);
if (!Win32.CredWrite(ref credential, 0))
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
GetPtrToArray
private static IntPtr GetPtrToArray(byte[] bytes)
{
var handle = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, handle, bytes.Length);
return handle;
}
我试过的有:
- 将
credentialBlob
更改为字节[],此操作因 CredentialRead 期间 PtrToStructure 中的编组错误而失败
- 将
credentialBlob
更改为字符串,并在解密前使用 Unicode.GetBytes(),这会产生一个 2 个字符串,假定是 IntPtr
的内容
我认为问题出在为 byte[] 共享内存,即生成要与 CredWrite()
一起使用的 IntPtr
的方法。之后尝试读取凭证时,blobSize
和 blob
均为 0(即指向 blob 的空指针)。
为了完整起见,这是来自 .net 4.6.1 代码库的 运行,我可以毫无问题地存储未加密的字符串(使用 Marshal.StringToCoTaskMemUni(password)
)。
你能帮忙吗?
原来我忘记了 Credential
是一个结构体,下面几行在一个单独的函数中(上面已简化)因此这些字段从未保留它们的值。
void SetPasswordProperties(Win32.Credential credential)
{
credential.credentialBlobSize = (uint)bytes.Length;
credential.credentialBlob = GetPtrToArray(bytes);
}
使用 ref
参数解决了这个问题,如下所示:
void SetPasswordProperties(ref Win32.Credential credential)
我正在尝试将加密密码保存到 Windows 凭据管理器,下面的代码可以正常读取,并且我可以生成正确加密的字符串,但是永远不会保留密码的加密形式.
为了完整起见,我需要加密密码,因为客户端应用程序需要这样做 (Office 2010)。如果我通过 Office 2010 保存密码,我就可以正确读取它。
凭证结构
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Credential
{
public UInt32 flags;
public UInt32 type;
public string targetName;
public string comment;
public System.Runtime.InteropServices.ComTypes.FILETIME lastWritten;
public UInt32 credentialBlobSize;
public IntPtr credentialBlob;
public UInt32 persist;
public UInt32 attributeCount;
public IntPtr credAttribute;
public string targetAlias;
public string userName;
}
阅读:
IntPtr credPtr;
if (!Win32.CredRead(target, settings.Type, 0, out credPtr))
{
Trace.TraceError("Could not find a credential with the given target name");
return;
}
var passwordBytes = new byte[blobSize];
Marshal.Copy(blob, passwordBytes, 0, (int)blobSize);
var decrypted = ProtectedData.Unprotect(passwordBytes, null, DataProtectionScope.CurrentUser);
return Encoding.Unicode.GetString(decrypted);
写入:
var bytes = Encoding.Unicode.GetBytes(password);
var encypted = ProtectedData.Protect(bytes, null, DataProtectionScope.CurrentUser);
//construct and set all the other properties on Credential...
credential.credentialBlobSize = (uint)bytes.Length;
credential.credentialBlob = GetPtrToArray(bytes);
if (!Win32.CredWrite(ref credential, 0))
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
GetPtrToArray
private static IntPtr GetPtrToArray(byte[] bytes)
{
var handle = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, handle, bytes.Length);
return handle;
}
我试过的有:
- 将
credentialBlob
更改为字节[],此操作因 CredentialRead 期间 PtrToStructure 中的编组错误而失败
- 将
credentialBlob
更改为字符串,并在解密前使用 Unicode.GetBytes(),这会产生一个 2 个字符串,假定是IntPtr
的内容
我认为问题出在为 byte[] 共享内存,即生成要与 CredWrite()
一起使用的 IntPtr
的方法。之后尝试读取凭证时,blobSize
和 blob
均为 0(即指向 blob 的空指针)。
为了完整起见,这是来自 .net 4.6.1 代码库的 运行,我可以毫无问题地存储未加密的字符串(使用 Marshal.StringToCoTaskMemUni(password)
)。
你能帮忙吗?
原来我忘记了 Credential
是一个结构体,下面几行在一个单独的函数中(上面已简化)因此这些字段从未保留它们的值。
void SetPasswordProperties(Win32.Credential credential)
{
credential.credentialBlobSize = (uint)bytes.Length;
credential.credentialBlob = GetPtrToArray(bytes);
}
使用 ref
参数解决了这个问题,如下所示:
void SetPasswordProperties(ref Win32.Credential credential)