如何在不将密码放入字符串的情况下检查Active Directory上的用户/密码组合?

时间:2011-04-05 06:51:39

标签: .net active-directory directoryservices

我想检查Windows域上的用户/密码组合。现在我使用以下代码执行此操作:

bool Login(String username, String password) {
    var principalContext = new PrincipalContext(ContextType.Domain);
    principalContext.ValidateCredentials(username, password);
}

虽然它有效,但让我烦恼的是我必须将密码放在String中才能使用该API;因为我使用SecureString将密码存储在其他地方,我真的想用某种方式检查用户名/密码组合,而不必将密码作为托管System.String传递。

实现这一目标的最佳方法是什么?

2 个答案:

答案 0 :(得分:3)

您可以尝试的一种方法可能是:

通过使用P / Invoke调用LoginUser来模拟用户,将密码作为SecureString as described in MSDN传递。

使用模拟用户连接到ActiveDirectory,而不传递用户名和密码:

AuthenticationTypes authenticationTypes = AuthenticationTypes.Secure;

using (var entry = new DirectoryEntry("LDAP://example.com", "", "", authenticationTypes))
{
    ...
}

我没有尝试过,但在我看来它应该有用。

答案 1 :(得分:3)

使用DsBindWithCred。请注意,即使凭据在技术上有效(例如帐户被锁定),此功能也会因拒绝访问而失败。如果您想要详细程度,则必须使用LogonUser功能,但每次调用都将算作登录尝试。

using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Text;

public class PInvoke
{
    public static bool TestCreds(string usernamePossiblyWithDomain, 
                                 SecureString password, 
                                 string dnsDomainName)
    {
        string username, usernameDomain;
        ParseUserName(usernamePossiblyWithDomain, out username, out usernameDomain);

        IntPtr pPass = Marshal.SecureStringToGlobalAllocUnicode(password);

        try
        {
            IntPtr hDS = IntPtr.Zero;
            IntPtr authID = MakePassCreds(username, usernameDomain, pPass);
            //if you're really paranoid, you can uncomment the next two lines
            //to zero out the memory as soon as possible
            //Marshal.ZeroFreeGlobalAllocUnicode(pPass);
            //pPass = IntPtr.Zero;

            try
            {
                int lastErr = DsBindWithCred(null, dnsDomainName, authID, ref hDS);
                switch(lastErr)
                {
                    case 0: return true;  //ERROR_SUCCESS
                    case 5: return false; //ERROR_ACCESS_DENIED
                    default: throw new Win32Exception(lastErr);
                }
            }
            finally
            {
                if(hDS != IntPtr.Zero) DsUnBind(ref hDS);
                if(authID != IntPtr.Zero) DsFreePasswordCredentials(authID);
            }
        }
        finally
        {
            if(pPass != IntPtr.Zero) Marshal.ZeroFreeGlobalAllocUnicode(pPass);
        }
    }

    [DllImport("credui.dll", CharSet = CharSet.Unicode)]
    protected static extern int CredUIParseUserName(string pszUserName,
                                    StringBuilder pszUser,   int ulUserMaxChars, 
                                    StringBuilder pszDomain, int ulDomainMaxChars);

    public static void ParseUserName(string usernamePossiblyWithDomain, 
                                     out string username, out string domain)
    {
        int MaxUserChars = 256, maxDomainChars = 256;
        StringBuilder sbUser = new StringBuilder(maxUserChars);
        StringBuilder sbDomain = new StringBuilder(maxDomainChars);
        int lastErr = CredUIParseUserName(usernamePossiblyWithDomain, sbUser,
                                 maxUserChars - 1, sbDomain, maxDomainChars - 1);
        if(lastErr != 0) throw new Win32Exception(lastErr);
        username = sbUser.ToString();
        domain = sbDomain.ToString();
    }

    [DllImport("ntdsapi.dll", CharSet = CharSet.Unicode)]
    protected static extern int DsMakePasswordCredentials(
        string User, string Domain, IntPtr Password, ref IntPtr pAuthIdentity);

    [DllImport("ntdsapi.dll")]
    public static extern int DsFreePasswordCredentials(IntPtr AuthIdentity);

    //caller is responsible for calling DsFreePasswordCredentials on the return val
    public static IntPtr MakePassCreds(string username, string domain, IntPtr pPass)
    {
        IntPtr auth = IntPtr.Zero;
        int lastErr = DsMakePasswordCredentials(username, domain, pPass, ref auth);
        if(lastErr != 0) throw new Win32Exception(lastErr);
        return auth;
    }

    [DllImport("ntdsapi.dll", CharSet = CharSet.Unicode)]
    protected static extern int DsBindWithCred(string DomainControllerName, 
                        string DnsDomainName, IntPtr AuthIdentity, ref IntPtr phDS);

    [DllImport("ntdsapi.dll")]
    public static extern int DsUnBind(ref IntPtr phDS);
}