Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you parse the Subject Alternate Names from an X509Certificate2?

Is there an easy way to get the Subject Alternate Names from an X509Certificate2 object?

        foreach (X509Extension ext in certificate.Extensions)
        {
            if (ext.Oid.Value.Equals(/* SAN OID */"2.5.29.17"))
            {
                byte[] raw = ext.RawData;
                // ?????? parse to get type and name ????????
            }
        }
like image 437
noctonura Avatar asked May 22 '13 17:05

noctonura


4 Answers

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));
}
like image 198
Mark Brackett Avatar answered Jan 01 '23 05:01

Mark Brackett


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);
like image 25
user7254972 Avatar answered Jan 01 '23 07:01

user7254972


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;
}
like image 26
Sebastian Krysmanski Avatar answered Jan 01 '23 07:01

Sebastian Krysmanski


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;
    }
like image 27
Jason Shuler Avatar answered Jan 01 '23 07:01

Jason Shuler