2014-07-11 41 views
5

Tôi sử dụng số SmtpClient để gửi email qua máy chủ SMTP bằng SSL. Máy chủ SMTP này được định cấu hình để sử dụng chứng chỉ tự ký.Thực hiện xác thực chứng chỉ mặc định khi ghi đè ServicePointManager.ServerCertificateValidationCallback

Vì nó nằm ngoài phạm vi để tôi cài đặt chứng chỉ trên tất cả các máy khách, tôi đã thêm ServicePointManager.ServerCertificateValidationCallback để thực hiện xác thực tùy chỉnh cho chứng chỉ tự ký của chúng tôi.

Tuy nhiên, tôi muốn thực thi xác thực tùy chỉnh chỉ khi tôi đang sử dụng SmtpClient và hoàn nguyên về hành vi mặc định khi xác thực chứng chỉ được thực hiện ở nơi khác.

Vì gọi lại là thuộc tính tĩnh, tôi cũng nên đảm bảo rằng cuộc gọi lại không được gọi bởi một chuỗi khác so với thư tôi sử dụng để gửi email.

tôi đã đưa ra đoạn mã sau:

Private Shared _defaultServerCertificateValidationCallback As System.Net.Security.RemoteCertificateValidationCallback 
Private Shared _overrideSmtpCertificateValidation As Boolean = False 
Private Shared _callbackLocker As New Object() 

Private Shared Function OurCertificateValidation(s As Object, certificate As Security.Cryptography.X509Certificates.X509Certificate, chain As Security.Cryptography.X509Certificates.X509Chain, sslPolicyErrors As Net.Security.SslPolicyErrors) As Boolean 

    SyncLock _callbackLocker 

     If _overrideSmtpCertificateValidation Then 
      _overrideSmtpCertificateValidation = False 
      Dim actualCertificate = Security.Cryptography.X509Certificates.X509Certificate.CreateFromCertFile("selfSignedSmtp.cert") 
      Return certificate.Equals(actualCertificate) 
     Else 
      'Should execute the default certificate validation. 
      Return _defaultServerCertificateValidationCallback(s, certificate, chain, sslPolicyErrors) 
     End If 

    End SyncLock 

End Function 

Public Sub SendMail() 
    _defaultServerCertificateValidationCallback = System.Net.ServicePointManager.ServerCertificateValidationCallback 
    System.Net.ServicePointManager.ServerCertificateValidationCallback = New System.Net.Security.RemoteCertificateValidationCallback(AddressOf OurCertificateValidation) 
    _overrideSmtpCertificateValidation = True 
    'Send mail using SMTP client here... 
End Sub 

gì mã này nên làm khi gửi một email là để giữ mặc định ServerCertificateValidationCallback trong một biến, cờ xác nhận tùy chỉnh được thực hiện, thực hiện các xác nhận tùy chỉnh trên cùng một chuỗi với tên SmtpClient, ngăn các chủ đề khác thực hiện xác thực tùy chỉnh bằng cách sử dụng SyncLock, sau đó hoàn nguyên về hành vi mặc định và cho phép các chủ đề khác tiếp tục xác thực chứng chỉ mặc định.

Tuy nhiên, ServerCertificateValidationCallback được đặt thành Nothing theo mặc định, vì vậy tôi không thể gọi rõ ràng cuộc gọi xác thực mặc định.

Có điều gì tôi có thể gọi thay vì Return _defaultServerCertificateValidationCallback(s, certificate, chain, sslPolicyErrors) có thể thực thi xác thực chứng chỉ mặc định khi cần không?

Trả lời

6

Tôi phát hiện ra rằng xác thực chứng chỉ mặc định được thực hiện trước ServerCertificateValidationCallback. Nếu sslPolicyErrors được đặt thành None, điều đó có nghĩa là quá trình xác thực đã thành công.

Tôi nghĩ rằng mã này đáp ứng nhu cầu của tôi và dường như đủ an toàn cho hoàn cảnh của tôi:

Private Shared _customCertificateLocker As New Object() 
Private Shared _customCertificateThreadId As Integer 
Private Shared _customCertificatePath As String 

'Returns True if the certificate is valid according to the default certificate validation algorithm, 
'or else if the certificate is the same as the specified custom certificate. 
Private Shared Function CustomCertificateValidation(s As Object, certificate As Security.Cryptography.X509Certificates.X509Certificate, chain As Security.Cryptography.X509Certificates.X509Chain, sslPolicyErrors As Net.Security.SslPolicyErrors) As Boolean 

    If sslPolicyErrors = System.Net.Security.SslPolicyErrors.None Then 
     'The certificate is valid according to the default certificate validation algorithm 
     Return True 
    Else 

     'Validates only for the thread that called UseCustomCertificate. 
     If System.Threading.Thread.CurrentThread.ManagedThreadId = _customCertificateThreadId Then 

      If Not File.Exists(_customCertificatePath) Then 
       Throw New FileNotFoundException("Certificate not found at path : '" & _customCertificatePath & "'.") 
      End If 

      Try 
       'Validates the specified custom certificate against the server certificate. 
       Dim actualCertificate = Security.Cryptography.X509Certificates.X509Certificate.CreateFromCertFile(_customCertificatePath) 
       Return certificate.Equals(actualCertificate) 
      Catch 
       Return False 
      End Try 

     End If 

    End If 

    Return False 

End Function 

'Performs an Action that needs the custom certificate validation, on one thread at a time. 
Public Shared Sub UseCustomCertificate(customCertificatePath As String, action As Action) 

    'Used to make sure that the ServerCertificateValidationCallback stays the same during the Action, even if multiple threads call this method. 
    SyncLock _customCertificateLocker 

     _customCertificateThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId 
     _customCertificatePath = customCertificatePath 
     System.Net.ServicePointManager.ServerCertificateValidationCallback = New System.Net.Security.RemoteCertificateValidationCallback(AddressOf CustomCertificateValidation) 

     Try 
      action() 

     Finally 
      _customCertificateThreadId = 0 
      _customCertificatePath = String.Empty 
      System.Net.ServicePointManager.ServerCertificateValidationCallback = Nothing 
     End Try 

    End SyncLock 

End Sub 

Ví dụ về sử dụng:

UseCustomCertificate("C:\example.cert", 
        Sub() 
         'Send email with SmtpClient... 
        End Sub) 

tôi sẽ chấp nhận câu trả lời này chỉ khi tôi không nhận được một câu trả lời an toàn hơn/tốt hơn trong một vài ngày.

Các vấn đề liên quan