Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extract the domain name out of an X509Certificate object during SslStream.AuthenticateAsClient? (.NET4)

I have a RemoteCertificateValidationCallback function called by SslStream.AuthenticateAsClient, which is passed an X509Certificate object.

I'd like to extract the name from that certificate, such that had I passed that string into AuthenticateAsClient, it would have passed. (Assuming no other issues.)

(Note: The Subject property contains the domain name, but it's inside a "CN=..., S=..." etc formatted string.)

See also: How to extract CN from X509Certificate in Java? (Asks a similar question for Java, but I can't find similar classes for .NET mentioned in those answers.)

(Followup to Eugene's answer.)
I've tried this...

var cert2 = new System.Security.Cryptography.X509Certificates.X509Certificate2();
cert2.Import(certificate.GetRawCertData());

... but cert2.SubjectName.Name still has the CN= etc formatting. Am I doing it wrong?

like image 698
billpg Avatar asked Dec 07 '11 12:12

billpg


4 Answers

I have done it the following way:

var cert2 = new X509Certificate2(cert);
string hostName = cert2.GetNameInfo(X509NameType.DnsName, false);

You may also check whether the certificate is valid:

bool valid = cert2.Verify();

(See this question for description of X509Certificate2 class)

like image 115
IMil Avatar answered Nov 04 '22 19:11

IMil


For my certificate strings it worked better with a small adjustment like this

public static List<string> Parse(string data, string delimiter)
        {
            if (data == null) return null;
            if (!delimiter.EndsWith("=")) delimiter = delimiter + "=";
            if (!data.Contains(delimiter)) return null;
            //base case
            var result = new List<string>();
            int start = data.IndexOf(delimiter) + delimiter.Length;
            int length = data.IndexOf(',', start) - start;
            if (length == 0) return null; //the group is empty
            if (length > 0)
            {
                result.Add(data.Substring(start, length));
                //only need to recurse when the comma was found, because there could be more groups
                var rec = Parse(data.Substring(start + length), delimiter);
                if (rec != null) result.AddRange(rec); //can't pass null into AddRange() :(
            }
            else //no comma found after current group so just use the whole remaining string
            {
                result.Add(data.Substring(start));
            }
            return result;
        } 

...

var name = Parse(_cert.Subject, "CN").FirstOrDefault();
var email = Parse(_cert.Subject, "E").FirstOrDefault();
like image 45
Soren_Skov Avatar answered Nov 04 '22 19:11

Soren_Skov


Use GetRawCertData method to get Certificate's DER data. Then create an instance of X509Certificate2 object and load the raw cert data using Import() method. Then use SubjectName property to access individual subject fields. Note - you also need to inspect Subject Alternative Name extension, but unfortunately there's no easy way to do this in .NET Framework classes (you might find it necessary to use third-party PKI library for proper certificate validation and management).

like image 1
Eugene Mayevski 'Callback Avatar answered Nov 04 '22 18:11

Eugene Mayevski 'Callback


I used the following method to parse strings returned from AD, it may help parse the data your recieving:

    /// <summary>
    /// Recursively searches the supplied AD string for all groups.
    /// </summary>
    /// <param name="data">The string returned from AD to parse for a group.</param>
    /// <param name="delimiter">The string to use as the seperator for the data. ex. ","</param>
    /// <returns>null if no groups were found -OR- data is null or empty.</returns>
    public static List<string> Parse(string data, string delimiter)
    {
        if (data == null) return null;

        if (!delimiter.EndsWith("=")) delimiter = delimiter + "=";

        //data = data.ToUpper(); // why did i add this?
        if (!data.Contains(delimiter)) return null;

        //base case
        var result = new List<string>();
        int start = data.IndexOf(delimiter) + 3;
        int length = data.IndexOf(',', start) - start;
        if (length == 0) return null; //the group is empty
        if (length > 0)
        {
            result.Add(data.Substring(start, length));

            //only need to recurse when the comma was found, because there could be more groups
            var rec = Parse(data.Substring(start + length), delimiter);
            if (rec != null) result.AddRange(rec); //can't pass null into AddRange() :(
        }
        else //no comma found after current group so just use the whole remaining string
        {
            result.Add(data.Substring(start));            
        }

        return result;
    }

So give it a string like "CN=my common name,CN=another common name,O=my orginization" and it will return a list containing both common names.

like image 1
Adam Naylor Avatar answered Nov 04 '22 18:11

Adam Naylor