首页 > 解决方案 > 使用 Kerberos Change-Password 协议和 .NET Core 3 更改 Active Directory 密码

问题描述

可以使用 LDAP 协议更改 AD 用户的密码。但是,如果用户必须在下次登录时更改其密码(或密码已过期),则除非使用管理员凭据,否则无法更改密码。过去我可以通过使用 WIN32 API解决这个问题。不幸的是,我不能在 linux 环境中使用 WINAPI。

根据这篇文章

当用户通过按 CTRL+ALT+DELETE 然后单击更改密码来更改他或她自己的密码时,如果目标是域,则 Windows NT 到 Windows 2003 将使用 NetUserChangePassword 机制(方法 1)。从 Windows Vista 开始,Kerberos 更改密码协议用于域帐户。如果目标是 Kerberos 领域,则使用 Kerberos 更改密码协议(方法 3)。

我寻找可以在 .NET Core 上运行的 Kerberos 客户端,我发现SteveSyfuhs/Kerberos.NET.

通过阅读Kerberos Change Password ProtocolKerberos.NET Samples,我编写了以下代码:

using Kerberos.NET;
using Kerberos.NET.Client;
using Kerberos.NET.Credentials;
using Kerberos.NET.Crypto;
using Kerberos.NET.Entities;
using System;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace KerberosDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string activeDirectoryServer = "10.12.34.56:88"; // kdc port
            string domain = "TEST.LOCAL";
            string username = "testuser@" + domain;
            string oldPassword = "123";
            string newPassword = "456";

            using var client = new KerberosClient(activeDirectoryServer);
            var kerbCred = new KerberosPasswordCredential(username, oldPassword, domain);
            await client.Authenticate(kerbCred);
            var serviceTicket = await client.GetServiceTicket("kadmin/changepw");

            var apReq = new KrbApReq
            {
                Ticket = serviceTicket.Ticket,
                Authenticator = KrbEncryptedData.Encrypt(Encoding.UTF8.GetBytes(newPassword).AsMemory(), kerbCred.CreateKey(), KeyUsage.ApReqAuthenticator),
                ProtocolVersionNumber = 1
            };

            var tcp = client.Transports.FirstOrDefault(t => t.Protocol == ProtocolType.Tcp);
            await tcp.SendMessage<KrbApReq, KrbAsRep>(domain, apReq);
        }
    }
}

我在以下位置遇到以下异常await tcp.SendMessage<KrbApReq, KrbAsRep>(domain, apReq);

未处理的异常。System.Security.Cryptography.CryptographicException:ASN1 损坏的数据。1& contentsLength, Int32& bytesRead) in d:\a\1\s\Kerberos.NET\Asn1\Experimental\AsnReader.cs:line 305 at Kerberos.NET.Entities.KrbError.CanDecode(ReadOnlyMemory在d:\a\1\s\Kerberos.NET\Entities\Krb\KrbError.cs中的 System.Security.Cryptography.Asn1.AsnReader.ReadTagAndLength(Nullable 1 编码):Kerberos.NET.Transport.KerberosTransportBase 的第 23 行。 d:\a\1\s\Kerberos.NET\Client\Transport\TcpKerberosTransport.cs 中的解码 [T](ReadOnlyMemory 1 response) in d:\a\1\s\Kerberos.NET\Client\Transport\KerberosTransportBase.cs:line 41 at Kerberos.NET.Transport.TcpKerberosTransport.ReadResponse[T](NetworkStream stream, CancellationToken cancellation) in d:\a\1\s\Kerberos.NET\Client\Transport\TcpKerberosTransport.cs:line 69 at Kerberos.NET.Transport.TcpKerberosTransport.SendMessage[T](String domain, ReadOnlyMemory1 编码,CancellationToken 取消):KerberosDemo 的第 58 行.Program.Main(String[] args) 的 KerberosDemo .Program.(String[] 参数)

感谢您的帮助。谢谢。

标签: c#.net.net-coreactive-directorykerberos

解决方案


我不妨把这个写成答案。

如果用户必须在下次登录时更改其密码(或密码已过期),则除非使用管理员凭据,否则无法更改密码

如果您使用用户自己的凭据向 AD 进行身份验证,这可能是一个问题,这是验证用户凭据的常用方法。如果用户的密码已过期(或专门设置为在下次登录时强制更改),则身份验证将失败,您将无法查找用户的帐户和更改密码。

为了解决这个问题,您可以使用不同的帐户对 AD 进行身份验证。它不必是管理员帐户。任何域凭据都可以。然后您可以查找用户的帐户。

由于您必须知道旧密码才能更改密码,因此 AD 认为需要更改它的所有授权。

这与重置密码不同,您需要一个对该帐户具有“重置密码”权限的帐户。

正如您所发现的,您还需要安全地连接到 AD,这样新密码就不会以纯文本形式发送。

所有这些都记录在属性的文档中unicodePwd


推荐阅读