如何从客户端证书中读取通用名称?

问题描述 投票:0回答:7

我们的应用程序需要一条数据,该数据包含在客户端证书的通用名称中。目前,我正在尝试从 HttpContext.Current.Request.ClientCertificate 获取它。我该如何读出这个?可悲的是,当我弄清楚为什么 SoapUI 不发送证书时,我正在尝试盲目编码,所以除了阅读 MSDN 上的对象并浏览空属性之外,我没有尝试太多,但我不确定我在寻找什么。回顾一下,我需要做什么才能从该证书中提取通用名? TIA

c# .net client-certificates
7个回答
20
投票

我可能来不及回答你的问题了,但我希望这能帮助其他正在寻找从证书中获取通用名称的方法的人。

如果您使用“主题”,您可能需要删除其他不必要的信息。 例如,CN = 本地主机、OU = 部门名称、O = 公司名称、L = 位置、S = 州、C = 国家/地区

Dim store As New X509Store(StoreName.My, StoreLocation.LocalMachine)
store.Open(OpenFlags.ReadOnly)
store.Certificates(0).Subject

但是如果您使用下面的代码,您将获得“localhost”,它直接为您提供证书的通用名称。

Dim store As New X509Store(StoreName.My, StoreLocation.LocalMachine)
store.Open(OpenFlags.ReadOnly)
store.Certificates(0).GetNameInfo(X509NameType.SimpleName, False)

以下是参考链接:- https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.getnameinfo(v=vs.110).aspx


4
投票

我对证书略知一二。这是我的工作流程:

我开始于:

HttpRequest.ClientCertificate

这让我:

HttpClientCertificate(作为返回类型)。

它似乎有一些属性,但没有一个明确命名为通用名称

Google 搜索:HttpClientCertificate 通用名称:

从 Web 服务端的上下文中提取 X509 证书时出现问题

其中有一些代码:

//extracting Common name from certificate
Subject = cert.Subject.ToString();

然后去了:

HttpClientCertificate.Subject

备注:

如果指定的 String 没有子字段,则 HttpClientCertificate 集合返回以逗号分隔的子字段列表。例如,C=US,O=Msft。

以我所拥有的极其有限的知识,我知道通用名称=在此列表中。我目前没有实际的方法来测试这个,但是解析这个值来获取您正在寻找的名称应该不难。

这是个好问题(+1),我很高兴你问这个问题,因为它可能对未来的读者有用。

我创建了一个 DotNetFiddle 示例,尽管它使用 HttpWebRequest 来获取 X509Certificate 类,但它确实有一个主题属性,该属性为 www.google.com 上的 https 返回以下值:

CN=www.google.com、O=Google Inc、L=山景城、S=加利福尼亚州、C=美国

所以我倾向于相信 HttpClientCertificate 上的主题将是相同的值(知道 CN 表示 CommonName)。


2
投票

由于证书格式的差异,您可能需要进行调整。

这是执行此操作的一些代码:

HttpClientCertificate theHttpCertificate = HttpContext.Current.Request.ClientCertificate; 
// split the subject into its parts
string[] subjectArray = theHttpCertificate.Subject.Split(',');
string[] nameParts;
string CN = string.Empty;
string firstName = string.Empty;
string lastName = string.Empty;

foreach (string item in subjectArray)
{
    string[] oneItem = item.Split('=');
    // Split the Subject CN information
    if (oneItem[0].Trim() == "CN")
    {
       CN = oneItem[1];
       if (CN.IndexOf(".") > 0)
       {// Split the name information
           nameParts = CN.Split('.');
           lastName = nameParts[0];
           firstName = nameParts[1];
        }
    }
}

2
投票

只是 Linq 中的一个单行代码。

var kvs = cert.Subject.Split(',').Select(x => new KeyValuePair<string, string>(x.Split('=')[0], x.Split('=')[1])).ToList();

返回通用列表。不要在这里使用字典,因为主题可能包含重复的字段。


0
投票

最好的办法是使用名称类型和布尔标志的内置 GetNameInfo 方法

假设您使用此扩展方法:

[return:MaybeNull]
public static X509Certificate2? GetCodeSignCertificate(this Assembly asm)
{
    if (asm is null)
    {
        throw new ArgumentNullException(nameof(asm));
    }

    if (!File.Exists(asm.Location))
    {
        return null;
    }

    using var cert=System.Security.Cryptography.X509Certificates.X509Certificate.CreateFromSignedFile(asm.Location);
    if (cert is null)
        return null;

    return new X509Certificate2(cert);
}

然后您可以使用类中的类型来获取证书,如下所示:

[TestMethod()]
public void TryGetCodeSigning()
{
    //var item = new Walter.CallStack();

    var item = new List<string>();
            
    using var cert = item.GetType().Assembly.GetCodeSignCertificate();
    Assert.IsNotNull(cert.GetNameInfo(X509NameType.SimpleName,true));

}

获取证书签名机构的名称

cert.GetNameInfo(X509NameType.SimpleName,true)

获取证书签名者的姓名

cert.GetNameInfo(X509NameType.SimpleName,false)

看看X509NameType,看看这是否适合您。


0
投票

我创建此扩展是为了处理主题名称的所有单独元素。

    public enum SubjectNameElements {CN,O,L,OU,S,C,}

public static readonly Dictionary<SubjectNameElements, string> SubNameSybms =
    new Dictionary<SubjectNameElements, string>{
            { SubjectNameElements.CN,"CN="},
            { SubjectNameElements.O ,"O="},
            { SubjectNameElements.L ,"L="},
            { SubjectNameElements.OU,"OU="},
            { SubjectNameElements.S ,"S="},
            { SubjectNameElements.C ,"C="},
    };

/// <summary>
/// Retrieve CN from subject Name of a certificate
/// </summary>
/// <param name="subjName"></param>
/// <param name="symb"></param>
/// <remarks>
/// Example:
///     GetOneElementFromSubjectName("C=IT, S=Italy, L=Torino, O=Example Italy S.p.A., OU=Example-Client, CN=www.google.com",SubjectNameElements.CN) => www.google.com
/// </remarks>
/// <returns> a string value or empty string in case of invalid options </returns>
public static string GetOneElementFromSubjectName(this X500DistinguishedName subjName, SubjectNameElements symb=SubjectNameElements.CN)
{
    string subjNameString = subjName.Name;
    try
    {
        string Symb = SubNameSybms[symb];
        string CName = subjNameString.Substring(subjNameString.IndexOf(Symb)).Split(",").First().Replace(Symb, string.Empty).Trim();
        return CName;
    }
    catch (Exception ex)
    {
        Log.Error("Error in GetOneElementFromSubjectName. Ex.Message: '" + ex.Message + "'. Ex.StackTrace: '" + ex.StackTrace + "'");
        return string.Empty;
    }

}

0
投票

对于 .NET 7 及更高版本:

string? commonName = certificate.SubjectName
    .EnumerateRelativeDistinguishedNames()
    .FirstOrDefault(x => x.GetSingleElementType().FriendlyName == "CN")
    ?.GetSingleElementValue();
© www.soinside.com 2019 - 2024. All rights reserved.