How do you parse the Subject Alternate Names from an X509Certificate2?
To get the "Subject Alternative Name" from a certificate:
X509Certificate2 cert = /* your code here */;
Console.WriteLine("UpnName : {0}{1}", cert.GetNameInfo(X509NameType.UpnName, false), Environment.NewLine);
Use the Format method of the extension for a printable version.
X509Certificate2 cert = /* your code here */;
foreach (X509Extension extension in cert.Extensions)
{
// Create an AsnEncodedData object using the extensions information.
AsnEncodedData asndata = new AsnEncodedData(extension.Oid, extension.RawData);
Console.WriteLine("Extension type: {0}", extension.Oid.FriendlyName);
Console.WriteLine("Oid value: {0}",asndata.Oid.Value);
Console.WriteLine("Raw data length: {0} {1}", asndata.RawData.Length, Environment.NewLine);
Console.WriteLine(asndata.Format(true));
}
Here's a solution that does not require parsing the text returned by AsnEncodedData.Format()
(but requires .NET 5):
using System.Formats.Asn1;
...
public static List<string> GetAlternativeDnsNames(X509Certificate2 cert)
{
const string SAN_OID = "2.5.29.17";
var extension = cert.Extensions[SAN_OID];
if (extension is null)
{
return new List<string>();
}
// Tag value "2" is defined by:
//
// dNSName [2] IA5String,
//
// in: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
var dnsNameTag = new Asn1Tag(TagClass.ContextSpecific, tagValue: 2, isConstructed: false);
var asnReader = new AsnReader(extension.RawData, AsnEncodingRules.BER);
var sequenceReader = asnReader.ReadSequence(Asn1Tag.Sequence);
var resultList = new List<string>();
while (sequenceReader.HasData)
{
var tag = sequenceReader.PeekTag();
if (tag != dnsNameTag)
{
sequenceReader.ReadEncodedValue();
continue;
}
var dnsName = sequenceReader.ReadCharacterString(UniversalTagNumber.IA5String, dnsNameTag);
resultList.Add(dnsName);
}
return resultList;
}
Based on the answer from Minh, here is a self-contained static function that should return them all
public static IEnumerable<string> ParseSujectAlternativeNames(X509Certificate2 cert)
{
Regex sanRex = new Regex(@"^DNS Name=(.*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
var sanList = from X509Extension ext in cert.Extensions
where ext.Oid.FriendlyName.Equals("Subject Alternative Name", StringComparison.Ordinal)
let data = new AsnEncodedData(ext.Oid, ext.RawData)
let text = data.Format(true)
from line in text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
let match = sanRex.Match(line)
where match.Success && match.Groups.Count > 0 && !string.IsNullOrEmpty(match.Groups[1].Value)
select match.Groups[1].Value;
return sanList;
}