【问题标题】:SmtpClient with Gmail带有 Gmail 的 SmtpClient
【发布时间】:2012-04-05 18:30:50
【问题描述】:

我正在为一个学校项目开发一个邮件客户端。我已经设法在 C# 中使用SmtpClient 发送电子邮件。这适用于任何服务器,但不适用于 Gmail。我相信这是因为谷歌使用了 TLS。我尝试在SmtpClient 上将EnableSsl 设置为true,但这并没有什么不同。

这是我用来创建SmtpClient 并发送电子邮件的代码。

this.client = new SmtpClient("smtp.gmail.com", 587);
this.client.EnableSsl = true;
this.client.UseDefaultCredentials = false;
this.client.Credentials = new NetworkCredential("username", "password");

try
{
    // Create instance of message
    MailMessage message = new MailMessage();

    // Add receiver
    message.To.Add("myemail@mydomain.com");

    // Set sender
    // In this case the same as the username
    message.From = new MailAddress("username@gmail.com");

    // Set subject
    message.Subject = "Test";

    // Set body of message
    message.Body = "En test besked";

    // Send the message
    this.client.Send(message);

    // Clean up
    message = null;
}
catch (Exception e)
{
    Console.WriteLine("Could not send e-mail. Exception caught: " + e);
}

这是我在尝试发送电子邮件时遇到的错误。

Could not send e-mail. Exception caught: System.Net.Mail.SmtpException: Message could not be sent. ---> System.IO.IOException: The authentication or decryption has failed. ---> System.InvalidOperationException: SSL authentication error: RemoteCertificateNotAvailable, RemoteCertificateChainErrors
  at System.Net.Mail.SmtpClient.<callback>m__4 (System.Object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, SslPolicyErrors sslPolicyErrors) [0x00000] in <filename unknown>:0 
  at System.Net.Security.SslStream+<BeginAuthenticateAsClient>c__AnonStorey7.<>m__A (System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Int32[] certErrors) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.SslClientStream.OnRemoteCertificateValidation (System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Int32[] errors) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.SslStreamBase.RaiseRemoteCertificateValidation (System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Int32[] errors) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.SslClientStream.RaiseServerCertificateValidation (System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Int32[] certificateErrors) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.validateCertificates (Mono.Security.X509.X509CertificateCollection certificates) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.ProcessAsTls1 () [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.Handshake.HandshakeMessage.Process () [0x00000] in <filename unknown>:0 
  at (wrapper remoting-invoke-with-check) Mono.Security.Protocol.Tls.Handshake.HandshakeMessage:Process ()
  at Mono.Security.Protocol.Tls.ClientRecordProtocol.ProcessHandshakeMessage (Mono.Security.Protocol.Tls.TlsStream handMsg) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.RecordProtocol.InternalReceiveRecordCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---
  at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---
  at System.Net.Mail.SmtpClient.Send (System.Net.Mail.MailMessage message) [0x00000] in <filename unknown>:0 
  at P2Mailclient.SMTPClient.send (P2Mailclient.Email email) [0x00089] in /path/to/my/project/SMTPClient.cs:57 

有人知道我为什么会收到这个错误吗?

【问题讨论】:

  • 在设置凭据之前尝试设置client.UseDefaultCredentials = false;
  • @Reniuz 这并没有什么不同。
  • 看起来像证书问题 - 请参阅我的答案。
  • 我已经编辑了我的答案并正在写下一条评论以通知您。请查看编辑后的答案。
  • @konrad.kruczynski 我现在不在我的电脑前,但我肯定会尽快查看它。感谢您的帮助。

标签: c# mono gmail monodevelop smtpclient


【解决方案1】:

Gmail 的 SMTP 服务器要求您使用有效的 gmail 电子邮件/密码组合来验证您的请求。您还需要启用 SSL。如果实际上无法看到传递的所有变量的转储,我可以做出的最佳猜测是您的凭据无效,请确保您使用的是有效的 GMAIL 电子邮件/密码组合。

您可能想阅读here 以了解一个工作示例。

编辑:好的,这是我当时编写和测试的东西,对我来说效果很好:

public static bool SendGmail(string subject, string content, string[] recipients, string from) {
    if (recipients == null || recipients.Length == 0)
        throw new ArgumentException("recipients");

    var gmailClient = new System.Net.Mail.SmtpClient {
        Host = "smtp.gmail.com",
        Port = 587,
        EnableSsl = true,
        UseDefaultCredentials = false,
        Credentials = new System.Net.NetworkCredential("******", "*****")
    };

    using (var msg = new System.Net.Mail.MailMessage(from, recipients[0], subject, content)) {
        for (int i = 1; i < recipients.Length; i++)
            msg.To.Add(recipients[i]);

        try {
            gmailClient.Send(msg);
            return true;
        }
        catch (Exception) {
            // TODO: Handle the exception
            return false;
        }
    }
}

如果您需要更多信息,请参阅类似的 SO 文章 here

【讨论】:

  • 我确定我的凭据已写入。我尝试将它们复制/粘贴到 Gmail 登录框中并能够登录。我更新了我的问题以显示我的代码的简化版本,其中包含硬编码的变量值。
  • @SimonBS 您是只使用您的谷歌用户名还是您的凭证的完整电子邮件地址?我认为从非 Google 域登录需要完整的电子邮件地址才能登录
  • 我正在使用整个电子邮件。例如。 myusername@gmail.com.
  • @SimonBS 并且您在您的 gmail 帐户中启用了 POP?
  • 我现在正在工作,所以我现在没有 C# 可以玩。当我回到家时,我会看看我是否可以为您写一个工作示例并适当地编辑我的答案。
【解决方案2】:

这段代码对我来说很好用,尝试将其粘贴到 LinqPad 中,编辑邮件地址和密码,然后告诉我们你看到了什么:

var client = new System.Net.Mail.SmtpClient("smtp.gmail.com", 587);
client.EnableSsl = true;
client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential("me@gmail.com", "xxxxxxx");

try
{
    // Create instance of message
    System.Net.Mail.MailMessage message = new System.Net.Mail.MailMessage();

    // Add receiver
    message.To.Add("me@gmail.com");

    // Set sender
    // In this case the same as the username
    message.From = new System.Net.Mail.MailAddress("me@gmail.com");

    // Set subject
    message.Subject = "Test";

    // Set body of message
    message.Body = "En test besked";

    // Send the message
    client.Send(message);

    // Clean up
    message = null;
}
catch (Exception e)
{
    Console.WriteLine("Could not send e-mail. Exception caught: " + e);
}

【讨论】:

  • 我在 Mac 上,没有 LinqPad。我只使用 MonoDevelop。使用上面的代码给了我完全相同的结果。
  • @SimonBS 您可能想用 Mono 重新标记问题。这可能是问题的一部分,因为似乎有多个人设法让相同的代码在 VS 中工作。
  • @psubsee2003 你可能是对的。我没有想到。我对 C# 还是很陌生,并不认为 IDE 很重要。我现在重新标记它。
  • @SimonBS 我从来没有在 VisualStudio 之外做过任何工作,所以我不知道你使用 MonoDevelop 是否重要,但鉴于事实,这似乎是下一件事检查
【解决方案3】:

我认为,您需要验证用于建立 SSL 连接的服务器证书.....

使用以下代码发送带有验证服务器证书的邮件.....

            this.client = new SmtpClient(_account.SmtpHost, _account.SmtpPort);
            this.client.EnableSsl = _account.SmtpUseSSL;
            this.client.Credentials = new NetworkCredential(_account.Username, _account.Password);

        try
        {
            // Create instance of message
            MailMessage message = new MailMessage();

            // Add receivers
            for (int i = 0; i < email.Receivers.Count; i++)
                message.To.Add(email.Receivers[i]);

            // Set sender
            message.From = new MailAddress(email.Sender);

            // Set subject
            message.Subject = email.Subject;

            // Send e-mail in HTML
            message.IsBodyHtml = email.IsBodyHtml;

            // Set body of message
            message.Body = email.Message;

            //validate the certificate
            ServicePointManager.ServerCertificateValidationCallback =
            delegate(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
            { return true; };


            // Send the message
            this.client.Send(message);

            // Clean up
            message = null;
        }
        catch (Exception e)
        {
            Console.WriteLine("Could not send e-mail. Exception caught: " + e);
        }

导入System.Security.Cryptography.X509Certificates命名空间以使用ServicePointManager

【讨论】:

  • 是的,当我在没有验证 X509Certificates 的情况下尝试代码时,它对我来说也很好......但有时,在尝试 ssl 连接时,它需要验证证书......
  • 我的同事在通过我们的电子邮件服务器从 Android 上的 Mono 应用程序发送电子邮件时遇到了问题。这解决了问题。太棒了!
  • 这也是我的解决方法。我可以运行我的 Mono 应用程序并从命令提示符发送邮件,但是一旦它进入 cron,它就会停止工作。从 cron 运行时,x509 行修复了该问题。
【解决方案4】:

尝试运行这个:

mozroots --import --ask-remove

在您的系统中(如果在 Windows 上,则仅在 bash 或 Mono 命令提示符中)。然后再次运行代码。

编辑:

我忘了你也应该跑

certmgr -ssl smtps://smtp.gmail.com:465

(并对问题回答是)。这适用于我在 Mono 2.10.8、Linux 上(以您的示例为例)。

【讨论】:

  • 这会在我的“信任库”中添加 140 个“新根证书”,但并不能解决问题,但感谢您的回答。
  • 很抱歉回复晚了。这成功了!非常感谢。我只是要确定。如果有人运行我的软件,他们是否必须运行这些命令才能使用该软件?这并不理想。对于一个学校项目来说,这并不重要,但我想知道,以便我可以把它放在我的关系中:-)
  • @SimonBS:他们将拥有或不拥有 - 这具体取决于所使用的操作系统/发行版及其政策。一切都很好地描述(有原因:))在这里:mono-project.com/FAQ:_Security
  • 谢谢。我会看看那个。非常感谢您的帮助!
  • 感谢这两个小花絮……在 OSX 和 Ubuntu 上非常有用且准确。最好的...
【解决方案5】:

在工作了 6 个月后,我于 2013 年 5 月开始使用 GMail 获取此信息。 Mono 项目的Using Trusted Roots Respectfully 文档提供了解决方法的指导。我选择了选项 #1:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

让我的服务在没有警告的情况下停止工作的电子邮件太具有破坏性了。

2016 年 8 月 26 日更新:用户 Chico 建议以下完整实现 ServerCertificateValidationCallback 回调。我没有测试过。

ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;

bool MyRemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
    bool isOk = true;
    // If there are errors in the certificate chain, look at each error to determine the cause.
    if (sslPolicyErrors != SslPolicyErrors.None) {
        for (int i=0; i<chain.ChainStatus.Length; i++) {
            if (chain.ChainStatus [i].Status != X509ChainStatusFlags.RevocationStatusUnknown) {
                chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
                chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
                chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan (0, 1, 0);
                chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
                bool chainIsValid = chain.Build ((X509Certificate2)certificate);
                if (!chainIsValid) {
                    isOk = false;
                }
            }
        }
    }
    return isOk;

}

【讨论】:

  • 虽然这确实可以解决服务问题并提供一种不会中断服务的方法,但它也通过说我不关心证书而消除了安全性的所有方面。因此,MITM 攻击可以很容易地监听您的通信,而无需在回调中进行额外检查以验证“不受信任”的证书绝对是您想要信任的证书。
【解决方案6】:

您需要在您的 gmail 帐户中启用两步验证并创建一个应用密码 (https://support.google.com/accounts/answer/185833?hl=en)。将密码替换为新的应用密码后,它应该可以工作。

Credentials = new System.Net.NetworkCredential("your email address", "your app password");

【讨论】:

  • 对我来说,这是实际的解决方案,因为 Google 帐户,特别是不允许应用程序代表用户登录以发送电子邮件。谢谢。
猜你喜欢
  • 2016-09-21
  • 2020-12-29
  • 2023-03-26
  • 2015-10-31
  • 1970-01-01
  • 2014-12-12
  • 1970-01-01
  • 2019-05-13
  • 1970-01-01
相关资源
最近更新 更多