我们的应用程序需要一条数据,该数据包含在客户端证书的通用名称中。目前,我正在尝试从 HttpContext.Current.Request.ClientCertificate 获取它。我该如何读出这个?可悲的是,当我弄清楚为什么 SoapUI 不发送证书时,我正在尝试盲目编码,所以除了阅读 MSDN 上的对象并浏览空属性之外,我没有尝试太多,但我不确定我在寻找什么。回顾一下,我需要做什么才能从该证书中提取通用名? TIA
我可能来不及回答你的问题了,但我希望这能帮助其他正在寻找从证书中获取通用名称的方法的人。
如果您使用“主题”,您可能需要删除其他不必要的信息。 例如,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)
我对证书略知一二。这是我的工作流程:
我开始于:
这让我:
HttpClientCertificate(作为返回类型)。
它似乎有一些属性,但没有一个明确命名为通用名称。
Google 搜索:HttpClientCertificate 通用名称:
其中有一些代码:
//extracting Common name from certificate
Subject = cert.Subject.ToString();
然后去了:
备注:
如果指定的 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)。
由于证书格式的差异,您可能需要进行调整。
这是执行此操作的一些代码:
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];
}
}
}
只是 Linq 中的一个单行代码。
var kvs = cert.Subject.Split(',').Select(x => new KeyValuePair<string, string>(x.Split('=')[0], x.Split('=')[1])).ToList();
返回通用列表。不要在这里使用字典,因为主题可能包含重复的字段。
最好的办法是使用名称类型和布尔标志的内置 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,看看这是否适合您。
我创建此扩展是为了处理主题名称的所有单独元素。
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;
}
}
对于 .NET 7 及更高版本:
string? commonName = certificate.SubjectName
.EnumerateRelativeDistinguishedNames()
.FirstOrDefault(x => x.GetSingleElementType().FriendlyName == "CN")
?.GetSingleElementValue();