在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
出于深奥的原因,我想针对同一个证书主题验证另一个字符串(另一个主机名)。如何正确验证特定主机名是否与证书主题相匹配?
答案 0 :(得分:3)
以下是相关标准:RFC2818 Section 3.1。
特别重要:问题是如何解析主题字符串,正确答案是:一般情况下,你不应该这样做。解析Subject字符串已经被弃用,因为至少2000年编写RFC2818时,有利于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评论。
答案 1 :(得分:1)
如果要模拟SSL主题名称验证,则必须执行一些操作,因为此过程不是很简单。以下是在证书中实施主题名称验证的指南:
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。如果Name Constraints定义Exclude部分,请验证另一个名称是否与列表中的任何条目不匹配。否则,此证书不允许使用该名称(不执行步骤3.2)。
3.2。如果Name Constraints定义Include部分,请验证另一个名称是否与此部分中的任何条目匹配。如果其他名称不属于“包含”部分中的任何条目,则此证书不允许使用此名称。
不幸的是,.NET没有一个可以代表SAN扩展的本机类,因此您必须编写自己的解码器或使用我自己的.NET扩展库(PKI.Core.dll)来自{{ 3}}
以下类代表SAN扩展:PowerShell PKI project