将凭证管理器 API 包装在 .NETCF 中

发布于 2024-07-12 19:30:04 字数 3304 浏览 13 评论 0原文

我想我成功地为 此处提到的 Credential API 函数创建了托管包装类在那里的一点帮助下至少从该函数返回了Win32错误代码为零或其他但预期的值(即来自 CredDelete 的 1168,如果调用它两次)并且适当的值存储在注册表中的正确位置(HKLM/Comm/Security/Credman/1..)。

现在,我使用 PPC 上 WindowsForm 中嵌入的 Webbrowser-Control 来对使用 NTLM 身份验证的网站进行身份验证。 我不想让用户必须输入其凭据的弹出对话框出现。 相反,我让用户可以将其凭据存储在他首先在 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 专家”提到 PIE 有自己的信用缓存机制,这就是为什么 PIE 忽略 CredUpdate 上的更改。 但我怀疑这是否 100% 正确,因为当我在根本没有凭据的设备上调用 CredWrite 时,PIE 也会忽略它们(弹出窗口 cred-inputdialog)。

有人可以帮助我吗?

think I successfully made a managed wrapperclass to the Credential API functions mentioned here with a little help from there At least the Win32-Errorcodes returned from that functions are Zero or other but expected (i.e. 1168 from CredDelete if calling it twice) and the appropriate values are stored at the correct place in the registry (HKLM/Comm/Security/Credman/1..).

Now I am using the Webbrowser-Control embedded in a WindowsForm on a PPC to authenticate to a Website which uses NTLM-Authentication. I dont want to let a popupdialog appear in which the user has to enter his credentials. Instead I am giving the user the possibilities to store his credentials on the device which he has inputted in an Optiondialog-Form in the first place (intern calling CredWrite/CredUpdate).

But the PIE is given a damn about it what I do with the API, neither CredWrite, -Update or -Delete is actually working. So what am I missing here?

Sample code for 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);
}

BTW the so called "MS-Experts" are mentioning that PIE has its own cahing mechanism for creds, and thats why PIE is ignoring the changes on CredUpdate. But I doubt that this is 100% correct, cause when I call CredWrite on a device with no credentials at all, that PIE ignores them too (popups cred-inputdialog).

Can someone assist me in that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

沉睡月亮 2024-07-19 19:30:07

我要检查的事情是

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

我以前曾使用设备进行过 NTLM 身份验证,只是没有使用浏览器控件,并且我使用了 身份验证服务 API这是一个本机示例。 我一直想将其移植到托管,只是没有找到时间。

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

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

The things I'd check are

  1. If you manually log in using the dialog, does it store anything at the expected credential registry key(HKLM/Comm/Security/Credman/1..)? If not, then I'd say it's pretty strong evidence that it isn't using the Cred Manager.
  2. If you do manual NTLM authentication (with Explorer for example), does the browser pop up the credential dialog?

I have done NTLM authentication with a device before, just not with the browser control and I used the Authentication Services APIs. Here's an example in native. I've been meaning to port it to managed, just haven't found the time.

Edit 1: In further looking at your code, I don't like the blob. What makes you think it should go in as a BSTR? That's a COM type, and I highly doubt it's what you want. I'd be inclined to just send in a byte array with the password. I'd try Unicode first, since CE is heavily biased toward Unicode, then ASCII if that fails.

I also think your marshaling of the CRED structure is suspect, but that's simply because I've been using the CF since pre-1.0 days and I've learned to "trust but verify" everything the marshaler does. I'd at least look at the CRED instance in a memory view and make sure that those string are indeed just 4-byte pointers and that the addresses that they point to actually contain your string data (nothing more and null terminated).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文