Windows Hello for Business Cloud Trust and KDC proxy

Windows Hello for Business cloud trust is the latest addition to deployment methods that can be used for Windows Hello for Business.

Windows Hello for Business cloud trust

Windows Hello for Business is Microsofts passwordless logon solution that uses an asymmetric key pair for authentication instead of using username and password. The private key is securely stored in the Trusted Platform Module (TPM), preventing the private key from getting leaked. All the technical complexity of the logon process is completely transparent to the user, she only has to unlock the credentials stored in the TPM using either a PIN or some kind biometrics. This brings this method en pair with what users are used from their smartphones.

Depending on the deployed Windows Hello for Business method used the authentication process is different. In case of hybrid cloud trust Azure AD is the initial point of contact for the client.

Hybrid Azure AD join authentication using Azure AD Kerberos (cloud trust)

Source: Microsoft Docs

  1. The user unlocks the credentials stored in the TPM
  2. The Cloud AP requests a nonce from Azure AD
  3. This nonce is signed with the private key part of the credentials and send back to Azure AD
  4. Azure AD checks the used key against an set of public keys stored as part of the user object
  5. If this check is successful it will check is the nonce is also valid and
  6. Sends back the Primary Refresh Token (PRT) to the client. But not only the PRT is provided but also a partial Kerberos TGT
  7. The PRT is then stored securely in the TPM
  8. To get access to on-premises resources the client uses the partial Kerberos TGT to send an TGS-REQ to an on-premises Domain Controller
  9. The domain controller validates the TGS-REQ and returns a full krbtgt as part of an TGS-REP
  10. This krbtgt can then be used to request additional TGT for different services when needed.

This description is already very technical, but let’s pop open the hood even more and see how this is all possible.

And while we are at it I will show you, how you can create your own “Azure AD Kerberos” to provide the full krbtgt ticket to every eligible client, even when no domain controller is in direct line of sight of the computer.

Why use a KDC proxy?

One of the most difficult tasks when using Active Directory is to protect your domain controllers. Legacy Windows Clients need to have line of sight access to at least one of your domain controllers. This is required to provide services like Kerberos and SMB for group policies and logon scripts.

Legacy architecture on-premises

With modern clients, managed through Intune, those requirements got fewer and were reduced to Kerberos (TCP+UDP/88). But still some kind of direct communication was still needed.

This is were the KDC proxy comes into play. This service is a broker service between the client and domain controller. The communication is encrypted using TLS and limited to one port (default TCP/443).

This service receives Kerberos communication encapsulated in TLS encrypted HTTP. The client must use the Kerberos Key Distribution Center (KDC) Proxy Protocol [MS-KKDCP] to send messages to this service.

Modern architecture with Azure AD and Intune

Because TLS is used as underlying protocol the same security features apply (e.g. forward secrecy).

Of course, the services target with the Kerberos TGT still need to be exposed to the client.

This service is by no means tight to any cloud service and can be used to provide Kerberos over the Internet even in legacy environments. In those scenarios the support for Kerberos change password messages will help to keep the password of the user in sync.

Lab setup

Windows Hello for Business cloud trust

To enable Windows Hello for Business cloud trust you must create multiple objects in your on-premises and cloud environment. Microsoft automated this process using the Set-AzureADKerberosServer cmdlet.

The following commands must be executed on a client with line of sight to an domain controller and internet access.

Install-Module -Name AzureADHybridAuthenticationManagement -AllowClobber
$domain = Get-ADDomain | Select-Object -ExpandProperty DNSRoot
# UPN of an Azure Active Directory global administrator
$userPrincipalName = ""
Set-AzureADKerberosServer -Domain $domain -UserPrincipalName $userPrincipalName

If you have more than one domain you will need to create one Azure AD Kerberos object for each domain.

This cmdlet creates an disabled on-prem user called krbtgt_AzureAD, note that the samAccountName will be different krbtgt_[0-9]{5}.

It also creates a computer account named AzureADKerberos which represents the Read-Only domain controller object.

So if you had a familiar feeling when reading the username you are right. Microsoft reuses some of the technology already built into Windows Server 2008.

The krbtgt_AzureAD account is connected to the computer object using the msds-KrbTgtLinkBl attribute.


But the real “magic” part of this account creation is the generated password. The account password, or to be more accurate the hash, is not only set to the krbtgt_AzureAD user account but also is send to Azure AD. The cloud directory stores this hash with a mapping of the domain it is valid in.

Creation of objects for Azure AD Kerberos

Of course, as with all Kerberos krbtgt users, this accounts password has to be changed regularly. Do not use your process you have in place for other krbtgt account. The cmdlet offers the parameter -RotateServerKey which must be used to keep the cloud entry in sync as well.

KDC proxy

You can use any current version of Windows to provide KDC proxy capabilities. The service is part of the core OS but must be configured and enabled.

You will need to deploy a TLS certificate on this server before you can start. Use a publicly signed certificate to make sure every client trusts this service even when Intune has not yet deployed custom root certificates.

The next steps are quite easy, and I have documented them in the following script.

The script assumes a certificate containing the subject name kdcproxy. Please change this to apply to your environment.
# Generate a new GUID for the application
$GUID = New-Guid | Select-Object -ExpandProperty Guid
# Get certificate thumbprint that should be used
$Thumbprint = Get-ChildItem 'Cert:\LocalMachine\My\' | ? Subject -match "kdcproxy" | Select -ExpandProperty Thumbprint
# Grant permissions to the Network Service account to the Url https://+:443/KdcProxy 
netsh http add urlacl url=https://+:443/KdcProxy user="NT AUTHORITY\Network Service"
# Create a certificate binding on all ip addresses
Add-NetIPHttpsCertBinding -ipport -CertificateHash $Thumbprint -CertificateStoreName "MY" -ApplicationId $GUID -NullEncryption $false

# Disable client authentication 
New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\KPSSVC\Settings -Name HttpsClientAuth -Type Dword -Value 0x0 -Force
# Enable password authentication, we discuss this later
New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\KPSSVC\Settings -Name DisallowUnprotectedPasswordAuth -Type Dword -Value 0x0 -Force

# Create an incoming firewall rule
New-NetFirewallRule -DisplayName "Allow KDC proxy TCP/443" -Direction Inbound -Protocol TCP -LocalPort 443

# Set the KDC proxy service to automatic
Set-Service -StartupType Automatic -Name kpssvc
# Start the KDC proxy 
Start-Service kpssvc

KDC proxy settings

Registry setting Type Example Description Needed
HttpsClientAuth Dword 0 Use the client certificate for client authentication Yes
DisallowUnprotectedPasswordAuth Dword 0 Allow the usage of password as authentication method No*
HttpsUrlGroup MultiString +:8443:KdcProxy Change the port and Url of the KDC service No

* This setting is relevant for a specific use case. Please bear with to decide if you want this enabled or not.


The client used in this setup is 100% Azure AD joined. No hybrid trust is used, and the client has no network connection to the domain controller. It is only connected to the internet.

Windows Hello for Business

Deploy the custom template with ./Device/Vendor/MSFT/PassportForWork/<TENANTID>/Policies/UseCloudTrustForOnPremAuth set to True (Type Boolean) and enable Windows Hello for Business.

Replace TENANTID with you own tenant id.

If you want to make those changes on a test device, you can also set this using gpedit.msc

Administrative Templates > Windows Component

Setting Value
Use Windows Hello for Business Enabled
Use cloud trust for on-premises authentication Enabled
Always enforce the usage of TPM in real case scenarios to avoid attacks on the secrets used. This applies to Intune and group policies.

KDC proxy

Create a device configuration profile for Windows 10+ devices, select “Settings catalog (preview)” and search for Kerberos.

Use “Administrative Templates” -> System -> Kerberos and set the following settings

Setting Value
Disable revocation checking for the SSL certificate
of KDC proxy servers
Specify KDC proxy servers for Kerberos clients Enabled
Name: Value: <https />

Change the name of the dns suffix to your domain name. You can also use * as a catch all or as a wildcard.


You can also set the exact same setting using group policies.

Initial logon process

End User experience

The user logs into the device using her username and password.

Directly after the logon screen she will be prompted to setup Windows Hello for Business.

Setup Windows Hello for Business

Set PIN for Windows Hello for Business

Immediately after the login the user checks for Kerberos tickets using klist. And voila the krbtgt is already here.

Kerberos ticket on Azure AD joined device

As you can see in the above screenshot the value of Kdc Called is not the domain controller but the FQDN of the KDC proxy.

Azure AD

Immediately after the Windows Hello for Business registration of the users, the Windows Hello for Business public key will show up in Azure AD. Check the ‘Authentication methods’ blade for the user.

Windows Hello for Business in the authentication methods blade

Active Directory

In a Hybrid Azure AD joined Key Trust Deployment the client could not immediately log out and log in using Windows Hello for Business.

This is because the Azure AD Connect has not yet sync the public key. Only one of the currently two registered Windows Hello for Business keys is present in the msDS-KeyCredentialLink attribute. But this is totally irrelevant for this use case, because the certificate is not used to authenticate to on-prem resources as we will see in a bit.

msDS-KeyCredentialLink registered to the user

After the sync is completed, both keys are present in the on-premises Active Directory as well.

msDS-KeyCredentialLink synced after setup

Because this behavior is the same as when you use the Hybrid Azure AD joined Key Trust Deployment of WHfB the migration scenario between those two is quite easy. Just follow the prerequisites and reconfigure the existing clients.

KDC proxy

On the KDC proxy server you will see multiple events, two for each Kerberos request send from the client. The first one with event Id 400 is just a confirmation that An HTTP request was received.

The next event with id 307 contains which domain controller was used for this request.

Rediscovered KDC\\ for domain INT.C4A8KORRIBAN.COM

Kerberos deep dive

When looking at the Kerberos communication on the wire, you will see the default behavior for a password login. The only exception being that no additional TGS is requested directly after the login because the Azure AD joined machine does not rely on Kerberos based services and therefor does not have the need for any Ticket Granting Tickets (TGT).

AS-REQ - AS-REP using username + password

  • AS-REQ
    The authentication service request is sent to the domain controller requesting service krbtgt/
    Because the the initial AS-REQ is missing any kind of pre-auth information this request fails and the KDC requests some kind of pre authentication and sends the salt to use for pre-auth
  • AS-REQ
    Now the client resends the AS-REQ using the encrypted current timestamp for pre-auth.
    Left: AS-REQ missing PA-ENC-TIMESTAMP - Right: AS-REQ including PA-ENC-TIMESTAMP
  • AS-REP
    After verifying the information provided, the authentication reply for krbtgt/ is returned containing the privilege attribute certificate (PAC).

Involved in this communication were the secrets of krbtgt and takeshi.kovacs, the end user.

Secrets used for AS-REQ - AS-REP using username + password


When DisallowUnprotectedPasswordAuth is set to 0 the KDC proxy will allow the user to retrieve a krbtgt using username and password.

To enhance security you should set this setting to 1 or delete the registry key all together. The KDC proxy will block every AS-REQ that sends an unprotected pa-timestamp. When using Windows Hello for Business or FIDO this is not the case, as I will show you later.

Thank you
A big shout out to Steve Syfuhs (@stevesyfuhs) for providing me with information about the mechanism behind DisallowUnprotectedPasswordAuth.

In the KDC proxy event log you will see the following information when this happens.

The account (Domain:, User: takeshi.kovacs) is rejected due to the usage of an unarmored Kerberos message

The client itself receive the following error, when using klist get krbtgt

Logon failure: The machine you are logging onto is protected by an authentication firewall.

When looking at the network traffic you can see the initial AS-REQ is send to the domain controller and an AS-REP is send back to the client.

Kerberos communication when DisallowUnprotectedPasswordAuth = 1 and username + password is used

But after this the KDC proxy will block the next AS-REQ requests send by the client that are based on the initial communication with an HTTP error code 403 - Forbidden.

HTTP/2 communication when DisallowUnprotectedPasswordAuth = 1 and username + password is used


HttpsClientAuth must be set to 0 otherwise the all connections will fail, even when the Windows Hello for Business certificate is replicated to the on-prem AD.

This is because the root certificate of this certificate (e.g. MS-Organization-P2P-Access [2022]) is not trusted by the KDC proxy server and a validation is not possible.

Windows Hello for Business login

Now let’s logoff and use Windows Hello for Business to login again and check the kerberos tickets with klist.

Kerberos tickets after Windows Hello for Business login

Again the KDC proxy was used to provide the TGT to the user.

The KERBEROS.MICROSOFTONLINE.COM ticket is present because this user is also configured to use Azure AD Kerberos. Kerberos in the cloud everywhere.

Azure AD

The Cloud Authentication Provider (CloudAP) was used to login to the computer. Windows Hello for Business unlocked the private key in the TPM of the machine to sign the nonce while logging in and Azure AD provided an PRT and in addition to that, an TGT for krbtgt/ was part of the response. You cannot see this ticket using klist and as long as you have TPM enabled you also cannot extract it.

Active Directory

The on-premises Active Directory had nothing to do with the login process until the client requested a krbtgt. You will see event id 4769 originating from the internal IP address of the KDC proxy requesting a service ticket for krbtgt.

A Kerberos service ticket was requested from the DC

KDC proxy

Since the KDC proxy eventlog is not very helpful, I decided to record and decrypt the TCP packets using Wireshark and mitmproxy.

HTTP/2 data flow

As you can see the communication is based on HTTP/2 and there are two main interactions.

Each representing a TGT exchange containing the Kerberos data.

The client sends, using the HTTP POST method, two DATA frames to the KDC proxy. This is a Kerberos TGS-REQ.

HTTP2 POST request to /KdcProxy

After receiving this information the KDC proxy will reply with a status code 200 and two DATA frames. This is a Kerberos TGS-REP.

Kerberos ticket within the data flow

Sadly I could not convince Wireshark to decode the Kerberos data within the HTTP/2 packet to compare them to the packets send from the KDC proxy to the DC. But as the protocol documentation states:

KKDCP does not alter the syntax of any Kerberos messages.

Nice to know
Sadly the magic in the second packet does not refer to the magical experience, but its the description Wireshark uses for HTTP/2 connection preface.

Kerberos deep dive

When you look at the Kerberos tickets send to the domain controller, you will note that this conversation does not start using a AS-REQ ticket but a TGS-REQ instead. This is because the client already was provided with a valid Kerberos ticket from Azure AD, therefore it is not necessary to begin using an authentication service request.

Kerberos tickets using Windows Hello for Business login

I cheated a little bit and used klist purge and klist get krbtgt for the result in the screenshot. The initial login using Windows Hello for Business is a bit messier. I will discuss this in the next section for anybody interested in all the details.
  1. TGS-REQ
    This kerberos packet is sent to the domain controller with the intent to request a full krbtgt for the user (TGS-REQ) as well as the NTLM hash of the user (KERB-KEY-LIST-REQ), using the already existing krbtgt that the user received from Azure AD.
    Partial TGT send to the DC in exchange for a full TGT and NTLM hash
    The existing krbtgt is issued for the user by server and contains no groups.
    cname value in TGS-REQ encrypted by krbtgt_24125
    No group membership and server
    This ticket is encrypted by the user krbtgt_24125@INT.C4A8KORRIBAN.COM which is the user generated the Set-AzureADKerberosServer cmdlet that acts as a read only domain controller. Because Azure AD knows the password (not in cleartext) of this user, it uses it to encrypt the enc-part data.
  2. TGS-REP
    The on-premises domain controller exchanges the partial krbtgt for an full krbtgt including the group memberships. Because the on-premises DC also is aware of the secret of krbtgt_24125 it can validate the data send in the request.
    TGT-REP with 102 group memberships

    This reply also includes the requested NTLM hash.
    KERB-KEY-LIST-REP contains the NTLM hash of the user
    This ticket is encrypted by: krbtgt@INT.C4A8KORRIBAN.COM
    This ticket is encrypted by: krbtgt@INT.C4A8KORRIBAN.COM
  3. TGS-REQ
    The client now has a full TGT but is missing the PAC-OPTIONS information that are normally part of the AS-REP. So it sends another request to get this information as well.
    PAC-OPTIONS-REQ requested as part of the TGS-REQ
    This ticket is encrypted by: krbtgt@INT.C4A8KORRIBAN.COM
  4. TGS-REP
    The resulting reply contains almost all the same information but includes the PAC-OPTIONS and is missing the KERB-KEY-LIST-REP (NTLM Hash)
    PAC-OPTIONS in second TGS-REP
    This ticket is encrypted by: krbtgt@INT.C4A8KORRIBAN.COM

The full story

Login using Windows Hello for Business

As you can see the initial Kerberos communication is a lot chattier that just klist get krbtgt. The exact reason for this is unclear to me, but I guess it has something to do with the different authentication packages that LSA asks if they can perform some action with the provided credentials. Steve Syfuhs goes into great detail on this behavior in his blog post What Happens When you Type Your Password into Windows?

Comment Result
The request is encrypted by the RODC krbtgt_24125, the response by krbtgt Kerberos ticket and NTLM hash received
The request is encrypted by the krbtgt, the response by krbtgt Kerberos ticket and PAC options received
AS-REQ AS-REP Unknown: 150
The PA-data type 150 is not documented but somehow understood by the KDC proxy and even with DisallowUnprotectedPasswordAuth disabled the ticket is forwarded to the DC. The request fails with eRR-PREAUTH-REQUIRED
The user certificate is used for PKINIT authentication* The request fails with eRR-CLIENT-NAME-MISMATCH because the certificate is unknown

Those requests are repeated multiple times. Four times for TGS-REQ <> TGS-REP and two times for AS-REQ <> AS-REP.

* Screenshots of the certificate used in the PKINIT flow request.

Certificate used to sign the AS-REQ

User certificate in Windows

With domain controller certificate

Should you come from a intact key trust implementation and the domain controller used in the flow has a valid certificate the behavior around PKINIT changes. This is most likely because the key trust mechanism kicks in.

Successful PKINIT

Comment Result
AS-REQ AS-REP Unknown: 150
The PA-data type 150 is not documented but somehow understood by the KDC proxy and even with DisallowUnprotectedPasswordAuth disabled the ticket is forwarded to the DC. The request fails with eRR-PREAUTH-REQUIRED
The user certificate is used for PKINIT authentication* PK-AS-REP is returned successfully

Those requests are repeated the same as in the first scenario.


When using a FIDO security key to log into this computer the flow deviates a little bit from the Windows Hello for Business login. Initially no TGT is requested after you log in but it can be requested using klist get krbtgt or will be automatically requested as soon as an Kerberos based resource is accessed.

The actual Kerberos flow when triggered by klist get krbtgt is exactly as described above.

FIDO used for login and krbtgt requested manually

When accessing a website this behavior is triggered in the background and in additional to the TGT a TGS for the web service is requested.

Access to a kerberos website

Reset Windows Hello for Business

When you test different scenarios with Windows Hello for Business it comes in handy to have an easy option to remove the key pair completely.

Login using the user you want to delete the Windows Hello for Business registration for and execute

certutil.exe -DeleteHelloContainer

Windows Hello container deleted

This will delete the certificates related to Windows Hello for Business and after you log off, you will need to use username and password to login again.

Don’t forget to remove the public key from the user object in Azure AD as well. Navigate to the user and remove Windows Hello for Business in the ‘Authentication methods’ blade.

Remove key from Azure AD