Skip to content

Commit 3b1c965

Browse files
committed
tests: add TestKeyExtensions for converting keys to PIV formats and update key retrieval methods
1 parent 82da8b1 commit 3b1c965

File tree

4 files changed

+137
-36
lines changed

4 files changed

+137
-36
lines changed

Yubico.YubiKey/tests/unit/Yubico/YubiKey/TestUtilities/TestKeysTests.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class TestKeysTests
99
[Fact]
1010
public void TestKey_LoadRSA_CanReadPublicKey()
1111
{
12-
var key = TestKeys.GetKey("rsa4096", false);
12+
var key = TestKeys.GetPublicKey("rsa4096");
1313

1414
var rsaKey = key.AsRSA();
1515
Assert.NotNull(rsaKey);
@@ -19,7 +19,7 @@ public void TestKey_LoadRSA_CanReadPublicKey()
1919
[Fact]
2020
public void TestKey_LoadRSA_CanReadPrivateKey()
2121
{
22-
var key = TestKeys.GetKey("rsa4096", true);
22+
var key = TestKeys.GetPrivateKey("rsa4096");
2323

2424
var rsaKey = key.AsRSA();
2525
Assert.NotNull(rsaKey);
@@ -29,7 +29,7 @@ public void TestKey_LoadRSA_CanReadPrivateKey()
2929
[Fact]
3030
public void TestKey_LoadECDsa_CanReadPublicKey()
3131
{
32-
var key = TestKeys.GetKey("p384", false);
32+
var key = TestKeys.GetPublicKey("p384");
3333

3434
var ecKey = key.AsECDsa();
3535
Assert.NotNull(ecKey);
@@ -38,23 +38,23 @@ public void TestKey_LoadECDsa_CanReadPublicKey()
3838
[Fact]
3939
public void TestKey_LoadECDsa_ThrowsOnRSAKey()
4040
{
41-
var key = TestKeys.GetKey("rsa4096", false);
41+
var key = TestKeys.GetPublicKey("rsa4096");
4242

4343
Assert.Throws<InvalidOperationException>(() => key.AsECDsa());
4444
}
4545

4646
[Fact]
4747
public void TestKey_LoadRSA_ThrowsOnECKey()
4848
{
49-
var key = TestKeys.GetKey("p384", false);
49+
var key = TestKeys.GetPublicKey("p384");
5050

5151
Assert.Throws<InvalidOperationException>(() => key.AsRSA());
5252
}
5353

5454
[Fact]
5555
public void TestKey_AsBase64_StripsHeaders()
5656
{
57-
var key = TestKeys.GetKey("rsa4096", false);
57+
var key = TestKeys.GetPublicKey("rsa4096");
5858

5959
string base64 = key.AsBase64();
6060
Assert.DoesNotContain("-----BEGIN", base64);
@@ -65,9 +65,9 @@ public void TestKey_AsBase64_StripsHeaders()
6565
[Fact]
6666
public void TestKey_AsPemBase64_PreservesFormat()
6767
{
68-
var key = TestKeys.GetKey("rsa4096", false);
68+
var key = TestKeys.GetPublicKey("rsa4096");
6969

70-
string pem = key.AsPem();
70+
string pem = key.AsPemString();
7171
Assert.Contains("-----BEGIN PUBLIC KEY-----", pem);
7272
Assert.Contains("-----END PUBLIC KEY-----", pem);
7373
}
@@ -94,7 +94,7 @@ public void TestCertificate_Load_CanReadAttestationCertificate()
9494
[Fact]
9595
public void TestKey_Load_ThrowsOnMissingFile()
9696
{
97-
Assert.Throws<FileNotFoundException>(() => TestKeys.GetKey("invalid", false));
97+
Assert.Throws<FileNotFoundException>(() => TestKeys.GetPublicKey("invalid"));
9898
}
9999

100100
[Fact]

Yubico.YubiKey/tests/utilities/Yubico/YubiKey/TestUtilities/SampleKeyPairs.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public static bool GetMatchingKeyAndCert(
3535
}
3636

3737
cert = TestKeys.GetCertificate(curve).AsX509Certificate2();
38-
privateKey = TestKeys.GetKey(curve, true).AsPrivateKey();
38+
privateKey = TestKeys.GetPrivateKey(curve).AsPrivateKey();
3939
return true;
4040
}
4141

@@ -56,30 +56,30 @@ public static bool GetKeysAndCertPem(
5656
}
5757

5858
var testCert = TestKeys.GetCertificate(curve, validAttest);
59-
var testPrivKey = TestKeys.GetKey(curve, true);
60-
var testPubKey = TestKeys.GetKey(curve, false);
59+
var testPrivKey = TestKeys.GetPrivateKey(curve);
60+
var testPubKey = TestKeys.GetPublicKey(curve);
6161

62-
cert = testCert.AsPem();
63-
privateKey = testPrivKey.AsPem();
64-
publicKey = testPubKey.AsPem();
62+
cert = testCert.AsPemString();
63+
privateKey = testPrivKey.AsPemString();
64+
publicKey = testPubKey.AsPemString();
6565
return true;
6666
}
6767

6868
public static PivPublicKey GetPivPublicKey(PivAlgorithm algorithm)
6969
{
70-
string curve = GetCurveFromAlgorithm(algorithm);
71-
return TestKeys.GetKey(curve, false).AsPublicKey();
70+
var curve = GetCurveFromAlgorithm(algorithm);
71+
return TestKeys.GetPublicKey(curve).AsPublicKey();
7272
}
7373

7474
public static PivPrivateKey GetPivPrivateKey(PivAlgorithm algorithm)
7575
{
76-
string curve = GetCurveFromAlgorithm(algorithm);
77-
return TestKeys.GetKey(curve, true).AsPrivateKey();
76+
var curve = GetCurveFromAlgorithm(algorithm);
77+
return TestKeys.GetPrivateKey(curve).AsPrivateKey();
7878
}
7979

8080
public static X509Certificate2 GetCert(PivAlgorithm algorithm)
8181
{
82-
string curve = GetCurveFromAlgorithm(algorithm);
82+
var curve = GetCurveFromAlgorithm(algorithm);
8383
return TestKeys.GetCertificate(curve).AsX509Certificate2();
8484
}
8585

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using Yubico.YubiKey.Piv;
2+
3+
namespace Yubico.YubiKey.TestUtilities
4+
{
5+
public static class TestKeyExtensions
6+
{
7+
/// <summary>
8+
/// Converts the key to a PIV private key format.
9+
/// </summary>
10+
/// <returns>PivPrivateKey instance</returns>
11+
public static PivPrivateKey AsPrivateKey(this TestKey key)
12+
{
13+
return new KeyConverter(key.AsPemString()).GetPivPrivateKey();
14+
}
15+
16+
/// <summary>
17+
/// Converts the key to a PIV public key format.
18+
/// </summary>
19+
/// <returns>PivPublicKey instance</returns>
20+
public static PivPublicKey AsPublicKey(this TestKey key)
21+
22+
{
23+
return new KeyConverter(key.AsPemString()).GetPivPublicKey();
24+
}
25+
}
26+
}
27+

Yubico.YubiKey/tests/utilities/Yubico/YubiKey/TestUtilities/TestKeys.cs

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,35 @@ public abstract class TestCrypto
1717
protected readonly byte[] _bytes;
1818
protected readonly string _pemStringFull;
1919

20+
/// <summary>
21+
/// Initializes a new instance of TestCrypto with PEM-encoded data from a file.
22+
/// </summary>
23+
/// <param name="filePath">Path to the PEM file containing cryptographic data.</param>
2024
protected TestCrypto(string filePath)
2125
{
22-
var pemString = File.ReadAllText(filePath);
23-
_pemStringFull = pemString.Replace("\n", "").Trim();
26+
_pemStringFull = File
27+
.ReadAllText(filePath)
28+
.Replace("\n", "")
29+
.Trim();
2430
_bytes = GetBytesFromPem(_pemStringFull);
2531
}
2632

33+
/// <summary>
34+
/// Returns the raw byte representation of the cryptographic data.
35+
/// </summary>
36+
/// <returns>Byte array containing the decoded cryptographic data.</returns>
2737
public byte[] AsRawBytes() => _bytes;
28-
public string AsPem() => _pemStringFull;
38+
39+
/// <summary>
40+
/// Returns the complete PEM-encoded string representation.
41+
/// </summary>
42+
/// <returns>String containing the full PEM data including headers and footers.</returns>
43+
public string AsPemString() => _pemStringFull;
44+
45+
/// <summary>
46+
/// Returns the Base64-encoded data without PEM headers and footers.
47+
/// </summary>
48+
/// <returns>Base64 string of the cryptographic data.</returns>
2949
public string AsBase64() => StripPemHeaderFooter(_pemStringFull);
3050

3151
private static byte[] GetBytesFromPem(string pemData)
@@ -53,17 +73,32 @@ private static string StripPemHeaderFooter(string pemData)
5373
}
5474
}
5575

76+
/// <summary>
77+
/// Represents a cryptographic key for testing purposes, supporting both RSA and EC keys.
78+
/// Provides conversion methods to standard .NET cryptographic types.
79+
/// </summary>
5680
public class TestKey : TestCrypto
5781
{
5882
private readonly string _curve;
5983
private readonly bool _isPrivate;
6084

85+
/// <summary>
86+
/// Loads a test key from the TestData directory.
87+
/// </summary>
88+
/// <param name="curve">The curve or key type (e.g., "rsa2048", "secp256r1")</param>
89+
/// <param name="isPrivate">True for private key, false for public key</param>
90+
/// <returns>A TestKey instance representing the loaded key</returns>
6191
private TestKey(string filePath, string curve, bool isPrivate) : base(filePath)
6292
{
6393
_curve = curve;
6494
_isPrivate = isPrivate;
6595
}
6696

97+
/// <summary>
98+
/// Converts the key to an RSA instance if it represents an RSA key.
99+
/// </summary>
100+
/// <returns>RSA instance initialized with the key data</returns>
101+
/// <exception cref="InvalidOperationException">Thrown if the key is not an RSA key</exception>
67102
public RSA AsRSA()
68103
{
69104
if (!_curve.StartsWith("rsa", StringComparison.OrdinalIgnoreCase))
@@ -77,6 +112,11 @@ public RSA AsRSA()
77112
return rsa;
78113
}
79114

115+
/// <summary>
116+
/// Converts the key to an ECDsa instance if it represents an EC key.
117+
/// </summary>
118+
/// <returns>ECDsa instance initialized with the key data</returns>
119+
/// <exception cref="InvalidOperationException">Thrown if the key is not an EC key</exception>
80120
public ECDsa AsECDsa()
81121
{
82122
if (_curve.StartsWith("rsa", StringComparison.OrdinalIgnoreCase))
@@ -90,49 +130,83 @@ public ECDsa AsECDsa()
90130
return ecdsa;
91131
}
92132

133+
/// <summary>
134+
/// Converts the key to a PIV private key format.
135+
/// </summary>
136+
/// <returns>PivPrivateKey instance</returns>
93137
public static TestKey Load(string curve, bool isPrivate)
94138
{
95139
var fileName = $"{curve}_{(isPrivate ? "private" : "public")}.pem";
96140
var filePath = Path.Combine("TestData", fileName);
97141
return new TestKey(filePath, curve, isPrivate);
98142
}
99-
100-
internal PivPrivateKey AsPrivateKey()
101-
{
102-
return new KeyConverter(_pemStringFull).GetPivPrivateKey();
103-
}
104-
105-
internal PivPublicKey AsPublicKey()
106-
{
107-
return new KeyConverter(_pemStringFull).GetPivPublicKey();
108-
}
109143
}
110144

145+
/// <summary>
146+
/// Represents an X.509 certificate for testing purposes.
147+
/// Supports both regular and attestation certificates.
148+
/// </summary>
111149
public class TestCertificate : TestCrypto
112150
{
151+
/// <summary>
152+
/// Indicates whether this certificate is an attestation certificate.
153+
/// </summary>
113154
public readonly bool IsAttestation;
114155

115156
private TestCertificate(string filePath, bool isAttestation) : base(filePath)
116157
{
117158
IsAttestation = isAttestation;
118159
}
119160

161+
/// <summary>
162+
/// Converts the certificate to an X509Certificate2 instance.
163+
/// </summary>
164+
/// <returns>X509Certificate2 instance initialized with the certificate data</returns>
120165
public X509Certificate2 AsX509Certificate2()
121166
{
122167
return new X509Certificate2(_bytes);
123168
}
124169

170+
/// <summary>
171+
/// Loads a certificate from the TestData directory.
172+
/// </summary>
173+
/// <param name="curve">The curve or key type associated with the certificate</param>
174+
/// <param name="isAttestation">True if loading an attestation certificate</param>
175+
/// <returns>A TestCertificate instance</returns>
125176
public static TestCertificate Load(string curve, bool isAttestation = false)
126177
{
127-
string fileName = $"{curve}_cert{(isAttestation ? "_attest" : "")}.pem";
128-
string filePath = Path.Combine("TestData", fileName);
178+
var fileName = $"{curve}_cert{(isAttestation ? "_attest" : "")}.pem";
179+
var filePath = Path.Combine("TestData", fileName);
129180
return new TestCertificate(filePath, isAttestation);
130181
}
131182
}
132183

184+
/// <summary>
185+
/// Provides convenient static methods to access test keys and certificates.
186+
/// </summary>
133187
public static class TestKeys
134188
{
135-
public static TestKey GetKey(string curve, bool isPrivate) => TestKey.Load(curve, isPrivate);
189+
190+
/// <summary>
191+
/// Gets a private key for the specified curve.
192+
/// </summary>
193+
/// <param name="curve">The curve or key type</param>
194+
/// <returns>TestKey instance representing the private key</returns>
195+
public static TestKey GetPrivateKey(string curve) => TestKey.Load(curve, true);
196+
197+
/// <summary>
198+
/// Gets a public key for the specified curve.
199+
/// </summary>
200+
/// <param name="curve">The curve or key type</param>
201+
/// <returns>TestKey instance representing the public key</returns>
202+
public static TestKey GetPublicKey(string curve) => TestKey.Load(curve, false);
203+
204+
/// <summary>
205+
/// Gets a certificate for the specified curve.
206+
/// </summary>
207+
/// <param name="curve">The curve or key type</param>
208+
/// <param name="isAttestation">True to get an attestation certificate</param>
209+
/// <returns>TestCertificate instance</returns>s
136210
public static TestCertificate GetCertificate(string curve, bool isAttestation = false) =>
137211
TestCertificate.Load(curve, isAttestation);
138212
}

0 commit comments

Comments
 (0)