Как извлечь доменное имя из объекта X509Certificate во время SslStream.AuthenticateAsClient? (.NET4)

У меня есть функция RemoteCertificateValidationCallback, вызываемая SslStream.AuthenticateAsClient, которому передается объект X509Certificate.

Я хотел бы извлечь имя из этого сертификата, так что если бы я передал эту строку в AuthenticateAsClient, она бы прошла. (Предполагая, что нет других проблем.)

(Примечание. Свойство Subject содержит имя домена, но внутри строки "CN =..., S =..." и т.д.)

См. также: Как извлечь CN из X509Certificate в Java? (задает аналогичный вопрос для Java, но я не могу найти похожие классы для .NET, упомянутые в эти ответы.)

(Последующая реакция на ответ Евгения.)
Я пробовал это...

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

... но cert2.SubjectName.Name по-прежнему имеет CN = etc форматирование. Я делаю это неправильно?

Ответ 1

Я сделал это следующим образом:

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

Вы также можете проверить, действительно ли сертификат:

bool valid = cert2.Verify();

(см. этот вопрос для описания класса X509Certificate2)

Ответ 2

Для моих строк сертификатов он работал лучше с небольшой настройкой, подобной этой

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();

Ответ 3

Используйте метод GetRawCertData для получения данных сертификата DER. Затем создайте экземпляр объекта X509Certificate2 и загрузите необработанные данные сертификата с помощью метода Import(). Затем используйте свойство SubjectName для доступа к отдельным темам. Примечание. Вам также необходимо проверить расширение альтернативного имени объекта, но, к сожалению, в классах .NET Framework нет простого способа (вам может понадобиться использовать стороннюю PKI-библиотеку для правильной проверки и управления сертификатами).

Ответ 4

Я использовал следующий метод для анализа строк, возвращаемых из AD, это может помочь разобрать полученные данные:

    /// <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;
    }

Итак, дайте ему строку типа "CN = мое общее имя, CN = другое общее имя, O = моя orginization", и он вернет список, содержащий оба общих имени.