c# - "The remote certificate is invalid according to the validation procedure." using Gmail SMTP server


Translate

I'm getting this error:

The remote certificate is invalid according to the validation procedure.

whenever I try to send e-mail using Gmail's SMTP server in my C# code. Can someone point me to the right direction for a solution to this problem?

The following is the stack trace...

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)

All Answers
  • Translate

    Warning: Do not use this in production code!

    As a workaround, you can switch off certificate validation. Only ever do this to obtain confirmation that the error is being throw because of a bad certificate.

    Call this method before you call 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://stackoverflow.com/a/14907718/740639
            ServicePointManager.ServerCertificateValidationCallback =
                delegate (
                    object s,
                    X509Certificate certificate,
                    X509Chain chain,
                    SslPolicyErrors sslPolicyErrors
                ) {
                    return true;
                };
        }
    

  • Translate

    The link here solved my problem.

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

    I went to url of the web service (on the server that had the issue), clicked on the little security icon in IE, which brought up the certificate. I then clicked on the Details tab, clicked the Copy To File button, which allowed me to export the certifcate as a .cer file. Once I had the certificate locally, I was able to import it into the certificate store on the server using the below instructions.

    Start a new MMC. File --> Add/Remove Snap-In... Click Add... Choose Certificates and click Add. Check the "Computer Account" radio button. Click Next.

    Choose the client computer in the next screen. Click Finish. Click Close. Click OK. NOW install the certificate into the Trusted Root Certification Authorities certificate store. This will allow all users to trust the certificate.


  • Translate

    You can improve the code by asking the user when the certificate is not valid whether he wants to continue or not. Do you want to continue? As below:

    ServicePointManager.ServerCertificateValidationCallback = 
        new RemoteCertificateValidationCallback(ValidateServerCertificate);
    

    And add a method like this:

    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;
        }
    }
    

  • Translate

    A little late to the party, but if you are looking for a solution like Yury's the following code will help you identify if the issue is related to a self-sign certificate and, if so ignore the self-sign error. You could obviously check for other SSL errors if you so desired.

    The code we use (courtesy of Microsoft - http://msdn.microsoft.com/en-us/library/office/dd633677(v=exchg.80).aspx) is as follows:

      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;
      }
    }
    

  • Translate

    I had the exact same problem and figured out that by default the Mail Shield from Avast antivirus had the "Scan SSL connection" activated. Make sure to turn that off.

    From my knowledge, Avast will "open" the mail, scan it for any viruses and then sign it using it's own certificate so the mail won't be signed by the gmail's certificate anymore which produces that error.

    Solution 1:

    • Turn off the SSL scans from your antivirus (or the entire mail shield).

    Solution 2 (Should be the best security speaking):

    • Get somehow the certificate used by the antivirus (Avast has an option to export it)
    • Import it in your imap/pop/smtp client before connecting to gmail server.

  • Translate

    Are you sure you are using correct SMTP server address?

    Both smtp.google.com and smtp.gmail.com work, but SSL certificate is issued to the second one.


  • Translate

    Get the same error while sending from outlook because of ssl. Tried setting EnableSSL = false resolved the issue.

    example:

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

  • Translate

    I had the same error when i tried to send email using SmtpClient via proxy server (Usergate).

    Verifies the certificate contained the address of the server, which is not equal to the address of the proxy server, hence the error. My solution: when an error occurs while checking the certificate, receive the certificate, export it and check.

    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);
        }
    

    Full code of my email sender class:

    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);
    }
    

    }


  • Translate

    My issue was on Windows 2003 Server, when calling AuthenticateAsClient. The solutions above (e.g. circumventing ServicePointManager.ServerCertificateValidationCallback) did not work.

    Turns out this is a bug in Windows 2003, and there is a hotfix:

    "Applications that use the Cryptography API cannot validate an X.509 certificate in Windows Server 2003"

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

    Installing this hotfix resolved my issue.


  • Translate

    Your website folder needs network service security. Especially the web.config. It uses this account to access your registry for the certificates. This will stop the need to add a hack to your code.


  • Translate

    Check your computer's Date and Time. If it is wrong, update it to the current time or set it automatically to get the time from the Internet.

    Because certificates are tied to a fixed time period, if your clock is wrong, you are likely to get errors like this. In that scenario, by fixing the time, the problem will be fixed.


  • Translate

    I know I am pretty late in this game, but I haven't seen an answer here pointing to the system.diagnostics logs for the TLS Stream.

    Before you do any changes to your code, make sure you understand what the problem is about. The AuthenticationException is one of that very generic exception which does not tell much. To learn what's going under the hood edit the app.config file for your application (or create a new one) and make sure you have System.Net trace source enabled in the system.diagnostics section, for example:

    <?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>
    

    Rerun your application and check the c:\network.log file. You should see there detailed information about your TLS (SSL) connection, for example:

    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..
    

    Knowing what causes the problem, you should be able to resolve it or at least narrow your Google searches.


  • Translate

    My issue was not that I was referencing the server by the IP address instead of the URL. I had purchased a signed certificate from a CA for use inside a private network. The URL specified on the certificate does matter when referencing the server. Once I referenced the server by the URL in the certificate everything started to work.


  • Translate

    For those encountering this same error when connecting to a local site with a self-signed certificate, the following blog post helped me out.

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


  • Translate

    In our case problem was caused by IIS server certificate. The certificate's subject was set to DNS name and users were trying to access web site by IP adress, so .NET certification validation failed. Problem disappeared when users started to use DNS name.

    So you have to change your Provider URL to https://CertificateSubject/xxx/xxx.application


  • Translate

    There is an MSDN blog article on investigating this type of issues:

    Troubleshooting ASP.NET – The remote certificate is invalid according to the validation procedure:
    http://blogs.msdn.com/b/jpsanders/archive/2009/09/16/troubleshooting-asp-net-the-remote-certificate-is-invalid-according-to-the-validation-procedure.aspx


  • Translate

    Adding this line worked for me. This in fact trusts all the certificates as mentioned here. However, this can be used primarily for troubleshooting. If this works for you, then it means that the certificate of the remote server is not added as a trusted certificate in your machine.

    System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(RemoteServerCertificateValidationCallback);
    

    Full code is

    private void sendAMail(String toAddress, String messageBody)
            {
                String msg = "Sending mail to : " + toAddress;
    
                MailMessage mail = new MailMessage();
                mail.To.Add(toAddress);
                mail.From = new MailAddress("from@mydomain.com");
                mail.Subject = "Subject: Test Mail";
                mail.Body = messageBody;
                mail.IsBodyHtml = true;            
    
                //Added this line here
                System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(RemoteServerCertificateValidationCallback);
                SmtpClient smtp = new SmtpClient();
    
                smtp.Host = "myhostname.com";            
                smtp.Credentials = new System.Net.NetworkCredential("sender@sample.com", "");
                smtp.EnableSsl = true;
                smtp.Port = 587;            
                smtp.Send(mail);            
            }
    
    
    private bool RemoteServerCertificateValidationCallback(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
    {
        //Console.WriteLine(certificate);
        return true;
    }
    

  • Translate

    It solved my issue

    smtpClient.Credentials = new NetworkCredential(sendMail.UserName, sendMail.Password);
    smtpClient.EnableSsl = false;//sendMail.EnableSSL;
    

    // With Reference to // Problem comes only Use above line to set false SSl to solve error when username and password is entered in SMTP settings.


  • Translate

    here is the solution that I decided to use.

            ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
            {
                string name = certificate.Subject;
    
                DateTime expirationDate = DateTime.Parse(certificate.GetExpirationDateString());
    
                if (sslPolicyErrors == SslPolicyErrors.None || (sslPolicyErrors == SslPolicyErrors.RemoteCertificateNameMismatch && name.EndsWith(".acceptabledomain.com") && expirationDate > DateTime.Now))
                {
                    return true;
                }
                return false;
            };