2012-04-06 96 views
1

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; 
    } 

能在正确的方向上的任何一个点,我怎么能得到这些类型的凭据保存?

感谢

+1

你最好使用老操作系统反斜杠。 – 2012-04-06 13:05:03

回答

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。