我有一个由 SslStream.AuthenticateAsClient 调用的 RemoteCertificateValidationCallback 函数,该函数传递一个 X509Certificate 对象。
我想从该证书中提取名称,这样如果我将该字符串传递给 AuthenticateAsClient,它就会通过。 (假设没有其他问题。)
(注意:Subject 属性包含域名,但它位于“CN=..., S=...”等格式的字符串内。)
另请参阅:如何从 Java 中的 X509Certificate 中提取 CN?(针对 Java 提出类似的问题,但我找不到这些答案中提到的 .NET 的类似类。)
(尤金的回答的后续。)
我试过这个...
var cert2 = new System.Security.Cryptography.X509Certificates.X509Certificate2();
cert2.Import(certificate.GetRawCertData());
...但 cert2.SubjectName.Name 仍然具有 CN= 等格式。我做错了吗?
我是按照以下方式完成的:
var cert2 = new X509Certificate2(cert);
string hostName = cert2.GetNameInfo(X509NameType.DnsName, false);
您还可以检查证书是否有效:
bool valid = cert2.Verify();
(有关 X509Certificate2 类的描述,请参阅这个问题)
对于我的证书字符串,通过这样的小调整效果更好
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();
使用 GetRawCertData 方法获取证书的 DER 数据。然后创建 X509Certificate2 对象的实例并使用 Import() 方法加载原始证书数据。然后使用 SubjectName 属性访问各个主题字段。注意 - 您还需要检查主题备用名称扩展,但不幸的是,在 .NET Framework 类中没有简单的方法来执行此操作(您可能会发现有必要使用第三方 PKI 库来进行正确的证书验证和管理)。
我使用以下方法来解析从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=我的组织”这样的字符串,它将返回一个包含两个常用名的列表。