Skip to content

Commit 1d33eb8

Browse files
committed
Implement ParseDistinguishedName
1 parent 7b7bb63 commit 1d33eb8

File tree

3 files changed

+154
-2
lines changed

3 files changed

+154
-2
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System.Security.Cryptography.X509Certificates;
2+
3+
namespace AdvancedSystems.Security.Cryptography;
4+
5+
/// <summary>
6+
/// Defines the Distinguished Name (DN) of an entity that owns or issued the certificate.
7+
/// This entity is typically a Certificate Authority (CA) or an intermediate CA in a chain of trust.
8+
/// </summary>
9+
/// <remarks>
10+
/// DN is a term that describes the identifying information in a certificate and is
11+
/// part of the <seealso cref="X509Certificate2"/> itself. A certificate contains DN
12+
/// information for both the owner or requestor of the certificate (called the Subject DN)
13+
/// and the CA that issues the certificate (called the Issuer DN). Depending on the
14+
/// identification policy of the CA that issues a certificate, the DN can include a variety
15+
/// of information.
16+
/// </remarks>
17+
/// <seealso href="https://datatracker.ietf.org/doc/html/rfc4514"/>
18+
public sealed record DistinguishedName
19+
{
20+
/// <summary>
21+
/// Gets or sets the <inheritdoc cref="RDN.C" path="/summary"/>.
22+
/// </summary>
23+
/// <remarks>
24+
/// <inheritdoc cref="RDN.C" path="/remarks"/>
25+
/// </remarks>
26+
public string? Country { get; init; }
27+
28+
/// <summary>
29+
/// Gets or sets the <inheritdoc cref="RDN.CN" path="/summary"/>.
30+
/// </summary>
31+
/// <remarks>
32+
/// <inheritdoc cref="RDN.CN" path="/remarks"/>
33+
/// </remarks>
34+
public string? CommonName { get; init; }
35+
36+
/// <summary>
37+
/// Gets or sets the <inheritdoc cref="RDN.L" path="/summary"/>.
38+
/// </summary>
39+
/// <remarks>
40+
/// <inheritdoc cref="RDN.L" path="/remarks"/>
41+
/// </remarks>
42+
public string? Locality { get; init; }
43+
44+
/// <summary>
45+
/// Gets or sets the <inheritdoc cref="RDN.O" path="/summary"/>.
46+
/// </summary>
47+
public string? Organization { get; init; }
48+
49+
/// <summary>
50+
/// Gets or sets the <inheritdoc cref="RDN.OU" path="/summary"/>.
51+
/// </summary>
52+
public string? OrganizationalUnit { get; init; }
53+
54+
/// <summary>
55+
/// Gets or sets the <inheritdoc cref="RDN.S" path="/summary"/>..
56+
/// </summary>
57+
/// <remarks>
58+
/// <inheritdoc cref="RDN.S" path="/remarks"/>
59+
/// </remarks>
60+
public string? State { get; init; }
61+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
namespace AdvancedSystems.Security.Cryptography;
2+
3+
/// <summary>
4+
/// The Relative Distinguished Names (RDNs) are attribute-value pairs that
5+
/// uniquely identify an entity.
6+
/// </summary>
7+
/// <remarks>
8+
/// This class defines the attributes that annotate one or more values.
9+
/// </remarks>
10+
/// <seealso cref="DistinguishedName"/>
11+
/// <seealso href="https://datatracker.ietf.org/doc/html/rfc4514"/>
12+
public static class RDN
13+
{
14+
/// <summary>
15+
/// Country (<c>C</c>)
16+
/// </summary>
17+
/// <remarks>
18+
/// This field could also be used to store the region.
19+
/// </remarks>
20+
public const string C = "C";
21+
22+
/// <summary>
23+
/// Common Name (<c>CN</c>)
24+
/// </summary>
25+
/// <remarks>
26+
/// This field denotes the certificate owner's common name.
27+
/// </remarks>
28+
public const string CN = "CN";
29+
30+
/// <summary>
31+
/// Locality (<c>L</c>)
32+
/// </summary>
33+
/// <remarks>
34+
/// This field could also be used to store the city.
35+
/// </remarks>
36+
public const string L = "L";
37+
38+
/// <summary>
39+
/// Organization (<c>O</c>)
40+
/// </summary>
41+
public const string O = "O";
42+
43+
/// <summary>
44+
/// Organizational Unit (<c>OU</c>)
45+
/// </summary>
46+
public const string OU = "OU";
47+
48+
/// <summary>
49+
/// State (<c>S</c>)
50+
/// </summary>
51+
/// <remarks>
52+
/// This field could also be used to store the province.
53+
/// </remarks>
54+
public const string S = "S";
55+
}

AdvancedSystems.Security/Extensions/CertificateStore.cs renamed to AdvancedSystems.Security/Extensions/CertificateExtensions.cs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
using System.Linq;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
24
using System.Security.Cryptography.X509Certificates;
35

46
using AdvancedSystems.Security.Abstractions;
57
using AdvancedSystems.Security.Abstractions.Exceptions;
8+
using AdvancedSystems.Security.Cryptography;
69

710
namespace AdvancedSystems.Security.Extensions;
811

912
/// <summary>
1013
/// Defines functions for interacting with X.509 certificates.
1114
/// </summary>
1215
/// <seealso href="https://datatracker.ietf.org/doc/rfc5280/"/>
13-
public static class CertificateStore
16+
public static partial class CertificateExtensions
1417
{
1518
/// <summary>
1619
/// Retrieves an X.509 certificate from the specified store using the provided thumbprint.
@@ -42,4 +45,37 @@ public static X509Certificate2 GetCertificate<T>(this T store, string thumbprint
4245
return certificate
4346
?? throw new CertificateNotFoundException("No valid certificate matching the search criteria could be found in the store.");
4447
}
48+
49+
public static DistinguishedName ParseDistinguishedName(string distinguishedName)
50+
{
51+
var rdns = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
52+
var dn = new X500DistinguishedName(distinguishedName);
53+
54+
foreach (var rdn in dn.EnumerateRelativeDistinguishedNames())
55+
{
56+
string? attribute = rdn.GetSingleElementType().FriendlyName;
57+
string value = rdn.GetSingleElementValue() ?? string.Empty;
58+
59+
if (string.IsNullOrEmpty(attribute)) continue;
60+
61+
rdns.Add(attribute, value);
62+
}
63+
64+
rdns.TryGetValue(RDN.C, out string? country);
65+
rdns.TryGetValue(RDN.CN, out string? commonName);
66+
rdns.TryGetValue(RDN.L, out string? locality);
67+
rdns.TryGetValue(RDN.O, out string? organization);
68+
rdns.TryGetValue(RDN.OU, out string? organizationalUnit);
69+
rdns.TryGetValue(RDN.S, out string? state);
70+
71+
return new DistinguishedName
72+
{
73+
CommonName = commonName,
74+
OrganizationalUnit = organizationalUnit,
75+
Organization = organization,
76+
Locality = locality,
77+
State = state,
78+
Country = country,
79+
};
80+
}
4581
}

0 commit comments

Comments
 (0)