2014-10-19 62 views
1

在C#中,我创建了一个这样的SSL连接到服务器:如何根据证书主题验证规范名称(通用名称,cn)?

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 

深奥原因无法访问,我想验证针对相同的另一个字符串(另一个主机名)证书主题。如何正确验证某个特定主机名是否与证书的主题匹配?

回答

3

下面是相关标准:RFC2818 Section 3.1

特别重要的:这个问题问如何解析的主题字符串,而正确答案是:一般情况下,你不应该这样做。解析主题字符串已被弃用,因为至少在编写RFC2818时支持subjectAltName.dNSName。

这里的单执行:(下面的文件搜索checkServerIdentityhttps://github.com/mono/mono/blob/master/mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerCertificate.cs

我以前发布的这个信息作为编辑的问题,并说这不能算作一个答案,因为代码是不完整 - 标记为TODODEPRECATED的部分。但是现在我已经更仔细地查看了代码和RFC,并发现RFC标准需要注释,并且TODO评论实际上已经完成 - 显然有人忘记删除TODO评论。

+0

您的另一个意见表明您可能没有意识到http://referencesource.microsoft.com/ - 我无法确切知道您希望在哪个类中找到.NET CLR实现,但如果它存在,它应该在那里可用。 – 2014-10-23 11:27:09

+0

@TomW谢谢你!不,我不知道...重要的是要注意许可条款的不同 - 单声道中的特定文件是MIT许可的,而MS参考源是MS-RSL,这是限制性更强的。此外,显然你只能通过某些程序集的.Net参考源 - 其中不包括System.Net.Security - 并且我一直无法找到MS参考代码的相关部分。 – 2014-10-23 13:02:23

+0

>解析主题字符串已被弃用,因为至少2000年编写RFC2818时,赞成subjectAltName.dNSName 我不同意,因为SAN不是必需的,因此您仍然必须解析主题字段。 – Crypt32 2014-10-27 12:03:16

1

如果你要模拟的SSL主题名称验证,你将不得不做一些工作,因为这个过程是不是很简单。以下是在证书中实施主题名称验证的指导:

  1. 评估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成功,检查整个证书链(到根certtificate)为名称约束扩展端验证:

    3.1。如果名称约束定义了排除部分,请验证其他名称是否与列表中的任何条目不匹配。否则,该证书的名称不被允许(步骤3.2未执行)。

    3.2。如果名称约束定义包含部分,请验证其他名称是否与本节中的任何条目相匹配。如果其他名称不属于“包含”部分中的任何条目,则该名称不适用于此证书。

  • 不幸的是,.NET不具有代表竟被一个SAN扩展的本地类,所以你必须编写自己的解码器或使用我自己的.NET扩展库(PKI.Core.dll)从PowerShell PKI project

    下面类代表SAN扩展:http://pkix2.sysadmins.lv/library/html/T_System_Security_Cryptography_X509Certificates_X509SubjectAlternativeNamesExtension.htm

    +0

    谢谢,实际上这个答案可能意外地正确,但是*可能有一个长达30页的标准写在某个地方,正式定义了主题的确切格式。在幕后,我确定他们已经阅读了相应的标准并进行了编码。我无法访问幕后的源代码(也许会找到这个单声道源代码,但是在仅仅几分钟的搜索中还没有找到它)。其他项目 - openssl,bouncycastle等,肯定有可读的源代码......我不想在这里发明一个新的解决方案。我想确保我做对了。 – 2014-10-22 10:53:42

    +0

    这不是很常见的任务。我不知道任何API或工具可以检查抽象URL(主机名)是否与连接上下文之外的主题字符串相匹配。因此,您必须使用主动连接(就像您已经这样做)或者实现您自己的URL-> Subject验证器。 – Crypt32 2014-10-22 11:21:07

    +0

    我知道。如果SslStream.AuthenticateAsClient使用的方法暴露给一般用途 - 但它不公开 - 所以我希望我将不得不重新创建它。在这个答案中写作很容易,只需编写一些正则表达式代码,以看起来有意义的方式解析字符串。但是我想更确定一点,它是正确的。这意味着要么找到一个示例(MS源代码,单声道源等),要么找到写在某处的标准,因此可以遵循它。 – 2014-10-22 17:49:56