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

本文关键字:读取 客户端 证书 | 更新日期: 2023-09-27 18:20:46

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

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

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

如果使用"主题",则可能需要删除其他不必要的信息。例如,CN=localhost,OU=DepartmentName,O=CompanyName,L=Location,S=State,C=Country

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

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

我开始于:

HttpRequest.ClientCertificate

这让我想到:

HttpClientCertificate(作为返回类型)。

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

谷歌搜索:HttpClientCertificate通用名称:

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

它有一些代码:

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

然后转到:

HttpClientCertificate.Subject

备注:

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

根据我所掌握的极其有限的知识,我知道Common Name=在这个列表中。我目前还没有实际的方法来测试这个,但解析这个值以获得您要查找的名称应该不难。

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

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

CN=www.google.com,O=谷歌公司,L=山景城,S=加利福尼亚州,C=美国

所以我倾向于相信HttpClientCertificate上的Subject将是相同的值(知道CN的意思是CommonName)。

只是Linq中的一个oneliner。

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

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

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

这里有一些代码可以做到这一点:

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];
        }
    }
}

我创建了这个扩展来处理主题名称的所有单独元素。

    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;
    }
}

最好的方法是使用内置的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,看看这是否适合你。