2014-10-19 123 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