Every Windows Hello for Business (WHfB) key-trust and hybrid deployment depends on one Active Directory attribute that almost nobody ever opens: msDS-KeyCredentialLink. It is where a user's WHfB public key physically lives in your directory. When sign-in works, you never think about it. When it breaks - stale keys, orphaned keys, a user with six entries and none of them valid - it is the first place you should look and the last place most admins know how to read.
This is the deep dive. We will decode exactly what that attribute stores, show you how to enumerate and audit it with PowerShell, explain how Microsoft Entra Connect populates it, and - critically - how to find and remove orphaned keys without locking anyone out. Along the way you will see why this same attribute is a favourite target for attackers, which is the subject of the next post in this series.
The problem: an invisible attribute that decides whether Hello works
In the key-trust model, a domain controller (DC) authenticates a WHfB sign-in by checking the user's public key against the value stored in their msDS-KeyCredentialLink attribute. If the key the client presents is not in that attribute, the DC has nothing to validate against and sign-in fails - often with a generic "that option is temporarily unavailable" or a silent fall-back to password. The attribute is multi-valued, so a user can accumulate keys from every device they have ever provisioned Hello on, and there is no built-in clean-up. Over years, that list grows, and stale entries make troubleshooting genuinely confusing.
Why it happens: what msDS-KeyCredentialLink actually stores
It is a multi-valued, forward-linked attribute on user (and computer) objects. Each value is not a simple string - it is a DN-Binary: a binary "Key Credential" structure paired with the distinguished name of the object that owns it. Raw, one value looks like this:
Decode that binary blob and it carries the fields that matter for troubleshooting:
| Field | What it is |
|---|---|
| Version | Structure version (WHfB uses version 2) |
| KeyID | SHA-256 hash of the public key - the unique identity of this credential |
| KeyMaterial | The actual RSA/ECC public key (the DC validates the sign-in against this) |
| KeyUsage | NGC for a Windows Hello Next Generation Credential key |
| KeySource | Where the key was written from (Active Directory) |
| DeviceId | GUID of the device the key was created on - your link back to a specific machine |
| KeyCreationTime | When the credential was registered |
| KeyApproximateLastLogonTimeStamp | Roughly when it was last used - your signal for "is this key stale?" |
The two fields you care about operationally are DeviceId (which machine does this key belong to?) and KeyApproximateLastLogonTimeStamp (has it been used recently, or is it a ghost from a device that was retired two years ago?).
How the key gets there
You do not write this attribute by hand. In a hybrid key-trust deployment, provisioning registers the user's public key with the Azure Device Registration Service, and then Microsoft Entra Connect, on its next sync cycle, pulls that public key down and writes it into the on-premises msDS-KeyCredentialLink attribute. That is the step people forget:
On-premises-only key trust is similar, except the Enterprise Device Registration Service writes the key directly to Active Directory. And note: even cloud Kerberos trust still syncs the key into this attribute (it just does not use it for the ticket) - so the mere presence of a key here does not prove which trust model a device uses.
msDS-KeyCredentialLink is granted to two special groups created for WHfB: Key Admins (domain scope) and Enterprise Key Admins (forest-root scope). Entra Connect's account effectively writes through this permission. Anyone you place in these groups can write key credentials for other users - treat membership as highly privileged.How to verify: enumerate and audit the attribute
Native PowerShell can read the attribute, but it returns the raw DN-Binary - useful for "does a key exist and how many," not for decoding fields:
To decode the fields (DeviceId, creation time, last logon) you need a parser. The community DSInternals module (by Michael Grafnetter) reads the structure natively and is the standard tool for this - it is what turns "3 keys" into "3 keys, one from a device deleted in 2024":
Get-ADUser; decoding the blob is where the community tool earns its place.The fix: find and remove orphaned keys safely
An orphaned key is a credential whose device no longer exists (the machine was wiped, retired, or its Entra device record deleted) but whose key was never removed from the user. Orphans are not usually harmful, but they clutter audits and they are indistinguishable from an attacker-planted key unless you correlate each one to a real, live device.
The audit approach: for each key, resolve its DeviceId against your Entra/AD device inventory. A key whose device is gone, or whose last-logon timestamp is ancient, is a removal candidate. Remove a specific value with native PowerShell:
DeviceId and last-logon timestamp first, remove one orphan at a time, and confirm the user can still sign in before touching the next.Proof it worked: the ongoing audit
A healthy directory has a defensible answer to "why does each user have the keys they have." The audit script for this post produces exactly that: per user, how many keys, when each was created and last used, and which ones have no matching live device. Run it monthly and orphans never pile up.
References
- How Windows Hello for Business provisioning works (key registration and sync)
- Windows Hello for Business hybrid key trust deployment guide
- Active Directory security groups (Key Admins / Enterprise Key Admins)
- MS-ADTS: Key Credential Link structure specification
Script for this post is in Daily-Tasks.