是否在.NET中调用从rfc-2253编码的专有名称解析CN?我知道有一些第三方库可以做到这一点,但如果可能的话,我更愿意使用本机.NET库。
字符串编码DN的示例
CN = L。 Eagle,O = Sue \,Grabbit和Runn,C = GB
CN = Jeff Smith,OU =销售额,DC = Fabrikam,DC = COM
答案 0 :(得分:8)
如果您使用的是X509Certificate2,则可以使用一种本机方法来提取DNS名称。 DNS名称通常等同于主证书的“主题”字段中的公共名称RDN:
x5092Cert.GetNameInfo(X509NameType.DnsName, false);
答案 1 :(得分:6)
当我找到你的时,我自己也有同样的问题。在BCL没找到任何东西;然而,我偶然发现了这篇CodeProject的文章,该文章直接敲响了头部。
我希望它也会帮助你。
http://www.codeproject.com/Articles/9788/An-RFC-2253-Compliant-Distinguished-Name-Parser
答案 2 :(得分:6)
在.NET源代码中挖掘后,看起来有一个内部实用程序类可以parse Distinguished Names进入它们的不同组件。遗憾的是,实用程序类未公开,但您可以使用反射访问它:
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
请注意,这不是内部实用程序类,可能会在将来的版本中更改。
答案 3 :(得分:3)
Win32功能有效吗?您可以将PInvoke与DsGetRdnW
一起使用。有关代码,请参阅我对其他问题的回答:https://stackoverflow.com/a/11091804/628981。
答案 4 :(得分:1)
在这里加我的两分钱。如果您首先了解哪些业务规则最终将决定将在贵公司实施多少,那么此实现将“最佳”。
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);
}
答案 5 :(得分:1)
如果订单不确定,我这样做:
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;
}
答案 6 :(得分:1)
很抱歉聚会晚了一点,但是我能够直接从c#调用Name属性
UserPrincipal p
然后我就可以打电话
p.Name
这给了我全名(通用名)
示例代码:
string name;
foreach(UserPrincipal p in PSR)
{
//PSR refers to PrincipalSearchResult
name = p.Name;
Console.WriteLine(name);
}
很显然,您必须填写空白。但这比解析正则表达式要容易。
答案 7 :(得分:1)
您可以使用AsnEncodedData类从ASN.1编码的专有名称中提取通用名称:
var distinguishedName= new X500DistinguishedName("CN=TestGroup,OU=Groups,OU=UT-SLC,OU=US,DC=Company,DC=com");
var commonNameData = new AsnEncodedData("CN", distinguishedName.RawData);
var commonName = commonNameData.Format(false);
此方法的缺点是,如果您指定了无法识别的OID或可分辨名称中缺少用OID标识的字段,则Format
方法将返回具有完整可分辨名称的编码值的十六进制字符串,因此您可能要验证结果。
此外,文档似乎也未指定是否允许AsnEncodedData构造函数的rawData参数包含除指定为第一个参数的OID之外的其他OID,因此它可能会在非Windows OS或.NET的未来版本中中断框架。
答案 8 :(得分:0)
你能不能只检索CN属性值?
正如你正确地注意到的那样,使用别人的课程,因为有许多有趣的边缘案例(转义逗号,转义其他字符),这使得解析DN看起来很简单,但实际上相当棘手。
我通常使用Novell(Now NetID)Identity Manager附带的Java类。所以这没有帮助。
答案 9 :(得分:0)
这个怎么样:
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 for Extracting Parts of an Active Directory Distiniguished Name。
答案 10 :(得分:0)
您可以使用正则表达式来执行此操作。这是一个可以解析整个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
答案 11 :(得分:0)
这是我从https://www.codeproject.com/Articles/9788/An-RFC-2253-Compliant-Distinguished-Name-Parser派生出来的,几乎符合RFC的故障安全DN解析器及其用法示例(提取主题名称为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;
}
}
}
答案 12 :(得分:0)
好吧,我是另一个迟到的人。这是我的解决方案:
var dn = new X500DistinguishedName("CN=TestGroup,OU=Groups,OU=UT-SLC,OU=US,DC=\"Company, inc\",DC=com");
foreach(var part in dn.Format(true).Split("\r\n"))
{
if(part == "") continue;
var parts = part.Split('=', 2);
var key = parts[0];
var value = parts[1];
// use your key and value as you see fit here.
}
基本上它利用 X500DistinguishedName.Format 方法将事情放在线上。然后按行分割,再把每一行分割成key值。
答案 13 :(得分:-3)
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();