Извлечь общее имя из отличительного имени
Есть ли вызов в .NET, который анализирует CN из кодированного rfc-2253 различного имени? Я знаю, что есть некоторые сторонние библиотеки, которые это делают, но я предпочел бы использовать собственные библиотеки .NET, если это возможно.
Примеры строкового кодирования DN
CN = L. Eagle, O = Sue \, Grabbit и Runn, C = GB
CN = Jeff Smith, OU = Sales, DC = Fabrikam, DC = COM
Ответы
Ответ 1
После изучения исходного кода .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
Пожалуйста, не считайте, что это внутренний служебный класс, который может измениться в будущем выпуске.
Ответ 2
Если вы работаете с X509Certificate2
, есть собственный метод, который вы можете использовать для извлечения простого имени. Простое имя эквивалентно RDN Common Name в поле Subject основного сертификата:
x5092Cert.GetNameInfo(X509NameType.SimpleName, false);
В качестве альтернативы, X509NameType.DnsName
можно использовать для получения альтернативного имени субъекта, если оно имеется; в противном случае по умолчанию будет использоваться общее имя:
x5092Cert.GetNameInfo(X509NameType.DnsName, false);
Ответ 3
У меня был тот же самый вопрос, когда я нашел твое. Не нашел ничего в BCL; однако я наткнулся на эту статью CodeProject, которая сразу же ударила ноготь по голове.
Я надеюсь, что это тоже поможет вам.
http://www.codeproject.com/Articles/9788/An-RFC-2253-Compliant-Distinguished-Name-Parser
Ответ 4
Учитываются ли функции Win32? Вы можете использовать PInvoke с DsGetRdnW
. Для кода см. Мой ответ на другой вопрос: fooobar.com/questions/282894/....
Ответ 5
Просто добавьте мои два цента здесь. Эта реализация работает "лучше", если вы сначала узнаете, какие бизнес-правила существуют, что в конечном итоге будет определять, какая часть 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);
}
Ответ 6
Если порядок не определен, я делаю это:
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;
}
Ответ 7
Не могли бы вы просто получить значения атрибута CN?
Как вы правильно заметили, используйте чужой класс, так как есть много забавных фреймов (экранированные запятые, экранированные другие символы), которые делают разбор DN красивым, но на самом деле достаточно сложным.
Обычно я использую класс Java, который поставляется с Novell (Now NetID) Identity Manager. Так что это не полезно.
Ответ 8
Как насчет этого:
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 Regular Expression для извлечения частей Distiniguished Name из Active Directory.
Ответ 9
Вы можете использовать регулярные выражения для этого. Вот шаблон регулярных выражений, который может проанализировать весь DN, тогда вы можете просто взять интересующие вас части:
(?:^|,\s?)(?:(?<name>[AZ]+)=(?<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
Ответ 10
Это мой почти 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;
}
}
}
Ответ 11
char [] character = {','}; string alteredName = item.Properties ["manager"] [0].ToString(). Replace ("CN =", "").Replace("\", ""); string [] manager = alteredName.Split (символ);
obj.Manager = manager [0] + "," + manager [1];
Ответ 12
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();