C# 调用 LogonUser 失败并返回 SecureString

C# Call LogonUser fail with SecureString

各位,我遇到了 LogonUser 函数的问题。

我只想知道我是否可以通过这个签名将 LogonUser 函数导入到 C# 中:

[DllImport("advapi32.dll", SetLastError = true)]
internal static extern int LogonUser(string username, string domain, IntPtr password, int logonType, int logonProvider, ref IntPtr token);

因为我不想使用字符串来保护我的密码,而是 SecureString class。然后稍后使用如下函数:

var passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(password);
var result = LogonUser(userName, domain, passwordPtr, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);

我总是得到 result = 0 并且消息显示用户名和密码不正确。 但是当我将签名更改为使用字符串密码时,一切正常。

请帮助我保护密码 SecureString 对我来说很重要。

正如 Alex K 所指出的,在 SecureStringToGlobalAllocUnicode 中有一个使用 LogonUser 的例子。请注意,P/Invoke 声明有:

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String username, String domain, IntPtr password,
            int logonType, int logonProvider, ref IntPtr token);

并且 CharSet = CharSet.Unicode 已指定。不幸的是,由于历史原因,默认的 CharSet 值为 Ansi,这就是 您的 P/Invoke 尝试将要使用的值。

这对 username 参数没问题,因为 P/Invoke 基础设施将确保它适当地转换 string。但它不适用于 password 参数,因为 您已经 执行了字符串转换,并且您已将其作为 Unicode 完成 - 而 P/Invoke 所看到的现在是 IntPtr

我建议更新您的 P/Invoke 签名以匹配示例中给出的签名。


另一种选择是改用 SecureStringToGlobalAllocAnsi 并单独留下 P/Invoke 签名。但这是一个严重的二流解决方案。严重不推荐在 2015 年编写非 Unicode 感知代码。

只需养成 始终 在您编写的任何 P/Invoke 签名中指定 CharSet.Unicode 的习惯。