C#在Windows XP专业版
pinvoking CredWrite问题,我现在有一个用C#调用CredRead/CredWrite from Advapi32.dll一个程序,很容易让那些没有加入到域,以节省终端服务器/ WebDAV服务器凭据的用户。 (http://msdn.microsoft.com/en-us/library/aa374788%28v=vs.85%29.aspx的凭据结构信息)C#在Windows XP专业版
这一切都正常工作,但我遇到了一个问题,在所有的XP操作系统持续存在;我无法保存终端服务器凭据。本地CredWrite方法返回错误代码87(ERROR_INVALID_PARAMETER)。
试图凭证持久性和类型的多种不同的组合之后,我也意识到问题的本身的TargetName。要将终端服务器保存在Windows密钥环中,TargetName是TERMSRV/server.domain.com。在Windows Vista或更高版本上,这对我的代码可以正常工作,但在Windows XP上不会。
奇怪的是,当你在Windows上运行XP的远程桌面应用程序,并将它保存的凭证,它这样做没有大惊小怪。当我使用RDP创建一个枚举保存凭据的方法时(显然没有返回密码斑点),我可以看到目标与我的程序试图编写的目标完全相同。
要确认问题是在的TargetName,我删除了“/”和它工作得很好。
下面是我的代码的相关部分:
var writeInt = NativeCredMan.WriteCred("TERMSRV/host.server.com", samAccountName, password, CRED_TYPE.DOMAIN_PASSWORD, CRED_PERSIST.LOCAL_MACHINE);
... ...
public enum CRED_TYPE : uint
{
GENERIC = 1,
DOMAIN_PASSWORD = 2,
DOMAIN_CERTIFICATE = 3,
DOMAIN_VISIBLE_PASSWORD = 4,
GENERIC_CERTIFICATE = 5,
DOMAIN_EXTENDED = 6,
MAXIMUM = 7, // Maximum supported cred type
MAXIMUM_EX = (MAXIMUM + 1000), // Allow new applications to run on old OSes
}
public enum CRED_PERSIST : uint
{
SESSION = 1,
LOCAL_MACHINE = 2,
ENTERPRISE = 3,
}
... ...
[DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr CredentialPtr);
[DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool CredWrite([In] ref NativeCredential userCredential, [In] UInt32 flags);
[DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]
static extern bool CredFree([In] IntPtr cred);
[DllImport("Advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode)]
static extern bool CredDelete(string target, CRED_TYPE type, int flags);
//[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
//static extern bool CredEnumerateold(string filter, int flag, out int count, out IntPtr pCredentials);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CredEnumerate(string filter, uint flag, out uint count, out IntPtr pCredentials);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct NativeCredential
{
public UInt32 Flags;
public CRED_TYPE 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;
internal static NativeCredential GetNativeCredential(Credential cred)
{
var ncred = new NativeCredential
{
AttributeCount = 0,
Attributes = IntPtr.Zero,
Comment = IntPtr.Zero,
TargetAlias = IntPtr.Zero,
Type = CRED_TYPE.DOMAIN_PASSWORD,
Persist = (UInt32) cred.Persist,
CredentialBlobSize = (UInt32) cred.CredentialBlobSize,
TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName),
CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob),
UserName = Marshal.StringToCoTaskMemUni(cred.UserName)
};
return ncred;
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Credential
{
public UInt32 Flags;
public CRED_TYPE Type;
public string TargetName;
public string Comment;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
public UInt32 CredentialBlobSize;
public string CredentialBlob;
public CRED_PERSIST Persist;
public UInt32 AttributeCount;
public IntPtr Attributes;
public string TargetAlias;
public string UserName;
}
.. 。 ...
public static int WriteCred(string key, string userName, string secret, CRED_TYPE type, CRED_PERSIST credPersist)
{
var byteArray = Encoding.Unicode.GetBytes(secret);
if (byteArray.Length > 512)
throw new ArgumentOutOfRangeException("The secret message has exceeded 512 bytes.");
var cred = new Credential
{
TargetName = key,
CredentialBlob = secret,
CredentialBlobSize = (UInt32) Encoding.Unicode.GetBytes(secret).Length,
AttributeCount = 0,
Attributes = IntPtr.Zero,
UserName = userName,
Comment = null,
TargetAlias = null,
Type = type,
Persist = credPersist
};
var ncred = NativeCredential.GetNativeCredential(cred);
var written = CredWrite(ref ncred, 0);
var lastError = Marshal.GetLastWin32Error();
if (written)
{
return 0;
}
var message = "";
if (lastError == 1312)
{
message = (string.Format("Failed to save " + key + " with error code {0}.", lastError) + " This error typically occurrs on home editions of Windows XP and Vista. Verify the version of Windows is Pro/Business or higher.");
}
else
{
message = string.Format("Failed to save " + key + " with error code {0}.", lastError);
}
MessageBox.Show(message);
return 1;
}
能在正确的方向上的任何一个点,我怎么能得到这些类型的凭据保存?
感谢
没关系。我发现了这个问题。回顾我的问题后,我注意到,这里的管理凭证计数器部分:
internal static NativeCredential GetNativeCredential(Credential cred)
{
var ncred = new NativeCredential
{
AttributeCount = 0,
Attributes = IntPtr.Zero,
Comment = IntPtr.Zero,
TargetAlias = IntPtr.Zero,
Type = CRED_TYPE.DOMAIN_PASSWORD,
Persist = (UInt32) cred.Persist,
CredentialBlobSize = (UInt32) cred.CredentialBlobSize,
TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName),
CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob),
UserName = Marshal.StringToCoTaskMemUni(cred.UserName)
};
return ncred;
}
有预定义为CRED.TYPE.DOMAIN_PASSWORD,而不是仅仅decalring类型的类型。
更改为:
internal static NativeCredential GetNativeCredential(Credential cred)
{
var ncred = new NativeCredential
{
AttributeCount = 0,
Attributes = IntPtr.Zero,
Comment = IntPtr.Zero,
TargetAlias = IntPtr.Zero,
Type = cred.type,
Persist = (UInt32) cred.Persist,
CredentialBlobSize = (UInt32) cred.CredentialBlobSize,
TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName),
CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob),
UserName = Marshal.StringToCoTaskMemUni(cred.UserName)
};
return ncred;
}
,并发送证书作为一种通用的解决了这个问题。显然,Windows Vista/7允许在DOMAIN_PASSWORD和GENERIC持久性密码上使用“/”,但XP只允许使用GENERIC。
你最好使用老操作系统反斜杠。 – 2012-04-06 13:05:03