0

认为我成功地为这里提到的 Credential API 函数创建了一个托管包装类,并得到了一些帮助至少从该函数返回的 Win32-Errorcodes 是零或其他但预期的(即,如果调用两次,则来自 CredDelete 的1168 )和适当的值存储在注册表中的正确位置 (HKLM/Comm/Security/Credman/1..)。

现在,我正在使用嵌入在 PPC 上的 WindowsForm 中的 Webbrowser-Control 对使用 NTLM-Authentication 的网站进行身份验证。我不想让弹出对话框出现,用户必须在其中输入他的凭据。相反,我让用户可以将他的凭据存储在他首先在 Optiondialog-Form 中输入的设备上(实习生调用 CredWrite/CredUpdate)。

但是 PIE 对我使用 API 所做的事情感到非常厌恶,CredWrite、-Update 或 -Delete 都没有真正起作用。那么我在这里错过了什么?

CredWrite 的示例代码:

[DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern int CredWrite([In]IntPtr pCred, [In]CREDWRITE_FLAGS dwflags);

public enum CREDWRITE_FLAGS : int
{
    CRED_FLAG_FAIL_IF_EXISTING = 0x00000400
}

    public struct CRED
    {
        public int dwVersion;
        public CRED_TYPE dwType;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string wszUser;
        public int dwUserLen;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string wszTarget;
        public int dwTargetLen;
        public IntPtr pBlob;
        public int dwBlobSize;
        public CRED_FLAGS dwFlags;
    }
    public enum CRED_TYPE
    {
        CRED_TYPE_NTLM = 0x00010002,
        CRED_TYPE_KERBEROS = 0x00010004,
        CRED_TYPE_PLAINTEXT_PASSWORD = 0x00010006,
        CRED_TYPE_CERTIFICATE = 0x00010008,
        CRED_TYPE_GENERIC = 0x0001000a,
        CRED_TYPE_DOMAIN_PASSWORD = 0x00010001,
    }
    public enum CRED_FLAGS : int
    {
        CRED_FLAG_PERSIST = 0x00000001,
        CRED_FLAG_DEFAULT = 0x00000002,
        CRED_FLAG_SENSITIVE = 0x00000008,
        CRED_FLAG_TRUSTED = 0x00000010
    }

public static void WriteCredentials(string target, string userName, string password)
{
    CRED cred = new CRED();
    cred.dwVersion = 1;
    cred.dwType = CRED_TYPE.CRED_TYPE_NTLM;
    cred.wszTarget = target;
    cred.dwTargetLen = target.Length + 1;
    cred.wszUser = userName;
    cred.dwUserLen = userName.Length + 1; 

    cred.dwBlobSize = (Encoding.Unicode.GetBytes(password).Length + 1) * 2;
    //cred.pBlob = Marshal.StringToCoTaskMemUni(password); //<--not in CF
    //cred.pBlob = Marshal2.StringToHGlobalUni(password); //<--from OpenNETCF, the same?
    cred.pBlob = Marshal.StringToBSTR(password); //<--not sure of that, but tried the other one also
    cred.dwFlags = CRED_FLAGS.CRED_FLAG_PERSIST | CRED_FLAGS.CRED_FLAG_SENSITIVE | CRED_FLAGS.CRED_FLAG_TRUSTED; //<-- results in 25 which is also used in creds read which are stored by the IE-UI-CredMan-dialog

    IntPtr credPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cred));
    Marshal.StructureToPtr(cred, credPtr, true);

    int ret = -1;
    ret = CredWrite(credPtr, CREDWRITE_FLAGS.CRED_FLAG_FAIL_IF_EXISTING); //returns zero, unless called twice with the same target/username-tuple.

    Marshal.FreeHGlobal(credPtr);
}

顺便说一句,所谓的“MS-Experts”提到 PIE 有自己的凭据缓存机制,这就是为什么 PIE 忽略 CredUpdate 上的更改。但我怀疑这是 100% 正确的,因为当我在根本没有凭据的设备上调用 CredWrite 时,PIE 也会忽略它们(弹出窗口 cred-inputdialog)。

有人可以帮助我吗?

4

1 回答 1

0

我要检查的是

  1. 如果您使用该对话框手动登录,它是否在预期的凭据注册表项 (HKLM/Comm/Security/Credman/1..) 中存储任何内容?如果不是,那么我会说它没有使用 Cred Manager 是非常有力的证据。
  2. 如果您进行手动 NTLM 身份验证(例如使用资源管理器),浏览器是否会弹出凭据对话框?

我之前用设备完成了 NTLM 身份验证,只是没有使用浏览器控件,我使用了Authentication Services APIs这是 native 中的一个示例。我一直想把它移植到托管,只是还没有找到时间。

编辑 1:在进一步查看您的代码时,我不喜欢 blob。是什么让你认为它应该作为 BSTR 加入?那是一种 COM 类型,我非常怀疑它是否是您想要的。我倾向于只发送带有密码的字节数组。我会先尝试 Unicode,因为 CE 严重偏向于 Unicode,如果失败了,那就是 ASCII。

我还认为您对 CRED 结构的封送处理是可疑的,但这仅仅是因为我从 1.0 之前的日子开始就一直在使用 CF,并且我已经学会了“信任但验证”封送处理程序所做的一切。我至少会在内存视图中查看 CRED 实例,并确保这些字符串确实只是 4 字节指针,并且它们指向的地址实际上包含您的字符串数据(仅此而已,并且以 null 结尾)。

于 2009-01-30T14:02:13.063 回答