使用Gmail SMTP服务器时,出现“根据验证过程,远程证书无效”的错误。

283

我遇到了这个错误:

根据验证程序,远程证书无效。

每当我尝试在我的C#代码中使用Gmail的SMTP服务器发送电子邮件时,都会出现这个错误。有人能指引我解决这个问题的正确方向吗?

以下是堆栈跟踪信息...

at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)
at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.TlsStream.CallProcessAuthentication(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)
at System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.PooledStream.Write(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.Mail.SmtpConnection.Flush()
at System.Net.Mail.SmtpConnection.GetConnection(String host, Int32 port)
at System.Net.Mail.SmtpTransport.GetConnection(String host, Int32 port)
at System.Net.Mail.SmtpClient.GetConnection()
at System.Net.Mail.SmtpClient.Send(MailMessage message)
at BulkEmail.frmemail.mailsending(String toaddress, String fromaddress, String fromname, String subject, String pwd, String attachements, String mailmessage, String htmlmessage, Int32 i, Int32 j, String replytoaddress)

1
你能告诉我们更多关于使用Gmail SMTP服务器的配置吗?我的猜测是:你能告诉我们更多关于SSL安全策略的信息吗(例如使用有效/无效的SSL证书)? - Brian Clozel
你能给我们提供一个代码示例,以便我们可以重现这个错误吗? - zaTricky
25个回答

344

警告:不要在生产代码中使用!

作为一种解决方法,您可以关闭证书验证。只有在需要确认错误是由于无效证书而引起时才这样做。

在调用smtpclient.Send()之前调用此方法:

[Obsolete("Do not use this in Production code!!!",true)]
static void NEVER_EAT_POISON_Disable_CertificateValidation()
{
    // Disabling certificate validation can expose you to a man-in-the-middle attack
    // which may allow your encrypted message to be read by an attacker
    // https://dev59.com/22Up5IYBdhLWcg3w-LQJ#14907718
    ServicePointManager.ServerCertificateValidationCallback =
        delegate (
            object s,
            X509Certificate certificate,
            X509Chain chain,
            SslPolicyErrors sslPolicyErrors
        ) {
            return true;
        };
}

217
这是一个黑客技巧,而不是一个修复方法? - LB.
107
希望看到一个修复方案,而不是完全关闭所有安全措施。 - Roman Starkov
85
作为解决安全问题的一种变通方法,你可以关闭安全性?什么鬼? - John Nicholas
84
不得不给这个投票负分,因为人们似乎仍然认为这是一个解决方案。这只是关闭安全性,不要在生产环境中使用,他甚至已经这么说了。唉。 - MGOwen
71
点赞。我完全同意这不应该在生产中使用,但是...我正在制作一个原型。他们提供给我的测试服务器强制我使用SSL。我对证书的处理还比较陌生,所以我只想找到一个快速解决方法,在我看来这很好,因为我不会在生产中使用它 - TweeZz
显示剩余22条评论

82

这里的链接解决了我的问题。

http://brainof-dave.blogspot.com.au/2008/08/remote-certificate-is-invalid-according.html

我访问了出现问题的服务器上 Web 服务的 URL,点击 IE 中的小安全图标,这样证书就弹出来了。然后我点击了“详细信息”选项卡,点击“复制到文件”按钮,这使我能够将证书导出为 .cer 文件。一旦我在本地拥有了证书,我就可以使用以下说明将其导入到服务器上的证书存储中。

启动新的 MMC。 文件 --> 添加/删除插件... 点击添加... 选择“证书”,然后点击添加。 选中“计算机帐户”单选按钮。点击下一步。

在下一个屏幕中选择客户端计算机。然后点击完成。 点击关闭。 点击确定。 现在,将证书安装到可信根证书颁发机构证书存储中。这将允许所有用户都信任该证书。


当通过导入工具在证书上导入证书而不是通过快照时,+1仅适用于您的用户帐户。使用快照允许您选择导入对象,用户帐户、服务帐户或所有人。感谢您的指引。我曾经困惑了一两分钟! - medina
2
如果您想使用命令行自动化所有开发/测试工作站,则需要在管理员命令提示符中运行以下命令:certutil -f -p test -importPFX Root devcert.pfxcertutil -f -p test -importPFX MY devcert.pfx(假设PFX密码为“test”)。 - DeepSpace101
如果您正在使用自签名证书进行测试,那么这是修复它的最佳方法,感谢 T-Rex! - ToastyMallows

45

稍微晚了一些,但如果您正在寻找像Yury那样的解决方案,以下代码将帮助您确定问题是否与自签名证书相关,如果是,则忽略自签名错误。如果需要,您当然可以检查其他SSL错误。

我们使用的代码(由Microsoft提供 - http://msdn.microsoft.com/en-us/library/office/dd633677(v=exchg.80).aspx)如下:

  private static bool CertificateValidationCallBack(
         object sender,
         System.Security.Cryptography.X509Certificates.X509Certificate certificate,
         System.Security.Cryptography.X509Certificates.X509Chain chain,
         System.Net.Security.SslPolicyErrors sslPolicyErrors)
    {
  // If the certificate is a valid, signed certificate, return true.
  if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
  {
    return true;
  }

  // If there are errors in the certificate chain, look at each error to determine the cause.
  if ((sslPolicyErrors & System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
  {
    if (chain != null && chain.ChainStatus != null)
    {
      foreach (System.Security.Cryptography.X509Certificates.X509ChainStatus status in chain.ChainStatus)
      {
        if ((certificate.Subject == certificate.Issuer) &&
           (status.Status == System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.UntrustedRoot))
        {
          // Self-signed certificates with an untrusted root are valid. 
          continue;
        }
        else
        {
          if (status.Status != System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.NoError)
          {
            // If there are any other errors in the certificate chain, the certificate is invalid,
         // so the method returns false.
            return false;
          }
        }
      }
    }

    // When processing reaches this line, the only errors in the certificate chain are 
// untrusted root errors for self-signed certificates. These certificates are valid
// for default Exchange server installations, so return true.
    return true;
  }
  else
  {
 // In all other cases, return false.
    return false;
  }
}

1
对于我的情况,sslPolicyErrors 是 RemoteCertificateNameMismatch,我修改了证书检查,因为我们的主题和颁发者值不相同。 - Ufuk Hacıoğulları
2
Exchange服务器使用的X509证书不断在自签名证书和受信任证书之间切换。使用这段代码不仅帮助了我的网络管理员和我找出了问题所在,而且通过仅绕过自签名证书,彻底解决了问题。这太完美了! - Bret

36

当证书无效时,您可以通过询问用户是否要继续来改进代码。您想要继续吗? 如下所示:

ServicePointManager.ServerCertificateValidationCallback = 
    new RemoteCertificateValidationCallback(ValidateServerCertificate);

然后添加一个像这样的方法:

public static bool ValidateServerCertificate(object sender,X509Certificate certificate,X509Chain chain,SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
        return true;
    else
    {
        if (System.Windows.Forms.MessageBox.Show("The server certificate is not valid.\nAccept?", "Certificate Validation", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
            return true;
        else
            return false;
    }
}

73
如果这是一个云应用,谁会点击“继续”按钮? - Konstantin Isaev
由于ServicePointManager.ServerCertificateValidationCallback是一个委托,最好使用ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertificate来进行操作,然后在获取结果后记得取消订阅,即ServicePointManager.ServerCertificateValidationCallback -= ValidateServerCertificate。 - undefined

25

我遇到了完全相同的问题,后来发现默认情况下Avast杀毒软件的邮件防护(Mail Shield)开启了"扫描SSL连接"功能。一定要关闭这个功能

据我所知,Avast会打开邮件,扫描其中是否有病毒,并用自己的证书进行签名,因此邮件就不再是由Gmail证书签名,这导致了出现错误。

解决方法1:

  • 关闭你的杀毒软件中的SSL扫描功能(或整个邮件防护)。

解决方法2(从安全角度考虑应该是最佳选择):

  • 以某种方式获取杀毒软件使用的证书(Avast有一个导出选项)
  • 在连接到Gmail服务器之前将其导入IMAP/POP/SMTP客户端中。

1
你节省了我很多时间。如果不是你,我可能要花很长时间才能弄清楚是我的杀毒软件的问题,而不是我的代码的问题。 - arao6
感谢您的回复。您为我节省了很多时间。 - SoftwareDveloper

24

我知道我现在参与有点晚了,但我还没有看到这里有指向TLS流的system.diagnostics日志的答案。

在对代码进行任何更改之前,请确保您了解问题所在。 AuthenticationException是一个非常通用的异常,它并没有提供太多信息。要了解底层发生了什么,请编辑应用程序的app.config文件(或创建一个新文件),并确保您已在system.diagnostics部分启用了System.Net跟踪源,例如:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <trace autoflush="true" />
    <sharedListeners>
      <add name="file" initializeData="c:\network.log" type="System.Diagnostics.TextWriterTraceListener" />
    </sharedListeners>
    <sources>
      <source name="System.Net" switchValue="Verbose">
        <listeners>
          <add name="file" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
</configuration>
重新运行您的应用程序并检查 c:\network.log 文件。您应该在那里看到关于您的TLS(SSL)连接的详细信息,例如:
System.Net Information: 0 : [12764] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = f44368:535f958, targetName = localhost, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
System.Net Information: 0 : [12764] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=OK).
System.Net Information: 0 : [12764] Remote certificate: [Version]
  V3

[Subject]
  CN=test
  Simple Name: test
  DNS Name: example.com

[Issuer]
  CN=Root CA
  Simple Name: Root CA
  DNS Name: Root CA

...

[Signature Algorithm]
  sha256RSA(1.2.840.113549.1.1.11)

[Public Key]
  Algorithm: RSA
  Length: 2048
  Key Blob: ....
System.Net Information: 0 : [12764] SecureChannel#38496415 - Remote certificate has errors:
System.Net Information: 0 : [12764] SecureChannel#38496415 -    Certificate name mismatch.
System.Net Information: 0 : [12764] SecureChannel#38496415 - Remote certificate was verified as invalid by the user.
System.Net Error: 0 : [12764] Exception in AppDomain#10923418::UnhandledExceptionHandler - The remote certificate is invalid according to the validation procedure..

了解问题的原因,您应该能够解决它或者至少缩小您在 Google 搜索中的范围。


16

因为SSL的原因,从Outlook发送邮件时遇到了相同的错误。尝试将EnableSSL设置为false可以解决问题。

示例:

var smtp = new SmtpClient
                {
                    Host = "smtp.gmail.com",                   
                    Port = 587,
                    EnableSsl = false,
                    DeliveryMethod = SmtpDeliveryMethod.Network,
                    UseDefaultCredentials = false,                   
                    Credentials = new NetworkCredential("xxx@gmail.com", "xxxxx")
                };

4
Gmail不允许您在将SSL设置为false时进行连接,我尝试了您的解决方案,但对我无效。 - Zeeshan Ajmal
是的,这就是我所说的“基本”(与“无”或“ssl”相比)......有时那些电子邮件设置很棘手。 - granadaCoder

11

当我尝试通过代理服务器 (Usergate) 使用 SmtpClient 发送电子邮件时,也遇到了同样的错误。

该错误是由于验证证书包含的服务器地址与代理服务器的地址不相等导致的。我的解决方案是,在检查证书时出现错误时,接收证书,导出并进行检查。

public static bool RemoteServerCertificateValidationCallback(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;

        // if got an cert auth error
        if (sslPolicyErrors != SslPolicyErrors.RemoteCertificateNameMismatch) return false;
        const string sertFileName = "smpthost.cer";

        // check if cert file exists
        if (File.Exists(sertFileName))
        {
            var actualCertificate = X509Certificate.CreateFromCertFile(sertFileName);
            return certificate.Equals(actualCertificate);
        }

        // export and check if cert not exists
        using (var file = File.Create(sertFileName))
        {
            var cert = certificate.Export(X509ContentType.Cert);
            file.Write(cert, 0, cert.Length);
        }
        var createdCertificate = X509Certificate.CreateFromCertFile(sertFileName);
        return certificate.Equals(createdCertificate);
    }

以下是我的电子邮件发送器类的完整代码:

public class EmailSender
{
    private readonly SmtpClient _smtpServer;
    private readonly MailAddress _fromAddress;

    public EmailSender()
    {
        ServicePointManager.ServerCertificateValidationCallback = RemoteServerCertificateValidationCallback;
        _smtpServer = new SmtpClient();
    }

    public EmailSender(string smtpHost, int smtpPort, bool enableSsl, string userName, string password, string fromEmail, string fromName) : this()
    {
        _smtpServer.Host = smtpHost;
        _smtpServer.Port = smtpPort;
        _smtpServer.UseDefaultCredentials = false;
        _smtpServer.EnableSsl = enableSsl;
        _smtpServer.Credentials = new NetworkCredential(userName, password);

        _fromAddress = new MailAddress(fromEmail, fromName);
    }

    public bool Send(string address, string mailSubject, string htmlMessageBody,
        string fileName = null)
    {
        return Send(new List<MailAddress> { new MailAddress(address) }, mailSubject, htmlMessageBody, fileName);
    }

    public bool Send(List<MailAddress> addressList, string mailSubject, string htmlMessageBody,
        string fileName = null)
    {
        var mailMessage = new MailMessage();
        try
        {
            if (_fromAddress != null)
                mailMessage.From = _fromAddress;

            foreach (var addr in addressList)
                mailMessage.To.Add(addr);

            mailMessage.SubjectEncoding = Encoding.UTF8;
            mailMessage.Subject = mailSubject;

            mailMessage.Body = htmlMessageBody;
            mailMessage.BodyEncoding = Encoding.UTF8;
            mailMessage.IsBodyHtml = true;

            if ((fileName != null) && (System.IO.File.Exists(fileName)))
            {
                var attach = new Attachment(fileName, MediaTypeNames.Application.Octet);
                attach.ContentDisposition.CreationDate = System.IO.File.GetCreationTime(fileName);
                attach.ContentDisposition.ModificationDate = System.IO.File.GetLastWriteTime(fileName);
                attach.ContentDisposition.ReadDate = System.IO.File.GetLastAccessTime(fileName);
                mailMessage.Attachments.Add(attach);
            }
            _smtpServer.Send(mailMessage);
        }
        catch (Exception e)
        {
            // TODO lor error
            return false;
        }
        return true;
    }

    public static bool RemoteServerCertificateValidationCallback(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
        return true;

    // if got an cert auth error
    if (sslPolicyErrors != SslPolicyErrors.RemoteCertificateNameMismatch) return false;
    const string sertFileName = "smpthost.cer";

    // check if cert file exists
    if (File.Exists(sertFileName))
    {
        var actualCertificate = X509Certificate.CreateFromCertFile(sertFileName);
        return certificate.Equals(actualCertificate);
    }

    // export and check if cert not exists
    using (var file = File.Create(sertFileName))
    {
        var cert = certificate.Export(X509ContentType.Cert);
        file.Write(cert, 0, cert.Length);
    }
    var createdCertificate = X509Certificate.CreateFromCertFile(sertFileName);
    return certificate.Equals(createdCertificate);
}

}


9

您确定正在使用正确的SMTP服务器地址吗?

smtp.google.com和smtp.gmail.com都可以使用,但SSL证书是针对第二个发行的。


6
我的问题出现在Windows 2003服务器上,在调用AuthenticateAsClient时。上述解决方案(例如规避ServicePointManager.ServerCertificateValidationCallback)无效。
原来这是Windows 2003中的一个错误,有一个热修复程序:
“使用加密API的应用程序无法验证Windows Server 2003中的X.509证书”

https://support.microsoft.com/en-us/kb/938397

安装此热修复程序解决了我的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接