Skip to content

Commit 3edf0a0

Browse files
authored
Merge pull request #243 from Yubico/dennisdyallo/keys-docs
refactor: Add consistent docs and proper naming for certain methods for creating keys
2 parents 91e88bc + 87642e9 commit 3edf0a0

File tree

24 files changed

+255
-92
lines changed

24 files changed

+255
-92
lines changed

Yubico.YubiKey/src/Yubico/YubiKey/Cryptography/AsnPrivateKeyDecoder.cs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,27 @@
1515
using System;
1616
using System.Formats.Asn1;
1717
using System.Globalization;
18-
using System.Linq;
1918
using System.Security.Cryptography;
2019

2120
namespace Yubico.YubiKey.Cryptography;
2221

22+
/// <summary>
23+
/// A class that converts ASN.1 DER encoded private keys to parameters and values.
24+
/// </summary>
2325
internal class AsnPrivateKeyDecoder
2426
{
27+
/// <summary>
28+
/// Creates an instance of <see cref="IPrivateKey"/> from a PKCS#8
29+
/// ASN.1 DER-encoded private key.
30+
/// </summary>
31+
/// <param name="pkcs8EncodedKey">
32+
/// The ASN.1 DER-encoded private key.
33+
/// </param>
34+
/// <returns>
35+
/// A new instance of <see cref="IPrivateKey"/>.
36+
/// </returns>
37+
/// <exception cref="CryptographicException">Thrown if privateKey does not match expected format.</exception>
38+
/// <exception cref="InvalidOperationException">Thrown if the algorithm is not supported</exception>
2539
public static IPrivateKey CreatePrivateKey(ReadOnlyMemory<byte> pkcs8EncodedKey)
2640
{
2741
var reader = new AsnReader(pkcs8EncodedKey, AsnEncodingRules.DER);
@@ -67,8 +81,25 @@ public static IPrivateKey CreatePrivateKey(ReadOnlyMemory<byte> pkcs8EncodedKey)
6781
ExceptionMessages.UnsupportedAlgorithm));
6882
}
6983

70-
public static Curve25519PrivateKey CreateCurve25519Key(ReadOnlyMemory<byte> pkcs8EncodedKey) =>
71-
Curve25519PrivateKey.CreateFromPkcs8(pkcs8EncodedKey);
84+
/// <summary>
85+
/// Creates an instance of <see cref="Curve25519PrivateKey"/> from a PKCS#8
86+
/// ASN.1 DER-encoded private key.
87+
/// </summary>
88+
/// <param name="pkcs8EncodedKey">
89+
/// The ASN.1 DER-encoded private key.
90+
/// </param>
91+
/// <returns>
92+
/// A new instance of <see cref="Curve25519PrivateKey"/>.
93+
/// </returns>
94+
/// <exception cref="CryptographicException">Thrown if privateKey does not match expected format.</exception>
95+
/// <exception cref="ArgumentException">Thrown if the algorithm is not <see cref="Oids.X25519"/> or
96+
/// <see cref="Oids.Ed25519"/></exception>
97+
public static Curve25519PrivateKey CreateCurve25519Key(ReadOnlyMemory<byte> pkcs8EncodedKey)
98+
{
99+
(byte[] privateKey, var keyType) = GetCurve25519PrivateKeyData(pkcs8EncodedKey);
100+
using var privateKeyHandle = new ZeroingMemoryHandle(privateKey);
101+
return Curve25519PrivateKey.CreateFromValue(privateKeyHandle.Data, keyType);
102+
}
72103

73104
public static (byte[] privateKey, KeyType keyType) GetCurve25519PrivateKeyData(ReadOnlyMemory<byte> pkcs8EncodedKey)
74105
{

Yubico.YubiKey/src/Yubico/YubiKey/Cryptography/AsnPublicKeyDecoder.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,26 @@
1919

2020
namespace Yubico.YubiKey.Cryptography;
2121

22+
/// <summary>
23+
/// A class that converts ASN.1 DER encoded X.509 SubjectPublicKeyInfo to instances of IPublicKey.
24+
/// </summary>
2225
internal class AsnPublicKeyDecoder
2326
{
24-
public static IPublicKey CreatePublicKey(ReadOnlyMemory<byte> pkcs8EncodedKey)
27+
/// <summary>
28+
/// Creates an instance of <see cref="IPublicKey"/> from a
29+
/// ASN.1 DER-encoded SubjectPublicKeyInfo.
30+
/// </summary>
31+
/// <param name="subjectPublicKeyInfo">
32+
/// The ASN.1 DER-encoded SubjectPublicKeyInfo.
33+
/// </param>
34+
/// <returns>
35+
/// A new instance of <see cref="IPublicKey"/>.
36+
/// </returns>
37+
/// <exception cref="CryptographicException">Thrown if public key does not match expected format.</exception>
38+
/// <exception cref="InvalidOperationException">Thrown if the algorithm is not supported</exception>
39+
public static IPublicKey CreatePublicKey(ReadOnlyMemory<byte> subjectPublicKeyInfo)
2540
{
26-
var reader = new AsnReader(pkcs8EncodedKey, AsnEncodingRules.DER);
41+
var reader = new AsnReader(subjectPublicKeyInfo, AsnEncodingRules.DER);
2742
var seqSubjectPublicKeyInfo = reader.ReadSequence();
2843
var seqAlgorithmIdentifier = seqSubjectPublicKeyInfo.ReadSequence();
2944

Yubico.YubiKey/src/Yubico/YubiKey/Cryptography/Curve25519PrivateKey.cs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,21 @@
1717

1818
namespace Yubico.YubiKey.Cryptography;
1919

20+
/// <summary>
21+
/// Represents a Curve25519 private key.
22+
/// </summary>
23+
/// <remarks>
24+
/// This sealed class encapsulates Curve25519 private key data and supports
25+
/// both Ed25519 and X25519 cryptographic operations.
26+
/// It also provides factory methods for creating instances from private key values or DER-encoded data.
27+
/// </remarks>
2028
public sealed class Curve25519PrivateKey : PrivateKey
2129
{
2230
private readonly Memory<byte> _privateKey;
2331

2432
/// <inheritdoc />
2533
public override KeyType KeyType => KeyDefinition.KeyType;
26-
34+
2735
/// <summary>
2836
/// Gets the key definition associated with this RSA private key.
2937
/// </summary>
@@ -37,7 +45,7 @@ public sealed class Curve25519PrivateKey : PrivateKey
3745
/// </summary>
3846
/// <returns>A <see cref="ReadOnlyMemory{T}"/> containing the private scalar value.</returns>
3947
public ReadOnlyMemory<byte> PrivateKey => _privateKey;
40-
48+
4149
private Curve25519PrivateKey(
4250
ReadOnlyMemory<byte> privateKey,
4351
KeyType keyType)
@@ -53,9 +61,9 @@ private Curve25519PrivateKey(
5361

5462
privateKey.CopyTo(_privateKey);
5563
}
56-
64+
5765
/// <inheritdoc />
58-
public override byte[] ExportPkcs8PrivateKey()
66+
public override byte[] ExportPkcs8PrivateKey()
5967
{
6068
ThrowIfDisposed();
6169
return AsnPrivateKeyEncoder.EncodeToPkcs8(_privateKey, KeyType);
@@ -70,10 +78,10 @@ public override byte[] ExportPkcs8PrivateKey()
7078

7179
/// <summary>
7280
/// Creates an instance of <see cref="Curve25519PrivateKey"/> from a PKCS#8
73-
/// DER-encoded private key.
81+
/// ASN.1 DER-encoded private key.
7482
/// </summary>
7583
/// <param name="pkcs8EncodedKey">
76-
/// The DER-encoded private key.
84+
/// The ASN.1 DER-encoded private key.
7785
/// </param>
7886
/// <returns>
7987
/// A new instance of <see cref="Curve25519PrivateKey"/>.
@@ -88,7 +96,7 @@ public static Curve25519PrivateKey CreateFromPkcs8(ReadOnlyMemory<byte> pkcs8Enc
8896
using var privateKeyHandle = new ZeroingMemoryHandle(privateKey);
8997
return new Curve25519PrivateKey(privateKeyHandle.Data, keyType);
9098
}
91-
99+
92100
/// <summary>
93101
/// Creates an instance of <see cref="Curve25519PrivateKey"/> from the given
94102
/// <paramref name="privateKey"/> and <paramref name="keyType"/>.

Yubico.YubiKey/src/Yubico/YubiKey/Cryptography/Curve25519PublicKey.cs

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@
1313
// limitations under the License.
1414

1515
using System;
16-
using System.Formats.Asn1;
1716
using System.Globalization;
1817
using System.Security.Cryptography;
1918

2019
namespace Yubico.YubiKey.Cryptography;
2120

21+
/// <summary>
22+
/// Represents a Curve25519 public key.
23+
/// </summary>
24+
/// <remarks>
25+
/// This sealed class encapsulates Curve25519 public key data as a compressed point
26+
/// and supports both Ed25519 and X25519 key types.
27+
/// It also provides factory methods for creating instances from public point values or DER-encoded data.
28+
/// </remarks>
2229
public sealed class Curve25519PublicKey : PublicKey
2330
{
2431
private readonly Memory<byte> _publicPoint;
@@ -52,7 +59,7 @@ private Curve25519PublicKey(
5259

5360
publicPoint.CopyTo(_publicPoint);
5461
}
55-
62+
5663
/// <summary>
5764
/// Converts this public key to an ASN.1 DER encoded format (X.509 SubjectPublicKeyInfo).
5865
/// </summary>
@@ -63,33 +70,21 @@ public override byte[] ExportSubjectPublicKeyInfo() =>
6370
AsnPublicKeyEncoder.EncodeToSubjectPublicKeyInfo(_publicPoint, KeyDefinition.KeyType);
6471

6572
/// <summary>
66-
/// Creates a new instance of <see cref="Curve25519PublicKey"/> from a DER-encoded public key.
73+
/// Creates a new instance of <see cref="Curve25519PublicKey"/> from a DER-encoded SubjectPublicKeyInfo.
6774
/// </summary>
68-
/// <param name="encodedKey">
69-
/// The DER-encoded public key.
75+
/// <param name="subjectPublicKeyInfo">
76+
/// The DER-encoded SubjectPublicKeyInfo.
7077
/// </param>
7178
/// <returns>
7279
/// A new instance of <see cref="Curve25519PublicKey"/>.
7380
/// </returns>
7481
/// <exception cref="CryptographicException">
75-
/// Thrown if the public key is invalid.
82+
/// Thrown if the subjectPublicKeyInfo is invalid.
7683
/// </exception>
77-
public static Curve25519PublicKey CreateFromPkcs8(ReadOnlyMemory<byte> encodedKey)
78-
{
79-
var reader = new AsnReader(encodedKey, AsnEncodingRules.DER);
80-
var seqSubjectPublicKeyInfo = reader.ReadSequence();
81-
var seqAlgorithmIdentifier = seqSubjectPublicKeyInfo.ReadSequence();
82-
83-
string oidAlgorithm = seqAlgorithmIdentifier.ReadObjectIdentifier();
84-
byte[] subjectPublicKey = seqSubjectPublicKeyInfo.ReadBitString(out int unusedBitCount);
85-
if (unusedBitCount != 0)
86-
{
87-
throw new CryptographicException("Invalid public key encoding");
88-
}
89-
90-
var keyType = KeyDefinitions.GetKeyTypeByOid(oidAlgorithm);
91-
return CreateFromValue(subjectPublicKey, keyType);
92-
}
84+
public static Curve25519PublicKey CreateFromSubjectPublicKeyInfo(ReadOnlyMemory<byte> subjectPublicKeyInfo) =>
85+
AsnPublicKeyDecoder
86+
.CreatePublicKey(subjectPublicKeyInfo)
87+
.Cast<Curve25519PublicKey>();
9388

9489
/// <summary>
9590
/// Creates an instance of <see cref="Curve25519PublicKey"/> from the given
@@ -114,4 +109,8 @@ public static Curve25519PublicKey CreateFromValue(ReadOnlyMemory<byte> publicPoi
114109

115110
return new Curve25519PublicKey(publicPoint, keyDefinition);
116111
}
112+
113+
[Obsolete("Use CreateFromSubjectPublicKeyInfo instead", false)]
114+
public static Curve25519PublicKey CreateFromPkcs8(ReadOnlyMemory<byte> subjectPublicKeyInfo) =>
115+
CreateFromSubjectPublicKeyInfo(subjectPublicKeyInfo);
117116
}

Yubico.YubiKey/src/Yubico/YubiKey/Cryptography/ECPrivateKey.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ namespace Yubico.YubiKey.Cryptography
2121
/// Represents the parameters for an Elliptic Curve (EC) private key.
2222
/// </summary>
2323
/// <remarks>
24-
/// This class encapsulates the parameters specific to EC private keys and
25-
/// contains the necessary private key data.
24+
/// This class encapsulates the parameters specific to EC private keys
25+
/// and provides factory methods for creating instances from EC parameters
26+
/// or DER-encoded data.
2627
/// </remarks>
2728
public class ECPrivateKey : PrivateKey
2829
{
@@ -33,7 +34,7 @@ public class ECPrivateKey : PrivateKey
3334
/// An <see cref="ECParameters"/> structure containing the curve parameters, key, and other
3435
/// cryptographic elements needed for EC operations.
3536
/// </value>
36-
public ECParameters Parameters { get;}
37+
public ECParameters Parameters { get; }
3738

3839
/// <summary>
3940
/// Gets the key definition associated with this RSA private key.
@@ -84,7 +85,7 @@ protected ECPrivateKey(ECDsa ecdsaObject)
8485
Parameters = ecdsaObject.ExportParameters(true);
8586
KeyDefinition = KeyDefinitions.GetByOid(Parameters.Curve.Oid);
8687
}
87-
88+
8889
/// <inheritdoc/>
8990
public override byte[] ExportPkcs8PrivateKey()
9091
{
@@ -115,12 +116,16 @@ public override void Clear()
115116
public static ECPrivateKey CreateFromPkcs8(ReadOnlyMemory<byte> encodedKey)
116117
{
117118
var parameters = AsnPrivateKeyDecoder.CreateECParameters(encodedKey);
119+
118120
return CreateFromParameters(parameters);
119121
}
120-
121-
#pragma warning disable CS0618 // Type or member is obsolete.
122+
123+
/// <summary>
124+
/// Creates an instance of <see cref="ECPrivateKey"/> from the given <paramref name="parameters"/>.
125+
/// </summary>
126+
/// <param name="parameters">The parameters to create the key from.</param>
127+
/// <returns>An instance of <see cref="ECPrivateKey"/>.</returns>
122128
public static ECPrivateKey CreateFromParameters(ECParameters parameters) => new(parameters);
123-
#pragma warning restore CS0618 // Type or member is obsolete
124129

125130
/// <summary>
126131
/// Creates a new instance of <see cref="ECPrivateKey"/> from the given

Yubico.YubiKey/src/Yubico/YubiKey/Cryptography/ECPublicKey.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
namespace Yubico.YubiKey.Cryptography;
1919

2020
/// <summary>
21-
/// Represents the parameters for an Elliptic Curve (EC) public key.
21+
/// Represents an Elliptic Curve (EC) public key.
2222
/// </summary>
2323
/// <remarks>
24-
/// This class encapsulates the parameters specific to EC public keys,
25-
/// ensuring that the key only contains necessary public key components.
24+
/// This class encapsulates EC public key parameters and provides cryptographic operations
25+
/// for NIST elliptic curves and provides factory methods for creating instances from EC parameters or DER-encoded data.
2626
/// </remarks>
2727
public class ECPublicKey : PublicKey
2828
{
@@ -99,7 +99,7 @@ protected ECPublicKey(ECDsa ecdsa)
9999

100100
/// <inheritdoc />
101101
public override byte[] ExportSubjectPublicKeyInfo() => AsnPublicKeyEncoder.EncodeToSubjectPublicKeyInfo(Parameters);
102-
102+
103103
/// <summary>
104104
/// Creates an instance of <see cref="ECPublicKey"/> from the given <paramref name="parameters"/>.
105105
/// </summary>
@@ -141,13 +141,19 @@ public static ECPublicKey CreateFromValue(ReadOnlyMemory<byte> publicPoint, KeyT
141141
}
142142

143143
/// <summary>
144-
/// Creates an instance of <see cref="ECPublicKey"/> from a DER-encoded public key.
144+
/// Creates an instance of <see cref="ECPublicKey"/> from a DER-encoded SubjectPublicKeyInfo.
145145
/// </summary>
146-
/// <param name="encodedKey">The DER-encoded public key.</param>
146+
/// <param name="subjectPublicKeyInfo">The DER-encoded SubjectPublicKeyInfo.</param>
147147
/// <returns>An instance of <see cref="IPublicKey"/>.</returns>
148148
/// <exception cref="CryptographicException">
149-
/// Thrown if the public key is invalid.
149+
/// Thrown if the subjectPublicKeyInfo is invalid.
150150
/// </exception>
151-
public static ECPublicKey CreateFromPkcs8(ReadOnlyMemory<byte> encodedKey) =>
152-
(ECPublicKey)AsnPublicKeyDecoder.CreatePublicKey(encodedKey);
151+
public static ECPublicKey CreateFromSubjectPublicKeyInfo(ReadOnlyMemory<byte> subjectPublicKeyInfo) =>
152+
AsnPublicKeyDecoder
153+
.CreatePublicKey(subjectPublicKeyInfo)
154+
.Cast<ECPublicKey>();
155+
156+
[Obsolete("Use CreateFromSubjectPublicKeyInfo instead", false)]
157+
public static ECPublicKey CreateFromPkcs8(ReadOnlyMemory<byte> subjectPublicKeyInfo) =>
158+
CreateFromSubjectPublicKeyInfo(subjectPublicKeyInfo);
153159
}

Yubico.YubiKey/src/Yubico/YubiKey/Cryptography/IKeyBase.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414

1515
namespace Yubico.YubiKey.Cryptography;
1616

17+
/// <summary>
18+
/// Defines the base contract for all cryptographic keys, providing key type identification.
19+
/// </summary>
20+
/// <remarks>
21+
/// This interface serves as the foundation for both public and private key abstractions,
22+
/// enabling polymorphic key type handling across different cryptographic algorithms.
23+
/// </remarks>
1724
public interface IKeyBase
1825
{
1926
/// <summary>

Yubico.YubiKey/src/Yubico/YubiKey/Cryptography/IPrivateKey.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@
1414

1515
namespace Yubico.YubiKey.Cryptography;
1616

17+
/// <summary>
18+
/// Defines the contract for cryptographic private keys.
19+
/// </summary>
20+
/// <remarks>
21+
/// This interface extends <see cref="IKeyBase"/> to include private key-specific operations
22+
/// for PKCS#8 export and secure memory cleanup.
23+
/// Known implementations include <see cref="ECPrivateKey"/>, <see cref="RSAPrivateKey"/> and <see cref="Curve25519PrivateKey"/>,.
24+
/// </remarks>
1725
public interface IPrivateKey : IKeyBase
1826
{
1927
/// <summary>

Yubico.YubiKey/src/Yubico/YubiKey/Cryptography/IPrivateKeyExtensions.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,33 @@
1616

1717
namespace Yubico.YubiKey.Cryptography;
1818

19+
20+
/// <summary>
21+
/// Extension methods for <see cref="IPrivateKey"/> to provide type-safe casting operations.
22+
/// </summary>
1923
public static class IPrivateKeyExtensions
2024
{
2125
/// <summary>
22-
/// Casts the key to the specified type.
26+
/// Safely casts an <see cref="IPrivateKey"/> instance to a specific derived type.
2327
/// </summary>
24-
/// <param name="key"></param>
25-
/// <typeparam name="T"></typeparam>
26-
/// <returns></returns>
27-
/// <exception cref="InvalidCastException"></exception>
28+
/// <typeparam name="T">The target type that implements <see cref="IPrivateKey"/>.</typeparam>
29+
/// <param name="key">The private key instance to cast.</param>
30+
/// <returns>The private key cast to the specified type <typeparamref name="T"/>.</returns>
31+
/// <exception cref="InvalidCastException">
32+
/// Thrown when the <paramref name="key"/> cannot be cast to type <typeparamref name="T"/>.
33+
/// The exception message includes both the source and target type names for debugging.
34+
/// </exception>
35+
/// <example>
36+
/// <code>
37+
/// IPrivateKey genericKey = GetPrivateKey();
38+
/// RsaPrivateKey rsaKey = genericKey.Cast&lt;RsaPrivateKey&gt;();
39+
/// // throws InvalidCastException if genericKey is not an RsaPrivateKey
40+
/// </code>
41+
/// </example>
42+
/// <remarks>
43+
/// This method provides a more explicit alternative to direct casting with clearer error messages.
44+
/// Prefer this over unsafe casting operations when type safety is critical for cryptographic operations.
45+
/// </remarks>
2846
public static T Cast<T>(this IPrivateKey key) where T : class, IPrivateKey =>
2947
key as T ?? throw new InvalidCastException($"Cannot cast {key.GetType()} to {typeof(T)}");
3048
}

0 commit comments

Comments
 (0)