Skip to content

Commit 7e43a1c

Browse files
Refactoring (#216)
* VS 2026 runner * Improved JSON parsing * Module exports test
1 parent f3c2ab5 commit 7e43a1c

46 files changed

Lines changed: 538 additions & 1817 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/autobuild.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ on:
1414
jobs:
1515
build:
1616
name: 'Build'
17-
runs-on: windows-2025
17+
runs-on: windows-2025-vs2026
1818
timeout-minutes: 10
1919
env:
2020
DOTNET_NOLOGO: true
@@ -106,7 +106,7 @@ jobs:
106106
shell: pwsh
107107
id: get-module-version
108108
run: |
109-
$version = Import-PowerShellDataFile -Path ./Src/DSInternals.PowerShell/DSInternals.psd1 | Select-Object -ExpandProperty ModuleVersion
109+
$version = (Test-ModuleManifest -Path ./Src/DSInternals.PowerShell/DSInternals.psd1 -ErrorAction SilentlyContinue).Version
110110
echo "version=$version" >> $env:GITHUB_OUTPUT
111111
112112
- name: Upload PowerShell Module as Artifact

.github/workflows/release.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ jobs:
143143
"DSInternals.Replication.Interop/Release_*/DSInternals.Replication.Interop.dll"
144144
"DSInternals.SAM/release_*/DSInternals.SAM.dll"
145145
"DSInternals.PowerShell/Release/DSInternals/*/DSInternals.PowerShell.dll"
146-
"DSInternals.PowerShell/Release/DSInternals/DSInternals.Bootstrap.psm1"
147146
"DSInternals.PowerShell/Release/DSInternals/DSInternals.psd1"
147+
"DSInternals.PowerShell/Release/DSInternals/Test-ModuleCompatibility.ps1"
148148
"DSInternals.PowerShell/Release/DSInternals/Integrity.Tests.ps1"
149149
"DSInternals.PowerShell/Release/DSInternals/DSInternals.types.ps1xml"
150150
"DSInternals.PowerShell/Release/DSInternals/Views/*.ps1xml"
@@ -217,8 +217,8 @@ jobs:
217217
shell: pwsh
218218
id: get-module-info
219219
run: |
220-
[hashtable] $manifest = Import-PowerShellDataFile -Path ./Src/DSInternals.PowerShell/DSInternals.psd1
221-
[string] $version = $manifest.ModuleVersion
220+
[System.Management.Automation.PSModuleInfo] $manifest = Test-ModuleManifest -Path ./Src/DSInternals.PowerShell/DSInternals.psd1 -ErrorAction SilentlyContinue
221+
[string] $version = $manifest.Version
222222
[string] $releaseNotes = $manifest.PrivateData.PSData.ReleaseNotes
223223
224224
echo "version=$version" >> $env:GITHUB_OUTPUT
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,22 @@
33
## Project Overview
44

55
DSInternals is a .NET-based project consisting of:
6+
67
- **DSInternals Framework**: A .NET library exposing internal Active Directory features
78
- **DSInternals PowerShell Module**: PowerShell cmdlets built on top of the Framework
89

910
The project primarily focuses on Active Directory security auditing, offline database manipulation, and password management.
1011

11-
Read the [README.md](README.md), [CHANGELOG.md](../Documentation/CHANGELOG.md), and [CONTRIBUTING.md](CONTRIBUTING.md) files
12-
and the [PowerShell module documentation](../Documentation/PowerShell/Readme.md) for further context.
12+
Read the [README.md](.github/README.md), [CHANGELOG.md](Documentation/CHANGELOG.md), and [CONTRIBUTING.md](.github/CONTRIBUTING.md) files
13+
and the [PowerShell module documentation](Documentation/PowerShell/Readme.md) for further context.
1314

1415
## Technology Stack
1516

1617
- **Languages**: C#, C++/CLI, PowerShell
1718
- **Frameworks**: .NET Framework 4.8, .NET 10.0 (Windows-only)
1819
- **Build System**: MSBuild with Central Package Management
1920
- **Testing**: MSTest with Microsoft.Testing.Platform runner
20-
- **IDE**: Visual Studio 2026+ with the [DSInternals.slnx](../Src/DSInternals.slnx) solution
21+
- **IDE**: Visual Studio 2026+ with the [DSInternals.slnx](Src/DSInternals.slnx) solution
2122

2223
## Build Instructions
2324

@@ -80,5 +81,3 @@ powershell -File ./Invoke-SmokeTests.ps1 -Configuration 'Debug'
8081
# Run smoke tests in PowerShell Core
8182
pwsh -File ./Invoke-SmokeTests.ps1 -Configuration 'Debug'
8283
```
83-
84-

Documentation/CHANGELOG.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,29 @@
55

66
All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
77

8+
## [Unreleased]
9+
10+
### Added
11+
12+
- Instructions and prompts for GitHub Copilot.
13+
14+
### Changed
15+
16+
- A new code signing certificate has been obtained from [DigiCert](https://www.digicert.com/).
17+
- Merged the `*.psm1` script bootstrapper of the binary PowerShell module into the `*.psd1` module manifest.
18+
- Improved the structure of the `DSInternals.Replication` NuGet package,
19+
which now includes assemblies for all processor architectures and the Visual C++ runtime.
20+
21+
### Fixed
22+
23+
- Improved generation of NGC keys stored in the `msDS-KeyCredentialLink` AD attribute
24+
to pass new validation constraints introduced in January 2026 Windows updates.
25+
26+
### Removed
27+
28+
- PowerShell cmdlets and all code related to the [decommissioned Azure AD Graph API](https://learn.microsoft.com/en-us/graph/migrate-azure-ad-graph-overview)
29+
have been removed.
30+
831
## [6.2] - 2025-12-05
932

1033
> [!WARNING]
@@ -296,14 +319,14 @@ This is a PowerShell-only release.
296319

297320
### Added
298321

299-
- The new [Set-AzureADUserEx](PowerShell/Set-AzureADUserEx.md#set-azureaduserex) cmdlet can be used to revoke FIDO2 and NGC keys in Azure Active Directory.
322+
- The new `Set-AzureADUserEx` cmdlet can be used to revoke FIDO2 and NGC keys in Azure Active Directory.
300323

301324
## [4.3] - 2020-04-02
302325

303326
### Added
304327

305328
- New logo and package icons!
306-
- The new [Get-AzureADUserEx](PowerShell/Get-AzureADUserEx.md#get-azureaduserex) cmdlet can be used to retrieve FIDO and NGC keys from Azure Active Directory, as the first tool on the market.
329+
- The new `Get-AzureADUserEx` cmdlet can be used to retrieve FIDO and NGC keys from Azure Active Directory, as the first tool on the market.
307330
- Both [lastLogon](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-ada1/93258066-276d-4357-8458-981c19caad95) and [lastLogonTimestamp](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-ada1/530d7194-20f6-4aaa-8d80-9ca6b6350ad6) user account attributes are now exposed. The LastLogonDate PowerShell property returns whichever of these 2 values is available.
308331
- The `-Server` parameter of the [Get-ADSIAccount](PowerShell/Get-ADSIAccount.md#get-adsiaccount) cmdlet now has the standard `-ComputerName` alias.
309332

Documentation/PowerShell/Add-ADReplNgcKey.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,31 @@ Composes and updates the msDS-KeyCredentialLink value on an object through the M
1515
### ByName
1616
```
1717
Add-ADReplNgcKey -PublicKey <Byte[]> [-SamAccountName] <String> [[-Domain] <String>] -Server <String>
18-
[-Credential <PSCredential>] [-Protocol <RpcProtocol>] [<CommonParameters>]
18+
[-Credential <PSCredential>] [<CommonParameters>]
1919
```
2020

2121
### ByUPN
2222
```
2323
Add-ADReplNgcKey -PublicKey <Byte[]> -UserPrincipalName <String> -Server <String> [-Credential <PSCredential>]
24-
[-Protocol <RpcProtocol>] [<CommonParameters>]
24+
[<CommonParameters>]
2525
```
2626

2727
### BySID
2828
```
2929
Add-ADReplNgcKey -PublicKey <Byte[]> -ObjectSid <SecurityIdentifier> -Server <String>
30-
[-Credential <PSCredential>] [-Protocol <RpcProtocol>] [<CommonParameters>]
30+
[-Credential <PSCredential>] [<CommonParameters>]
3131
```
3232

3333
### ByDN
3434
```
3535
Add-ADReplNgcKey -PublicKey <Byte[]> [-DistinguishedName] <String> -Server <String>
36-
[-Credential <PSCredential>] [-Protocol <RpcProtocol>] [<CommonParameters>]
36+
[-Credential <PSCredential>] [<CommonParameters>]
3737
```
3838

3939
### ByGuid
4040
```
4141
Add-ADReplNgcKey -PublicKey <Byte[]> -ObjectGuid <Guid> -Server <String> [-Credential <PSCredential>]
42-
[-Protocol <RpcProtocol>] [<CommonParameters>]
42+
[<CommonParameters>]
4343
```
4444

4545
## DESCRIPTION

Documentation/PowerShell/Get-ADReplAccount.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,42 +15,38 @@ Reads one or more accounts through the MS-DRSR protocol, including secret attrib
1515
### All
1616
```
1717
Get-ADReplAccount [-All] [-NamingContext <String>] [-Properties <AccountPropertySets>]
18-
[-ExportFormat <AccountExportFormat>] -Server <String> [-Credential <PSCredential>] [-Protocol <RpcProtocol>]
19-
[<CommonParameters>]
18+
[-ExportFormat <AccountExportFormat>] -Server <String> [-Credential <PSCredential>] [<CommonParameters>]
2019
```
2120

2221
### ByName
2322
```
2423
Get-ADReplAccount [-Properties <AccountPropertySets>] [-ExportFormat <AccountExportFormat>]
2524
[-SamAccountName] <String> [[-Domain] <String>] -Server <String> [-Credential <PSCredential>]
26-
[-Protocol <RpcProtocol>] [<CommonParameters>]
25+
[<CommonParameters>]
2726
```
2827

2928
### ByUPN
3029
```
3130
Get-ADReplAccount [-Properties <AccountPropertySets>] [-ExportFormat <AccountExportFormat>]
32-
-UserPrincipalName <String> -Server <String> [-Credential <PSCredential>] [-Protocol <RpcProtocol>]
33-
[<CommonParameters>]
31+
-UserPrincipalName <String> -Server <String> [-Credential <PSCredential>] [<CommonParameters>]
3432
```
3533

3634
### BySID
3735
```
3836
Get-ADReplAccount [-Properties <AccountPropertySets>] [-ExportFormat <AccountExportFormat>]
39-
-ObjectSid <SecurityIdentifier> -Server <String> [-Credential <PSCredential>] [-Protocol <RpcProtocol>]
40-
[<CommonParameters>]
37+
-ObjectSid <SecurityIdentifier> -Server <String> [-Credential <PSCredential>] [<CommonParameters>]
4138
```
4239

4340
### ByDN
4441
```
4542
Get-ADReplAccount [-Properties <AccountPropertySets>] [-ExportFormat <AccountExportFormat>]
46-
[-DistinguishedName] <String> -Server <String> [-Credential <PSCredential>] [-Protocol <RpcProtocol>]
47-
[<CommonParameters>]
43+
[-DistinguishedName] <String> -Server <String> [-Credential <PSCredential>] [<CommonParameters>]
4844
```
4945

5046
### ByGuid
5147
```
5248
Get-ADReplAccount [-Properties <AccountPropertySets>] [-ExportFormat <AccountExportFormat>] -ObjectGuid <Guid>
53-
-Server <String> [-Credential <PSCredential>] [-Protocol <RpcProtocol>] [<CommonParameters>]
49+
-Server <String> [-Credential <PSCredential>] [<CommonParameters>]
5450
```
5551

5652
## DESCRIPTION

Documentation/PowerShell/Get-ADReplBackupKey.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ Reads the DPAPI backup keys from a domain controller through the MS-DRSR protoco
1313
## SYNTAX
1414

1515
```
16-
Get-ADReplBackupKey [-Domain <String>] -Server <String> [-Credential <PSCredential>] [-Protocol <RpcProtocol>]
17-
[<CommonParameters>]
16+
Get-ADReplBackupKey [-Domain <String>] -Server <String> [-Credential <PSCredential>] [<CommonParameters>]
1817
```
1918

2019
## DESCRIPTION

Documentation/PowerShell/Get-ADReplKdsRootKey.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ Fetches the specified KDS Root Key through the MS-DRSR protocol.
1313
## SYNTAX
1414

1515
```
16-
Get-ADReplKdsRootKey [-RootKeyId] <Guid> -Server <String> [-Credential <PSCredential>]
17-
[-Protocol <RpcProtocol>] [<CommonParameters>]
16+
Get-ADReplKdsRootKey [-RootKeyId] <Guid> -Server <String> [-Credential <PSCredential>] [<CommonParameters>]
1817
```
1918

2019
## DESCRIPTION

Src/DSInternals.Common.Test/KeyCredentialTester.cs

Lines changed: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -14,59 +14,10 @@ private static KeyCredential CreateSampleKey()
1414
{
1515
byte[] publicKey = "525341310008000003000000000100000000000000000000010001C1A78914457758B0B13C70C710C7F8548F3F9ED56AD4640B6E6A112655C98ECAC1CBD68A298F5686C08439428A97FE6FDF58D78EA481905182BAD684C2D9C5CDE1CDE34AA19742E8BBF58B953EAC4C562FCF598CC176B02DBE9FFFEF5937A65815C236F92892F7E511A1FEDD5483CB33F1EA715D68106180DED2432A293367114A6E325E62F93F73D7ECE4B6A2BCDB829D95C8645C3073B94BA7CB7515CD29042F0967201C6E24A77821E92A6C756DF79841ACBAAE11D90CA03B9FCD24EF9E304B5D35248A7BD70557399960277058AE3E99C7C7E2284858B7BF8B08CDD286964186A50A7FCBCC6A24F00FEE5B9698BBD3B1AEAD0CE81FEA461C0ABD716843A5".HexToBinary();
1616
Guid deviceId = Guid.Parse("47f577e3-d2d0-4a0a-8aca-e0501098bde4");
17-
DateTime creationTime = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);
17+
DateTime creationTime = new(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);
1818
return new KeyCredential(publicKey, deviceId, DummyDN, creationTime);
1919
}
2020

21-
[TestMethod]
22-
public void KeyCredential_RoundTrip_DoubleQuoted()
23-
{
24-
KeyCredential key = CreateSampleKey();
25-
string json = key.ToJson();
26-
KeyCredential result = KeyCredential.ParseJson(json);
27-
Assert.IsNotNull(result);
28-
Assert.AreEqual(key.Identifier, result.Identifier);
29-
Assert.AreEqual(key.DeviceId, result.DeviceId);
30-
Assert.AreEqual(key.CreationTime, result.CreationTime);
31-
}
32-
33-
[TestMethod]
34-
public void KeyCredential_Deserialize_SingleQuoted_Input()
35-
{
36-
KeyCredential key = CreateSampleKey();
37-
string json = key.ToJson().Replace('"', '\'');
38-
KeyCredential result = KeyCredential.ParseJson(json);
39-
Assert.IsNotNull(result);
40-
Assert.AreEqual(key.DeviceId, result.DeviceId);
41-
}
42-
43-
[TestMethod]
44-
public void KeyCredential_Deserialize_TrailingComma_And_Comments()
45-
{
46-
KeyCredential key = CreateSampleKey();
47-
string json = key.ToJson();
48-
string jsonWithComment = json.Insert(1, "\n // comment\n");
49-
jsonWithComment = jsonWithComment.Substring(0, jsonWithComment.Length - 1) + ",\n}";
50-
KeyCredential result = KeyCredential.ParseJson(jsonWithComment);
51-
Assert.IsNotNull(result);
52-
Assert.AreEqual(key.DeviceId, result.DeviceId);
53-
}
54-
55-
[TestMethod]
56-
public void Parse_SingleQuoted_WithEscapedApostrophe_Works()
57-
{
58-
var jsonSingleQuoted = "{ 'OwnerDN':'CN=O\\'Connor,DC=contoso,DC=com', 'IsComputerKey': false }";
59-
var obj = KeyCredential.ParseJson(jsonSingleQuoted);
60-
Assert.IsNotNull(obj);
61-
}
62-
63-
[TestMethod]
64-
public void Parse_BadJson_StillThrows()
65-
{
66-
var bad = "{ \"OwnerDN\": \"CN=User,DC=contoso,DC=com\" ";
67-
Assert.ThrowsExactly<JsonException>(() => _ = KeyCredential.ParseJson(bad));
68-
}
69-
7021
[TestMethod]
7122
public void KeyCredential_Parse_NonMFAKey()
7223
{
@@ -500,7 +451,7 @@ public void KeyCredential_Parse_DeviceKey_TPM()
500451
Assert.AreEqual(KeyFlags.None, key.CustomKeyInfo.Flags);
501452
Assert.AreEqual("OoYGWyO8xBkLx3x2d0QltMav9+41lY0sGxKIMPF9if0=", key.Identifier);
502453
Assert.AreEqual(key.LastLogonTime, key.CreationTime);
503-
Assert.AreEqual("2019-08-02T18:11:37.5665512Z", key.CreationTime.ToUniversalTime().ToString("o"));
454+
Assert.AreEqual("2019-08-02T18:11:37.5665512Z", key.CreationTime.Value.ToUniversalTime().ToString("o"));
504455
Assert.AreEqual(0x304, key.RawKeyMaterial.Length);
505456

506457
// Serialize
@@ -542,7 +493,7 @@ public void KeyCredential_Parse_Impacket()
542493
Assert.AreEqual(KeySource.AD, key.Source);
543494

544495
// Bug in Impacket causes a time skew of 1600 years:
545-
Assert.AreEqual(3625, key.CreationTime.Year);
496+
Assert.AreEqual(3625, key.CreationTime.Value.Year);
546497

547498
// Serialize
548499
byte[] serialized = key.ToByteArray();
@@ -604,7 +555,7 @@ public void KeyCredential_Generate_CompouterKey_FromPublicKey()
604555
Assert.AreEqual(KeyCredentialVersion.Version2, key.Version);
605556
Assert.AreEqual(KeyUsage.NGC, key.Usage);
606557
Assert.AreEqual(KeySource.AD, key.Source);
607-
Assert.IsNull(key.CustomKeyInfo);
558+
Assert.AreEqual(KeyFlags.MFANotUsed, key.CustomKeyInfo.Flags);
608559
Assert.IsNull(key.LastLogonTime);
609560
}
610561

@@ -625,7 +576,7 @@ public void KeyCredential_Generate_CompouterKey_FromCertificate()
625576
Assert.AreEqual(KeyCredentialVersion.Version2, key.Version);
626577
Assert.AreEqual(KeyUsage.NGC, key.Usage);
627578
Assert.AreEqual(KeySource.AD, key.Source);
628-
Assert.IsNull(key.CustomKeyInfo);
579+
Assert.AreEqual(KeyFlags.MFANotUsed, key.CustomKeyInfo.Flags);
629580
Assert.IsNull(key.LastLogonTime);
630581
}
631582
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using DSInternals.Common.Data;
2+
3+
namespace DSInternals.Common.Test;
4+
5+
/// <summary>
6+
/// Contains tests for parsing the key material corresponding to KEY_USAGE_FIDO.
7+
/// </summary>
8+
[TestClass]
9+
public class KeyMaterialFidoTester
10+
{
11+
[TestMethod]
12+
public void FidoKeyMaterial_Parse_FIDO_Input1()
13+
{
14+
string encodedKeyMaterial = "eyJ2ZXJzaW9uIjoxLCJhdXRoRGF0YSI6Ik5XeWUxS0NUSWJscFh4NnZrWUlEOGJWZmFKMm1IN3lXR0V3VmZkcG9ESUhGQUFBQUdjdHBTQjZQOTBBNWsrd0tKeW1oVktnQUVCckljaURTekdqanNLcmRTelZJdElHbEFRSURKaUFCSVZnZ29uTkNlY2EwZE5LTzlaWXBGdjlvMCtlZ0VGQ1ZTeXJ0UmN1NndrMStoT2dpV0NDNWE3MFI0ZlNFZ0ZpNWxnQTRBVVBmN1Y0Q2p5VWcvb1VWTFEzem5kZnc1YUZyYUcxaFl5MXpaV055WlhUMSIsIng1YyI6WyJNSUlDdlRDQ0FhV2dBd0lCQWdJRUdLeEd3REFOQmdrcWhraUc5dzBCQVFzRkFEQXVNU3d3S2dZRFZRUURFeU5aZFdKcFkyOGdWVEpHSUZKdmIzUWdRMEVnVTJWeWFXRnNJRFExTnpJd01EWXpNVEFnRncweE5EQTRNREV3TURBd01EQmFHQTh5TURVd01Ea3dOREF3TURBd01Gb3diakVMTUFrR0ExVUVCaE1DVTBVeEVqQVFCZ05WQkFvTUNWbDFZbWxqYnlCQlFqRWlNQ0FHQTFVRUN3d1pRWFYwYUdWdWRHbGpZWFJ2Y2lCQmRIUmxjM1JoZEdsdmJqRW5NQ1VHQTFVRUF3d2VXWFZpYVdOdklGVXlSaUJGUlNCVFpYSnBZV3dnTkRFek9UUXpORGc0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFZWVvN0xIeEpjQkJpSXd6U1ArdGc1U2t4Y2RTRDhRQytoWjFyRDRPWEF3RzFSczNVYnMvSzQrUHpENEhwN1dLOUpvMU1IcjAzczd5K2txakNydXRPT3FOc01Hb3dJZ1lKS3dZQkJBR0N4QW9DQkJVeExqTXVOaTR4TGpRdU1TNDBNVFE0TWk0eExqY3dFd1lMS3dZQkJBR0M1UndDQVFFRUJBTUNCU0F3SVFZTEt3WUJCQUdDNVJ3QkFRUUVFZ1FReTJsSUhvLzNRRG1UN0FvbkthRlVxREFNQmdOVkhSTUJBZjhFQWpBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1huUU9YMkdENEx1RmRNUng1YnJyN0l2cW40SVRadXJUR0c3dFg4K2Ewd1lwSU43aGNQRTdiNUlORDlOYWwyYkhPMm9yaC90U1JLU0Z6Qlk1ZTRjdmRhOXJBZFZmR29PalRhQ1c2Rlo1L3RhMk0ydmdFaG96NURvOGZpdW9Yd0JhMVhDcDYxSmZJbFB0eDExUFhtNXBJUzJ3M2JYSTdtWTB1SFVNR3Z4QXp0YTc0ektYTHNsYUxhU1FpYlNLaldLdDloK1NzWHk0SkdxY1ZlZk9sYVFsSmZYTDFUZ2E2d2NPMFFUdTZYcStVdzdaUE5QbnJwQnJMYXVLRGQyMDJSbE40U1A3b2hMM2Q5Ykc2VjVoVXovM091c05FQlpVbjVXM1ZtUGoxWm5GYXZrTUIzUmtSTU9hNThNWkFPUkpUNGltQVB6cnZKMHZ0djk0L3k3MUM2dFo1Il0sImRpc3BsYXlOYW1lIjoiWXViaUtleSA1In0=";
15+
byte[] keyMaterialBytes = Convert.FromBase64String(encodedKeyMaterial);
16+
17+
// Parse the FIDO key
18+
var keyMaterial = KeyMaterialFido.ParseJson(keyMaterialBytes);
19+
20+
// Check all fields
21+
Assert.IsNotNull(keyMaterial);
22+
Assert.AreEqual(1, keyMaterial.Version);
23+
Assert.AreEqual(1, keyMaterial.AttestationCertificatesRaw?.Length);
24+
Assert.AreEqual("YubiKey 5", keyMaterial.DisplayName);
25+
Assert.AreEqual("e7d092ba192fdbbb2f36552832d616126971a269", keyMaterial.AttestationCertificates[0].Thumbprint.ToLowerInvariant());
26+
Assert.AreEqual("cb69481e-8ff7-4039-93ec-0a2729a154a8", keyMaterial.AuthenticatorData?.AttestedCredentialData.AaGuid.ToString());
27+
}
28+
29+
[TestMethod]
30+
public void FidoKeyMaterial_Parse_FIDO_Input2()
31+
{
32+
string encodedKeyMaterial = "eyJ2ZXJzaW9uIjoxLCJhdXRoRGF0YSI6Ik5XeWUxS0NUSWJscFh4NnZrWUlEOGJWZmFKMm1IN3lXR0V3VmZkcG9ESUhGQUFBRmRoTGUxMFZMN1VmVXE2cm5FL1VkWTVNQUlKVW96bENOMTFMWmFFOFF0SFhWU2JUeXltVEVNaWxpcTA0RjFtblJwaC9YcFFFQ0F5WWdBU0ZZSVAzWms5Vm5URUpONlQ4VWgxMHRPcUpmdDRicW1leVJUUzhvNkJQK2prYkNJbGdnYjFnR0dNMnM3T2dDMTNQdkZBVTJ1UDZJcVVGWWdiaGxQc3diUzRDK1FoV2hhMmh0WVdNdGMyVmpjbVYwOVE9PSIsIng1YyI6WyJNSUlDUXpDQ0FlbWdBd0lCQWdJUUhmSzFXbEhjUzJpRm85bWVhWC90RmpBS0JnZ3Foa2pPUFFRREFqQkpNUXN3Q1FZRFZRUUdFd0pWVXpFZE1Cc0dBMVVFQ2d3VVJtVnBkR2xoYmlCVVpXTm9ibTlzYjJkcFpYTXhHekFaQmdOVkJBTU1Fa1psYVhScFlXNGdSa2xFVHlCRFFTQXdNekFnRncweE9ERXlNalV3TURBd01EQmFHQTh5TURNek1USXlOREl6TlRrMU9Wb3djREVMTUFrR0ExVUVCaE1DVlZNeEhUQWJCZ05WQkFvTUZFWmxhWFJwWVc0Z1ZHVmphRzV2Ykc5bmFXVnpNU0l3SUFZRFZRUUxEQmxCZFhSb1pXNTBhV05oZEc5eUlFRjBkR1Z6ZEdGMGFXOXVNUjR3SEFZRFZRUUREQlZHVkNCQ2FXOVFZWE56SUVaSlJFOHlJREEwTnpBd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTNjJoSWJ5ZW5IOVdQbnpZSGVoYUJSM0M3cXN3b21aa2FQekd5VWxGUmlKSU1vM3VJVGVJbUZPRmZOY0R1T3pvcTF3Y1hYR1RtRXRFdHhGMndvOW5va280R0pNSUdHTUIwR0ExVWREZ1FXQkJTQkkxWG9MRFkxL0hKYWJhK1czMm54aHhwM1dqQWZCZ05WSFNNRUdEQVdnQlJCdC94TmRjcU8wcDhzMHhlYnpZTlJpbm5ZcVRBTUJnTlZIUk1CQWY4RUFqQUFNQk1HQ3lzR0FRUUJndVVjQWdFQkJBUURBZ1J3TUNFR0N5c0dBUVFCZ3VVY0FRRUVCQklFRUJMZTEwVkw3VWZVcTZybkUvVWRZNU13Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUk2R1NWaTEwcjY3M3VxdHNvKzJvQjZmNVM1Z0UwZmY0NHQzTmNRK1ROOU5BaUFDL1NDUCtlS3cxQm5tY1NnYnhjUXBZdVdqQlBNVkRmcWVnOHBibU9kSEt3PT0iLCJvQWpxMkNlTkEyL2d5NW14YzV0Vll4NVJ4RWFMektEMWxhMDdhZ3RmR28wPSJdLCJkaXNwbGF5TmFtZSI6IkZlaXRpYW4gQWxsLUluLVBhc3MifQ==";
33+
byte[] keyMaterialBytes = Convert.FromBase64String(encodedKeyMaterial);
34+
35+
// Parse the FIDO key and check all fields
36+
var keyMaterial = KeyMaterialFido.ParseJson(keyMaterialBytes);
37+
Assert.AreEqual("Feitian All-In-Pass", keyMaterial.DisplayName);
38+
Assert.AreEqual("9e3808651ecfa53162772e9d2bc62bfe568350a9", keyMaterial.AttestationCertificates[0].Thumbprint.ToLowerInvariant());
39+
Assert.AreEqual("12ded745-4bed-47d4-abaa-e713f51d6393", keyMaterial.AuthenticatorData.AttestedCredentialData.AaGuid.ToString());
40+
}
41+
42+
[TestMethod]
43+
public void FidoKeyMaterial_Parse_Null()
44+
{
45+
Assert.IsNull(KeyMaterialFido.ParseJson((string)null));
46+
}
47+
48+
[TestMethod]
49+
public void FidoKeyMaterial_Parse_Empty_String()
50+
{
51+
Assert.IsNull(KeyMaterialFido.ParseJson(String.Empty));
52+
}
53+
54+
[TestMethod]
55+
public void FidoKeyMaterial_Parse_Empty_Array()
56+
{
57+
Assert.IsNull(KeyMaterialFido.ParseJson([]));
58+
}
59+
}

0 commit comments

Comments
 (0)