使用C# SmtpClient发送邮件:System.Net.Mail认证失败问题
Alright, let's break down why you're hitting that "Mailbox name not allowed" error with System.Net.Mail.SmtpClient, and how to get it to behave like MailKit's implementation.
The Root Cause
The core issue is different authentication timing and mechanism negotiation logic between the two libraries:
- MailKit proactively sends an
EHLOcommand right after connecting to the SMTP server, fetches all supported authentication mechanisms, and tries them in priority order before sending any email commands. System.Net.Mail.SmtpClientuses a "lazy" authentication approach: it only attempts to authenticate when the server explicitly demands it (usually after you try to send theMAIL FROMcommand). On your server, this sequence triggers the error because it expects authentication first, before any mailbox-related commands.
Solution 1: Manually Implement SMTP Flow (Simulate MailKit's Behavior)
Since System.Net.Mail.SmtpClient doesn't expose an API to force early authentication, you can bypass it entirely and handle the SMTP conversation directly with TcpClient. This lets you replicate MailKit's proactive authentication logic:
using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; public static void SendEmailWithProactiveAuth() { // Configure your SMTP details string smtpServer = "mail.domain.com"; int port = 25; string username = "sender"; string password = "MyPassword"; string fromAddress = "sender@domain.com"; string toAddress = "recipient@domain.com"; string subject = "My subject"; string body = "my body"; using (var tcpClient = new TcpClient(smtpServer, port)) using (var stream = tcpClient.GetStream()) using (var reader = new StreamReader(stream, Encoding.ASCII)) using (var writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = true }) { // Read server welcome message Console.WriteLine(reader.ReadLine()); // Send EHLO to get server capabilities writer.WriteLine($"EHLO {Dns.GetHostName()}"); string response; bool authFound = false; while ((response = reader.ReadLine()) != null) { Console.WriteLine(response); if (response.StartsWith("250 ")) break; // End of EHLO responses // Look for supported authentication mechanisms if (response.StartsWith("250-AUTH")) { var authMechanisms = response.Substring(9).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); // Try mechanisms in priority order (match MailKit's ranking) foreach (var mechanism in authMechanisms) { if (mechanism.Equals("PLAIN", StringComparison.OrdinalIgnoreCase)) { // PLAIN auth: encode "\0username\0password" as Base64 var plainAuth = Convert.ToBase64String(Encoding.ASCII.GetBytes($"\0{username}\0{password}")); writer.WriteLine($"AUTH PLAIN {plainAuth}"); response = reader.ReadLine(); if (response.StartsWith("235")) { Console.WriteLine("PLAIN authentication succeeded"); authFound = true; goto Authenticated; } } else if (mechanism.Equals("LOGIN", StringComparison.OrdinalIgnoreCase)) { // LOGIN auth: send username then password as Base64 writer.WriteLine("AUTH LOGIN"); response = reader.ReadLine(); // Should get 334 VXNlcm5hbWU6 (username prompt) if (response.StartsWith("334")) { writer.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(username))); response = reader.ReadLine(); // Should get 334 UGFzc3dvcmQ6 (password prompt) if (response.StartsWith("334")) { writer.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(password))); response = reader.ReadLine(); if (response.StartsWith("235")) { Console.WriteLine("LOGIN authentication succeeded"); authFound = true; goto Authenticated; } } } } } throw new InvalidOperationException("Server doesn't support any compatible authentication mechanisms"); } } Authenticated: if (!authFound) throw new InvalidOperationException("Failed to authenticate with server"); // Send email commands writer.WriteLine($"MAIL FROM:<{fromAddress}>"); Console.WriteLine(reader.ReadLine()); writer.WriteLine($"RCPT TO:<{toAddress}>"); Console.WriteLine(reader.ReadLine()); writer.WriteLine("DATA"); Console.WriteLine(reader.ReadLine()); // Write email headers and body writer.WriteLine($"Subject: {subject}"); writer.WriteLine($"From: {fromAddress}"); writer.WriteLine($"To: {toAddress}"); writer.WriteLine(); writer.WriteLine(body); writer.WriteLine("."); // End of message Console.WriteLine(reader.ReadLine()); // Cleanup writer.WriteLine("QUIT"); Console.WriteLine(reader.ReadLine()); } }
Solution 2: Force Authentication with Reflection (Not Recommended)
If you absolutely need to stick with System.Net.Mail.SmtpClient, you can use reflection to call its internal Authenticate method, forcing it to authenticate before sending the email. This is a hacky approach and may break with .NET updates:
using System.Net.Mail; using System.Reflection; public static void SendEmailWithForcedAuth() { var smtp = new SmtpClient("mail.domain.com", 25) { EnableSsl = false, UseDefaultCredentials = false, Credentials = new NetworkCredential("sender", "MyPassword") }; // Connect to the server smtp.Connect(); // Use reflection to call the internal Authenticate method var authenticateMethod = typeof(SmtpClient).GetMethod( "Authenticate", BindingFlags.Instance | BindingFlags.NonPublic ); authenticateMethod?.Invoke(smtp, null); // Prepare and send the email var mailMessage = new MailMessage { From = new MailAddress("sender@domain.com"), IsBodyHtml = false, Body = "my body", Subject = "My subject" }; mailMessage.To.Add(new MailAddress("recipient@domain.com")); smtp.Send(mailMessage); smtp.Disconnect(true); }
A Better Long-Term Option
Keep in mind that System.Net.Mail.SmtpClient is marked as obsolete in .NET Framework and isn't actively maintained. MailKit is the official recommended alternative for email handling—it's more robust, supports modern SMTP standards, and already does exactly what you need without workarounds. Unless you have strict constraints forcing you to use the old library, sticking with MailKit is the smarter choice.
内容的提问来源于stack exchange,提问作者Jester

