Skip to content

Commit 7d3a04b

Browse files
authored
Merge pull request #8159 from Deland-Han/ci3631
AB#3631: Scripts to extract certificate information from msDS-KeyCredentialLink _ Create a standalone article
2 parents 28eaaf0 + 9f16d48 commit 7d3a04b

File tree

4 files changed

+204
-0
lines changed

4 files changed

+204
-0
lines changed

support/windows-client/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,8 @@ items:
11171117
href: ./user-profiles-and-logon/event-id-300-windows-hello-successfully-created-in-windows-10.md
11181118
- name: Windows Hello errors during PIN creation in Windows 10
11191119
href: ./user-profiles-and-logon/windows-hello-errors-during-pin-creation-in-windows-10.md
1120+
- name: Retrieve certificate to troubleshoot Windows Hello for Business logon failures
1121+
href: ./user-profiles-and-logon/retrieve-certificate-to-troubleshoot-windows-hello-for-business.md
11201122
- name: Virtualization
11211123
items:
11221124
- name: Virtualization
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
title: Retrieve Certificate to Troubleshoot Windows Hello for Business Logon Failures
3+
description: This article introduces a solution to retrieve certificate information from Active Directory.
4+
ms.date: 02/13/2025
5+
manager: dcscontentpm
6+
audience: itpro
7+
ms.topic: troubleshooting
8+
ms.reviewer: takondo
9+
ms.custom: sap:User Logon and Profiles\User profiles, csstroubleshoot
10+
---
11+
# Retrieve certificate to troubleshoot Windows Hello for Business logon failures
12+
13+
This article introduces how to troubleshoot Windows Hello for Business (WHfB) logon failures in a hybrid environment.
14+
15+
## Scenario
16+
17+
After deploying WHfB to a key trust hybrid environment, you encounter one of the following issues:
18+
19+
- Users can't log on to a hybrid joined device by using WHfB.
20+
- Single sign-on (SSO) to on-premises resources fails after logging on to a Microsoft Entra ID joined device by using WHfB.
21+
22+
## Analysis
23+
24+
To fix these issues, you need to review the certificate used for authentication.
25+
26+
The **msDS-KeyCredentialLink** attribute of the Active Directory user object contains information on the certificate used for authentication. This certificate is created during the user's WHfB provisioning and uploaded to Microsoft Entra ID. This information should then be synchronized from Microsoft Entra ID to on-premises Active Directory through Microsoft Entra Connect. If this attribute isn't properly synchronized, you might see WHfB logon failures, or SSO failures to on-premises resources.
27+
28+
## Resolution
29+
30+
To troubleshoot this issue, we recommend using the script in [Scripts: View the certificate information in the msDS-KeyCredentialLink attribute from AD user objects](../../windows-server/support-tools/script-to-view-msds-keycredentiallink-attribute-value.md) to retrieve and analyze the certificate information.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
---
2+
title: Scripts to View the Certificate Information in the msDS-KeyCredentialLink Attribute
3+
description: This article introduces a script to view the certificate information in the msDS-KeyCredentialLink attribute from AD user objects.
4+
ms.date: 02/13/2025
5+
manager: dcscontentpm
6+
audience: itpro
7+
ms.topic: troubleshooting
8+
ms.reviewer: takondo
9+
ms.custom: sap:User Logon and Profiles\User profiles, csstroubleshoot
10+
---
11+
# Scripts: View the certificate information in the msDS-KeyCredentialLink attribute from AD user objects
12+
13+
The **msDS-KeyCredentialLink** attribute can be viewed in PowerShell. However, the value is in binary format and can't be read. This script helps you do the following things:
14+
15+
- Enumerate all users in Active Directory (AD) that have a non-null value in **msDS-KeyCredentialLink**.
16+
- Extract the bcrypt-sha256 key ID hash of each certificate saved in **msDS-KeyCredentialLink** and save it to a file.
17+
18+
You can then use the saved information to check whether the expected values are in the user's **msDS-KeyCredentialLink** attribute.
19+
20+
## Script
21+
22+
[!INCLUDE [Script disclaimer](../../includes/script-disclaimer.md)]
23+
24+
After you run the script, the results are saved in the file **C:\temp\KeyCredentialLink-report.txt**.
25+
26+
```powershell
27+
$outputfile = "C:\temp\KeyCredentialLink-report.txt"
28+
New-Item -ItemType file -Path $outputfile -Force | Out-Null
29+
30+
"Report generated on " + (Get-Date) | Out-File $outputfile
31+
32+
# Enumerate all AD users that has a msds-KeyCredentialLink value
33+
foreach ($user in (Get-ADUser -LDAPFilter '(msDS-KeyCredentialLink=*)' -Properties "msDS-KeyCredentialLink")) {
34+
35+
# For each user, output the UPN, DN, and all key IDs in msDS-KeyCredentialLink
36+
"===========`nUser: $($user.UserPrincipalName)`nDN: $($user.DistinguishedName)" | Out-File $outputfile -Append
37+
"KeyCredialLink Entries:" | Out-File $outputfile -Append
38+
" Source|Usage|DeviceID |KeyID" | Out-File $outputfile -Append
39+
" -------------------------------------------------------------------" | Out-File $outputfile -Append
40+
41+
foreach ($blob in ($user."msDS-KeyCredentialLink")) {
42+
$KCLstring = ($blob -split ':')[2]
43+
44+
# Check that the entries are version 2
45+
if ($KCLstring.Substring(0, 8) -eq "00020000") {
46+
$curIndex = 8
47+
48+
# Parse all KeyCredentialLink entries from the hex string
49+
while ($curIndex -lt $KCLstring.Length) {
50+
51+
# Read the length, reverse the byte order to account for endianess, then convert to an int
52+
# The length is in bytes, so multiply by 2 to get the length in characters
53+
$strLength = ($KCLstring.Substring($curIndex, 4)) -split '(?<=\G..)(?!$)'
54+
[array]::Reverse($strLength)
55+
$kcle_Length = ([convert]::ToInt16(-join $strLength, 16)) * 2
56+
57+
# Read the identifier and value
58+
$kcle_Identifier = $KCLstring.Substring($curIndex + 4, 2)
59+
$kcle_Value = $KCLstring.Substring($curIndex + 6, $kcle_Length)
60+
61+
switch ($kcle_Identifier) {
62+
# KeyID
63+
'01' {
64+
$KeyID = $kcle_Value
65+
}
66+
67+
# KeyUsage
68+
'04' {
69+
switch ($kcle_Value) {
70+
'01' { $Usage = "NGC " }
71+
'07' { $Usage = "FIDO " }
72+
'08' { $Usage = "FEK " }
73+
Default { $Usage = $kcle_Value }
74+
}
75+
}
76+
77+
# Source
78+
'05' {
79+
switch ($kcle_Value) {
80+
'00' { $Source = "AD " }
81+
'01' { $Source = "Entra " }
82+
Default { $Source = $kcle_Value }
83+
}
84+
}
85+
86+
# DeviceID
87+
'06' {
88+
$tempByteArray = $kcle_Value -split '(?<=\G..)(?!$)'
89+
$DeviceID = [System.Guid]::new($tempByteArray[3..0] + $tempByteArray[5..4] + $tempByteArray[7..6] + $tempByteArray[8..16] -join "")
90+
}
91+
}
92+
93+
$curIndex += 6 + $kcle_Length
94+
}
95+
96+
# Save the data to file
97+
" $Source|$Usage|$DeviceID|$KeyID" | Out-File $outputfile -Append
98+
}
99+
}
100+
}
101+
```
102+
103+
## Script sample output and analysis
104+
105+
Here's a sample output of the script:
106+
107+
```output
108+
109+
DN: CN=user1,OU=MyOU,DC=contoso,DC=com
110+
KeyCredialLink Entries:
111+
Source|Usage|DeviceID |KeyID
112+
-------------------------------------------------------------------
113+
Entra |NGC |8a763ab0-0f6f-44f3-99ae-599a6aaca45b|FD68391824C44158B23C5605F567A588D02C4B2962AC96B789EDBCE091CF5067
114+
Entra |NGC |9cf88f41-1e1e-462b-87d5-6938410ea82c|E91EF4E058513155A3F7E7E5B3E34951ADE923FFD0A7C24BB9957510F007E2F3
115+
Entra |NGC |d04581fc-d1c8-45fc-a5ad-192bc649574f|E60B476088CC5CDF6FB75454CFA6E17C3059D51F2CA1E414E1554715BE6C0527
116+
===========
117+
118+
DN: CN=user2,OU=MyOU,DC=contoso,DC=com
119+
KeyCredialLink Entries:
120+
Source|Usage|DeviceID |KeyID
121+
---------------------------
122+
Entra |NGC |c8fcc7a6-8f3f-4ec7-a90b-49c6988ba3a4|32EF67B902CB498710F0091F5B10B6A4A2F05D621B748B8150E08FA3048F227F
123+
```
124+
125+
Each `KeyCredentialLink` entry represents a certificate. The output contains the following information:
126+
127+
- `Source`: The source of the certificate. This information can either be from Microsoft Entra ID or on-premises AD.
128+
- `Usage`: The defined usage of the certificate. This information can be NGC (WHfB), FIDO, or FEK (File Encryption Key).
129+
- `DeviceID`: The ID of the computer where the certificate was created. This information is the Device ID in Microsoft Entra ID and the objectGUID in AD.
130+
- `KeyID`: The bcrypt-sha256 key ID hash of the certificate.
131+
132+
The matching certificate should be found in the user's personal certificate store on the computer with the matching `DeviceID`. To find the certificate being used on the client, you can run `certutil -v -user -store my` from a PowerShell or command prompt to dump detailed certificate information from the user's personal store. In this example, you should find a self-signed certificate where the subject and issuer are the same and in the form of `CN=<User SID>/login.windows.net/<Tenant ID>/<user UPN>`. Once you find this certificate, check the bcrypt-sha256 key ID hash. This hash value should match one of the entries in the **msDS-KeyCredentialLink** attribute.
133+
134+
Here's an excerpt from [email protected]'s certificate store:
135+
136+
```output
137+
> certutil -v -user -store my
138+
my "Personal"
139+
================ Certificate 0 ================
140+
X509 Certificate:
141+
Version: 3
142+
Serial Number: 184621…
143+
Signature Algorithm:
144+
Algorithm ObjectId: 1.2.840.113549.1.1.11 sha256RSA
145+
Algorithm Parameters:
146+
05 00
147+
Issuer:
148+
CN=S-1-5-21-394…7436/login.windows.net/ccf…83a/[email protected]
149+
150+
:
151+
152+
Signature Algorithm:
153+
Algorithm ObjectId: 1.2.840.113549.1.1.11 sha256RSA
154+
Algorithm Parameters:
155+
05 00
156+
Signature: UnusedBits=0
157+
0000 19 17 8f 65 c1 e3 f1 0a 3b 62 90 7f fa 94 13 ad
158+
// snip
159+
00f0 a2 e3 72 54 a5 0a 84 30 9e 8b 81 19 d3 61 46 58
160+
Signature matches Public Key
161+
Root Certificate: Subject matches Issuer
162+
Key Id Hash(rfc-sha1): 9a546…
163+
Key Id Hash(sha1): d66eb…
164+
Key Id Hash(bcrypt-sha1): 7f4a0…
165+
Key Id Hash(bcrypt-sha256): 32ef67b902cb…
166+
```
167+
168+
## Reference
169+
170+
[Key Credential Link Structures](/openspecs/windows_protocols/ms-adts/de61eb56-b75f-4743-b8af-e9be154b47af)

support/windows-server/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3119,6 +3119,8 @@ items:
31193119
href: ./support-tools/scripts-to-retrieve-profile-age.md
31203120
- name: Scripts to retrieve profile age and optionally delete aged copies
31213121
href: ./support-tools/scripts-retrieve-profile-age-delete-aged-copies.md
3122+
- name: Scripts to view the certificate information in the msDS-KeyCredentialLink attribute
3123+
href: ./support-tools/script-to-view-msds-keycredentiallink-attribute-value.md
31223124
- name: TroubleShootingScript toolset (TSS)
31233125
items:
31243126
- name: Introduction to TroubleShootingScript toolset (TSS)

0 commit comments

Comments
 (0)