【问题标题】:Execute default certificate validation when overriding ServicePointManager.ServerCertificateValidationCallback覆盖 ServicePointManager.ServerCertificateValidationCallback 时执行默认证书验证
【发布时间】:2014-09-02 03:40:16
【问题描述】:

我使用SmtpClient 通过使用 SSL 的 SMTP 服务器发送电子邮件。此 SMTP 服务器配置为使用自签名证书。

由于我无法在所有客户端计算机上安装证书,我添加了一个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保留在变量中,标记要执行的自定义验证,在与SmtpClient相同的线程上执行自定义验证,防止其他线程使用SyncLock 执行自定义验证,然后恢复为默认行为并允许其他线程继续使用默认证书验证。

但是,ServerCertificateValidationCallback 默认设置为Nothing,因此我无法显式调用默认验证回调。

有什么我可以调用而不是 Return _defaultServerCertificateValidationCallback(s, certificate, chain, sslPolicyErrors) 来在需要时执行默认证书验证吗?

【问题讨论】:

    标签: c# vb.net multithreading ssl smtpclient


    【解决方案1】:

    我发现默认证书验证是在ServerCertificateValidationCallback 之前完成的。如果sslPolicyErrors设置为None,则表示验证成功。

    我认为这段代码满足了我的需求,并且对于我的情况来说似乎足够安全:

    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)
    

    只有在几天内我没有得到更安全/更好的答案时,我才会接受这个答案。

    【讨论】:

      猜你喜欢
      • 2012-02-21
      • 2021-11-16
      • 2020-01-17
      • 2014-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-12
      • 1970-01-01
      相关资源
      最近更新 更多