如何根据证书使用者验证规范名称(公用名,cn)
本文关键字:cn 何根 证书 使用者 规范名 验证 | 更新日期: 2023-09-27 18:27:09
在 C# 中,我创建了一个与服务器的 SSL 连接,如下所示:
var hostname = "www.example.com";
var client = new TcpClient(hostname, 443);
var sslStream = new SslStream(client.GetStream());
sslStream.AuthenticateAsClient(hostname);
在上述操作完成而不引发异常后,我知道服务器证书已经过验证,主机名与主题充分匹配。 主题可以通过属性 sslStream.RemoteCertificate.Subject
访问,该属性是 DN 格式的字符串,类似于
CN=www.example.com, O=Example Inc, L=New York, S=New York, C=US
或
CN=*.example.com, OU=Certificate Authority Validated
出于深奥的原因,我想针对同一证书主题验证另一个字符串(另一个主机名(。 如何正确验证特定主机名是否与证书的主题匹配?
以下是相关标准: RFC2818 第 3.1 节。
特别重要的是:这个问题询问如何解析主题字符串,正确答案是:一般来说,你不应该这样做。 至少自 2000 年编写 Subject 字符串时RFC2818以来,解析 Subject 字符串已被弃用,取而代之的是 subjectAltName.dNSName。
这是单声道实现:(在以下文件中搜索checkServerIdentity
(https://github.com/mono/mono/blob/master/mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerCertificate.cs
我之前发布了此信息作为对问题的编辑,并说它不算作答案,因为代码不完整 - 标记为 TODO
和 DEPRECATED
的代码部分。 但是现在我已经更仔细地审查了代码和 RFC,发现 RFC 标准需要DEPRECATED
注释,并且实际上已经完成了TODO
注释 - 只是显然有人忘记删除 TODO 注释。
模拟SSL使用者名称验证,则必须执行一些工作,因为此过程不是很简单。下面是在证书中实现使用者名称验证的指南:
- 评估 X509Certificate2 对象的使用者备用名称扩展。如果此扩展不存在,则:
1.1. 使用以下正则表达式模式从证书中提取 CN 属性: 'CN=([^,]+)'
并使用所需的名称对其进行验证。由于 CN 属性可能包含通配符,因此应使用正则表达式进行验证。下面是一个简单的通配符类的示例。
class Wildcard : Regex {
public Wildcard(String pattern) : base(WildcardToRegex(pattern)) { }
public Wildcard(String pattern, RegexOptions options) : base(WildcardToRegex(pattern), options) { }
public static String WildcardToRegex(String pattern) {
return "^" + Escape(pattern).Replace("''*", ".*") + "$";
}
}
如果显示 SAN 扩展名,请忽略主题字段验证,并以相同的方式对 DNSName 和 IPAddress 备用名称集合使用地址验证。
如果步骤 1 或 2 成功,请检查名称约束扩展结束的完整证书链(最多为根证书(:
3.1. 如果名称约束定义了排除部分,请验证另一个名称是否与列表中的任何条目不匹配。否则,不允许使用此证书的名称(不执行步骤 3.2(。
3.2. 如果名称约束定义了包含部分,请验证另一个名称是否与本节中的任何条目匹配。如果其他名称不属于"包含"部分中的任何条目,则此证书不允许使用此名称。
不幸的是,.NET 没有表示 SAN 扩展的本机类,因此您必须编写自己的解码器或使用我自己的 .NET 扩展库 (PKI.Core.dll(来自PowerShell PKI项目
以下类表示 SAN 扩展:http://pkix2.sysadmins.lv/library/html/T_System_Security_Cryptography_X509Certificates_X509SubjectAlternativeNamesExtension.htm