2014-07-11 18 views
5

SmtpClient을 사용하여 SSL을 사용하는 SMTP 서버를 통해 전자 메일을 보냅니다. 이 SMTP 서버는 자체 서명 인증서를 사용하도록 구성됩니다.ServicePointManager.ServerCertificateValidationCallback을 재정의 할 때 기본 인증서 유효성 검사 실행

모든 클라이언트 컴퓨터에 인증서를 설치할 수있는 범위가 좁기 때문에 ServicePointManager.ServerCertificateValidationCallback을 추가하여 자체 서명 된 인증서의 사용자 지정 유효성 검사를 구현했습니다.

그러나 SmtpClient을 사용할 때만 사용자 지정 유효성 검사를 실행하고 다른 곳에서 인증서 유효성 검사를 수행 할 때 기본 동작으로 되돌리고 싶습니다.

콜백은 정적 속성이므로 콜백이 전자 메일을 보내는 데 사용하는 스레드가 아닌 다른 스레드에서 호출되지 않도록해야합니다.

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 

이메일을 보내는 변수에 기본 ServerCertificateValidationCallback을 유지하는 것입니다 때이 코드를 어떻게해야합니까, 사용자 지정 유효성 검사를 수행 할 플래그는 사용자 지정 유효성 검사를 수행 :

는 다음 코드를 내놓았다 과 같은 스레드에서 SyncLock을 사용하여 다른 스레드가 사용자 지정 유효성 검사를 수행하지 못하게 한 다음 기본 동작으로 되돌리고 다른 스레드가 기본 인증서 유효성 검사를 계속할 수 있도록합니다.

그러나 기본적으로 ServerCertificateValidationCallbackNothing으로 설정되어 있으므로 기본 유효성 검사 콜백을 명시 적으로 호출 할 수 없습니다.

필요할 때 기본 인증서 유효성 검사를 실행하는 Return _defaultServerCertificateValidationCallback(s, certificate, chain, sslPolicyErrors) 대신 호출 할 수있는 것이 있습니까?

답변

6

기본 인증서 유효성 검사가 ServerCertificateValidationCallback보다 먼저 수행된다는 것을 알게되었습니다. sslPolicyErrorsNone으로 설정된 경우 유효성 검사가 성공했음을 의미합니다. 사용의

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 

예 : 나는을하지 않은 경우에만 나는이 대답을 받아 들일 것입니다

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

이 코드는 내 요구를 충족하고 내 상황에 대한 충분한 안전 보인다 생각 며칠 후에 더 안전하고 더 나은 대답을 얻을 수 있습니다.