14

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?

Community
  • 1
  • 1
billpg
  • 3,195
  • 3
  • 30
  • 57
  • sorry for bothering you, but please take a look: you have accepted the answer which has a single upvote, while my answer has +23 and is definitely a lot simpler and safer (though I guess came a bit late...) Would you consider changing the accepted answer? I am asking this not for my own ego, but for the benefit of the other users. – IMil Nov 27 '17 at 16:27
  • 1
    @IMil Oh all right then. Sorry Adam. – billpg Nov 28 '17 at 11:06

4 Answers4

32

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)

Community
  • 1
  • 1
IMil
  • 1,391
  • 13
  • 17
2

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();
Soren_Skov
  • 39
  • 4
  • This way isn't safe to handle a lot of kind certificates. For instance, some has 'OU' as a delimiter and I don't know all delimiters can exists. – Thiago Araújo May 12 '22 at 14:06
1

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.

Adam Naylor
  • 6,172
  • 10
  • 49
  • 69
1

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).

Eugene Mayevski 'Callback
  • 45,135
  • 8
  • 71
  • 121
  • Thanks for that, but I'm still getting the CN= etc stuff back. I've edited the Q with some sample code. – billpg Dec 07 '11 at 14:42
  • @billpg Looks like you need to parse the name :( -- This class is like an extension to the SubjectName or IssuerName property, which is the name of the person or entity that the certificate is being issued to. X.500 is an international standard for distributed directory services. The distinguished name uses the following format: [X500:/C=CountryCode/O=Organization/OU=OrganizationUnit/CN=CommonName] – Eugene Mayevski 'Callback Dec 07 '11 at 15:22
  • @billpg I still suggest you use third-party library for handy management of certificates. – Eugene Mayevski 'Callback Dec 07 '11 at 15:22
  • Thanks for that. I was hoping the code that .NET's SslStream uses internally had a public interface. Its a bit maddening to know the code I want is in there but I can't call it. :) – billpg Dec 07 '11 at 15:30