Извлечь общее имя из отличительного имени - PullRequest
14 голосов
/ 07 октября 2011

Есть ли в .NET вызов, который анализирует CN по уникальному имени в кодировке rfc-2253? Я знаю, что есть некоторые сторонние библиотеки, которые делают это, но я бы предпочел использовать нативные библиотеки .NET, если это возможно.

Примеры строкового кодирования DN

CN = L. Орел, O = Сью \, Граббит и Ранн, C = GB

CN = Джефф Смит, OU = Продажи, DC = Fabrikam, DC = COM

Ответы [ 11 ]

8 голосов
/ 03 ноября 2016

Если вы работаете с X509Certificate2, есть собственный метод, который вы можете использовать для извлечения DNS-имени. DNS-имя обычно эквивалентно RDN Common Name в поле Subject основного сертификата:

x5092Cert.GetNameInfo(X509NameType.DnsName, false);
7 голосов
/ 29 апреля 2014

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

string dn = "CN=TestGroup,OU=Groups,OU=UT-SLC,OU=US,DC=Company,DC=com";
Assembly dirsvc = Assembly.Load("System.DirectoryServices");
Type asmType = dirsvc.GetType("System.DirectoryServices.ActiveDirectory.Utils");
MethodInfo mi = asmType.GetMethod("GetDNComponents", BindingFlags.NonPublic | BindingFlags.Static);
string[] parameters = { dn };
var test = mi.Invoke(null, parameters);
//test.Dump("test1");//shows details when using Linqpad 

//Convert Distinguished Name (DN) to Relative Distinguished Names (RDN) 
MethodInfo mi2 = asmType.GetMethod("GetRdnFromDN", BindingFlags.NonPublic | BindingFlags.Static);
var test2 = mi2.Invoke(null, parameters);
//test2.Dump("test2");//shows details when using Linqpad 

Результаты будут выглядеть так:

//test1 is array of internal "Component" struct that has name/values as strings
Name   Value 
CN     TestGroup 
OU     Groups 
OU     UT-SLC
OU     US 
DC     company 
DC     com 


//test2 is a string with CN=RDN 
CN=TestGroup 

Пожалуйста, не считайте, что это внутренний служебный класс, который может измениться в будущем выпуске.

6 голосов
/ 11 апреля 2012

У меня был такой же вопрос, когда я нашел ваш. Не нашел ничего в BCL; тем не менее, я наткнулся на эту статью CodeProject, которая попала прямо в голову.

Надеюсь, вам это тоже поможет.

http://www.codeproject.com/Articles/9788/An-RFC-2253-Compliant-Distinguished-Name-Parser

3 голосов
/ 21 ноября 2012

Подсчитывают ли функции Win32?Вы можете использовать PInvoke с DsGetRdnW.Код см. В моем ответе на другой вопрос: https://stackoverflow.com/a/11091804/628981.

1 голос
/ 15 апреля 2018

Если порядок неопределенный, я делаю это:

private static string ExtractCN(string dn)
{
    string[] parts = dn.Split(new char[] { ',' });

    for (int i = 0; i < parts.Length; i++)
    {
        var p = parts[i];
        var elems = p.Split(new char[] { '=' });
        var t = elems[0].Trim().ToUpper();
        var v = elems[1].Trim();
        if (t == "CN")
        {
            return v;
        }
    }
    return null;
}
1 голос
/ 20 декабря 2016

Просто добавив мои два цента здесь.Эта реализация работает «лучше», если вы сначала узнаете, какие бизнес-правила существуют, что в конечном итоге будет диктовать , сколько RFC когда-либо будет внедрено в вашей компании.

private static string ExtractCN(string distinguishedName)
{
    // CN=...,OU=...,OU=...,DC=...,DC=...
    string[] parts;

    parts = distinguishedName.Split(new[] { ",DC=" }, StringSplitOptions.None);
    var dc = parts.Skip(1);

    parts = parts[0].Split(new[] { ",OU=" }, StringSplitOptions.None);
    var ou = parts.Skip(1);

    parts = parts[0].Split(new[] { ",CN=" }, StringSplitOptions.None);
    var cnMulti = parts.Skip(1);

    var cn = parts[0];

    if (!Regex.IsMatch(cn, "^CN="))
        throw new CustomException(string.Format("Unable to parse distinguishedName for commonName ({0})", distinguishedName));

    return Regex.Replace(cn, "^CN=", string.Empty);
}
0 голосов
/ 16 ноября 2018

Это мой почти RFC-совместимый анализатор отказоустойчивых DN, полученный из https://www.codeproject.com/Articles/9788/An-RFC-2253-Compliant-Distinguished-Name-Parser и пример его использования (извлечение имени субъекта в виде CN и O, оба необязательных, с запятой):

    private static string GetCertificateString(X509Certificate2 certificate)
    {
        var subjectComponents = certificate.Subject.ParseDistinguishedName();
        var subjectName = string.Join(", ", subjectComponents
            .Where(m => (m.Item1 == "CN") || (m.Item1 == "O"))
            .Select(n => n.Item2)
            .Distinct());

        return $"{certificate.SerialNumber} {certificate.NotBefore:yyyy.MM.dd}-{certificate.NotAfter:yyyy.MM.dd} {subjectName}";
    }

    private enum DistinguishedNameParserState
    {
        Component,
        QuotedString,
        EscapedCharacter,
    };

    public static IEnumerable<Tuple<string, string>> ParseDistinguishedName(this string value)
    {
        var previousState = DistinguishedNameParserState.Component;
        var currentState = DistinguishedNameParserState.Component;
        var currentComponent = new StringBuilder();
        var previousChar = char.MinValue;
        var position = 0;

        Func<StringBuilder, Tuple<string, string>> parseComponent = sb =>
        {
            var s = sb.ToString();
            sb.Clear();

            var index = s.IndexOf('=');
            if (index == -1)
            {
                return null;
            }

            var item1 = s.Substring(0, index).Trim().ToUpper();
            var item2 = s.Substring(index + 1).Trim();

            return Tuple.Create(item1, item2);
        };

        while (position < value.Length)
        {
            var currentChar = value[position];

            switch (currentState)
            {
                case DistinguishedNameParserState.Component:
                    switch (currentChar)
                    {
                        case ',':
                        case ';':
                            // Separator found, yield parsed component
                            var component = parseComponent(currentComponent);
                            if (component != null)
                            {
                                yield return component;
                            }
                            break;

                        case '\\':
                            // Escape character found
                            previousState = currentState;
                            currentState = DistinguishedNameParserState.EscapedCharacter;
                            break;

                        case '"':
                            // Quotation mark found
                            if (previousChar == currentChar)
                            {
                                // Double quotes inside quoted string produce single quote
                                currentComponent.Append(currentChar);
                            }
                            currentState = DistinguishedNameParserState.QuotedString;
                            break;

                        default:
                            currentComponent.Append(currentChar);
                            break;
                    }
                    break;

                case DistinguishedNameParserState.QuotedString:
                    switch (currentChar)
                    {
                        case '\\':
                            // Escape character found
                            previousState = currentState;
                            currentState = DistinguishedNameParserState.EscapedCharacter;
                            break;

                        case '"':
                            // Quotation mark found
                            currentState = DistinguishedNameParserState.Component;
                            break;

                        default:
                            currentComponent.Append(currentChar);
                            break;
                    }
                    break;

                case DistinguishedNameParserState.EscapedCharacter:
                    currentComponent.Append(currentChar);
                    currentState = previousState;
                    currentChar = char.MinValue;
                    break;
            }

            previousChar = currentChar;
            position++;
        }

        // Yield last parsed component, if any
        if (currentComponent.Length > 0)
        {
            var component = parseComponent(currentComponent);
            if (component != null)
            {
                yield return component;
            }
        }
    }
0 голосов
/ 03 апреля 2018

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

(?:^|,\s?)(?:(?<name>[A-Z]+)=(?<val>"(?:[^"]|"")+"|(?:\\,|[^,])+))+

Здесь он отформатирован немного лучше, и с некоторыми комментариями:

(?:^|,\s?)               <-- Start or a comma
(?:
    (?<name>[A-Z]+)
    =
    (?<val>
        "(?:[^"]|"")+"   <-- Quoted strings
        |
        (?:\\,|[^,])+    <-- Unquoted strings
    )
)+

Это регулярное выражение даст вам name и val группы захвата для каждого совпадения.

Строки DN могут быть заключены в кавычки (например, "Hello", что позволяет имсодержат неэкранированные запятые. В качестве альтернативы, если они не заключены в кавычки, запятые должны быть экранированы обратной косой чертой (например, Hello\, there!). Это регулярное выражение обрабатывает строки как в кавычках, так и без кавычек.

Вот ссылка, так что вы можете увидеть ее в действии:https://regex101.com/r/7vhdDz/1

0 голосов
/ 28 октября 2015

Как насчет этого:

string cnPattern = @"^CN=(?<cn>.+?)(?<!\\),";
string dn        = @"CN=Doe\, John,OU=My OU,DC=domain,DC=com";

Regex re = new Regex(cnPattern);          
Match m  = re.Match(dn);

if (m.Success)
{
  // Item with index 1 returns the first group match.
  string cn = m.Groups[1].Value;
}

Адаптировано из Регулярное выражение Powershell для извлечения частей отличительного имени Active Directory .

0 голосов
/ 19 июня 2015
using System.Linq; 

var dn = "CN=Jeff Smith,OU=Sales,DC=Fabrikam,DC=COM";
var cn = dn.Split(',').Where(i => i.Contains("CN=")).Select(i => i.Replace("CN=", "")).FirstOrDefault();
...