Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The remote certificate is invalid according to the validation procedure. #307

Closed
yuribb opened this issue Mar 3, 2016 · 30 comments
Closed
Labels
question A question about how to do something

Comments

@yuribb
Copy link

yuribb commented Mar 3, 2016

Hello. After updating MailKit to version 1.2.20 When I try to connect by using MailKit.ImapClient to gmail with default settings(Host = "imap.gmail.com", Port = 993, UseSSL = true), then the error appears "System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure." at
MailKit.Net.Imap.ImapClient.Connect(String host, Int32 port, SecureSocketOptions options, CancellationToken cancellationToken). If I set SecureSocketOptions.None to connection settings, MailKit.ImapClient return Unexpectedly disconnected. What's wrong?

@jstedfast
Copy link
Owner

In MailKit prior to 1.2.20, I made the default certificate validation callback always return "valid", mostly to avoid this type of question for the multitude of mail servers that have self-signed certificates and I figured that anyone serious about security would override the ServerCertificateValidationCallback property on ImapClient. I've since had second thoughts about that because it opens people up to MITM attacks that they may not even be aware is a potential threat.

I don't think GMail's certificate is self-signed, but I have also noticed that on Mac and Linux, this now fails, but on Windows it seems to continue to work.

Are you on Mac or Linux?

Either way, the solution is to do something like this:

client.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => {
    if (sslPolicyErrors == SslPolicyErrors.None)
        return true;

    // if there are errors in the certificate chain, look at each error to determine the cause.
    if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0) {
        if (chain != null && chain.ChainStatus != null) {
            foreach (var status in chain.ChainStatus) {
                if ((certificate.Subject == certificate.Issuer) && (status.Status == X509ChainStatusFlags.UntrustedRoot)) {
                    // self-signed certificates with an untrusted root are valid. 
                    continue;
                } else if (status.Status != 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;
    }

    return false;
};

@jstedfast
Copy link
Owner

I've just added this logic as a public static method called MailService.DefaultServerCertificateValidationCallback()

I've also made it the default callback for all SMTP, POP3 and IMAP client instances.

Hopefully this will not bite me in the butt :(

@yuribb
Copy link
Author

yuribb commented Mar 3, 2016

Thank you for information. It really helped for me.

@jstedfast
Copy link
Owner

One way to make this more secure is that you could get the Thumbprint of the X509Certificate you know is Google's GMail server and hardcode that into the callback so that you can compare the Thumbprint values.

if (certificate is X509Certificate2 && ((X509Certificate2) certificate).Thumbprint == "71DFBE124D89ED9218F539A82D4127FBB4BC9997")
    return true;

@atiqi36
Copy link

atiqi36 commented May 13, 2016

Hi mate,
I am getting this same error and I am on windows. No solution is working for me, I keep getting this same error
the remote certificate is invalid according to the validation procedure.
Any suggestions? Is it possible that there is something wrong to the server I am trying to connect? This is the server I am trying to connect actually mail.cosgroup.com at 143

@jstedfast
Copy link
Owner

@jvilhuber
Copy link

I missed this change when you made it on March 3rd (I use my own validation function, so it doesn't affect me), and like you I tend to waffle on security checks like this. The more paranoid in me would prefer the more rigorous check, failing on self-signed certs (partly to raise awareness of the evilness of using self-signed certs for a public server like this, because MitM attacks), and if someone wants a less secure setting, they should override the more secure check.

That being said, I answer a lot of certificate questions ("Why doesn't your app connect to my self-signed-certificate-server?!"), and it gets tedious after a while, so it's hard to argue with your change.

Note that there's now a free way of getting actual (non-self-signed) certs: https://letsencrypt.org

@atiqi36
Copy link

atiqi36 commented May 16, 2016

Hey Jeffrey,
Thanks for the suggestion, I was able to override and connect imap which is downloading the emails now but not sending them.
No matter what I do, just keep getting this error

The SMTP server does not support authentication.
at MailKit.Net.Smtp.SmtpClient.Authenticate(Encoding encoding, ICredentials credentials, CancellationToken cancellationToken)
   at MailKit.MailService.Authenticate(String userName, String password, CancellationToken cancellationToken)

I will be very glad if you could help?

@atiqi36
Copy link

atiqi36 commented May 16, 2016

Further to this, if I check Capabilities, I get this error while sending emails

Requested action not taken: mailbox unavailable
   at MailKit.Net.Smtp.SmtpClient.ProcessRcptToResponse(SmtpResponse response, MailboxAddress mailbox)
   at MailKit.Net.Smtp.SmtpClient.RcptTo(MimeMessage message, MailboxAddress mailbox, CancellationToken cancellationToken)
   at MailKit.Net.Smtp.SmtpClient.Send(FormatOptions options, MimeMessage message, MailboxAddress sender, IList`1 recipients, CancellationToken cancellationToken, ITransferProgress progress)
   at MailKit.Net.Smtp.SmtpClient.Send(FormatOptions options, MimeMessage message, CancellationToken cancellationToken, ITransferProgress progress)
   at MailKit.MailTransport.Send(MimeMessage message, CancellationToken cancellationToken, ITransferProgress progress)

And on capability check, server has SmtpCapabilities.EightBitMime and size restriction of 20971520

@jstedfast
Copy link
Owner

The SMTP server does not support authentication.
at MailKit.Net.Smtp.SmtpClient.Authenticate(Encoding encoding, ICredentials credentials, CancellationToken cancellationToken)
   at MailKit.MailService.Authenticate(String userName, String password, CancellationToken cancellationToken)

This error means that your SMTP server does not support authentication, so... just don't authenticate (i.e. skip calling the client.Authenticate(...) method).

Requested action not taken: mailbox unavailable
   at MailKit.Net.Smtp.SmtpClient.ProcessRcptToResponse(SmtpResponse response, MailboxAddress mailbox)
   at MailKit.Net.Smtp.SmtpClient.RcptTo(MimeMessage message, MailboxAddress mailbox, CancellationToken cancellationToken)
   at MailKit.Net.Smtp.SmtpClient.Send(FormatOptions options, MimeMessage message, MailboxAddress sender, IList`1 recipients, CancellationToken cancellationToken, ITransferProgress progress)
   at MailKit.Net.Smtp.SmtpClient.Send(FormatOptions options, MimeMessage message, CancellationToken cancellationToken, ITransferProgress progress)
   at MailKit.MailTransport.Send(MimeMessage message, CancellationToken cancellationToken, ITransferProgress progress)

This error means that one of the email addresses (aka mailboxes) that you are sending the message to does not exist.

@jstedfast jstedfast added the question A question about how to do something label May 17, 2016
@atiqi36
Copy link

atiqi36 commented May 17, 2016

Hey Jeff, if I skip authenticate it throws this mailbox error and its just a normal valid hotmail email. Stuck

@jstedfast
Copy link
Owner

The error is from the server, so I'd be fairly confident that the error is accurate. Maybe the mailbox is full?

@atiqi36
Copy link

atiqi36 commented May 17, 2016

No, I have tried sending to different emails, same error, and I have tried a different email from the same server but same result.
I don't understand, the same email works with some systems, do you think I should try sending by .net SMTP class? The client is saying he uses owa the online outlook system and the email account works fine with that.

@jstedfast
Copy link
Owner

Sure, give that a try. You could also try getting a log (see the FAQ) to see what capabilities the server supports.

@jvilhuber
Copy link

jvilhuber commented May 17, 2016

normal valid hotmail email: Note that hotmail also support exchange, and most clients are more than likely using exchange and not imap.

That being said:

SMTP:

$ telnet smtp-mail.outlook.com 587
Trying 65.55.163.152...
Connected to smtp.glbdns2.microsoft.com.
Escape character is '^]'.
220 BLU437-SMTP99.smtp.hotmail.com Microsoft ESMTP MAIL Service, Version: 8.0.9200.16384 ready at  Tue, 17 May 2016 15:30:32 -0700
EHLO hotmail.com
250-BLU437-SMTP99.smtp.hotmail.com Hello [XX.XXX.XXX.XX]
250-TURN
250-SIZE 41943040
250-ETRN
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-8bitmime
250-BINARYMIME
250-CHUNKING
250-VRFY
250-TLS
250-STARTTLS
250 OK

IMAP:

$ openssl s_client -connect imap-mail.outlook.com:993 -crlf -quiet
depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign Organization Validation CA - SHA256 - G2
verify error:num=20:unable to get local issuer certificate
* OK Outlook.com IMAP4rev1 server version 17.4.0.0 ready (BAY451-IMAP42)
A0001 CAPABILITY
* CAPABILITY IMAP4rev1 CHILDREN ID NAMESPACE UIDPLUS UNSELECT AUTH=PLAIN AUTH=XOAUTH2 SASL-IR
A0001 OK CAPABILITY completed

@jstedfast
Copy link
Owner

@atiqi36 make sure to connect using SecureSocketOptions.StartTls, this might then expose what authentication options Hotmail supports for SMTP. Once you do that, you'll probably need to add back the call to Authenticate().

@atiqi36
Copy link

atiqi36 commented May 19, 2016

Guys, I think I may have been unclear. It's not the hotmail server I am using. The smtp server I am using to send email is mail.cosgroup.com and the email is of the same domain like email@cosgroup.com
I can't send email at all, I have tried all the configuration, Auto, SSL, TLS etc but nothing works. Tried with authentication or without but still don't work.
And on capability check, server has SmtpCapabilities.EightBitMime and size restriction of 20971520
I can connect to imap to download emails so you can rule out the firewall.
What could be wrong? What ever I do I get this bloody error "The SMTP server does not support authentication."

@jstedfast
Copy link
Owner

jstedfast commented May 19, 2016

The server might not support authentication until you have toggled into TLS mode.

So what you need to do is connect on port 25 or 587 (which ever one you are using) and pass SecureSocketOptions.StartTls:

using (var client = new SmtpClient (new ProtocolLogger ("smtp.log"))) {
    client.Connect ("mail.cosgroup.com", 587, SecureSocketOptions.StartTls);
    client.Authenticate ("username", "password");
    client.Send (message);
    client.Disconnect (true);
}

@atiqi36
Copy link

atiqi36 commented May 19, 2016

Now getting this error
"The SMTP server does not support the STARTTLS extension." :(

@jstedfast
Copy link
Owner

Have you tried contacting your network administrator and asking him/her what settings to use for connecting via SMTP?

@cklaus33
Copy link

Dear Jeffrey,

I try to use MailKit Imap Client with a self-signed certificate in vb.net. As I don't have any experience with certificates I have some problems to get this running.
I get the Error "System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure." but I could not implement the "ServerCertificateValidationCallback". How do I have to implement this? do you have an example? Do I need to download the certificate and use these information as parameter for the MailService.DefaultServerCertificateValidationCallback() method?
Thank you for any answer.

Kind regards

@jstedfast
Copy link
Owner

Here's an example in C# (I don't know VB):

bool CustomCertificateValidationCallback (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    return certificate.Thumbprint == "<hardcoded thumbprint of your imap server goes here>";
}
using (var client = new ImapClient ()) {
    client.ServerCertificateValidationCallback = CustomCertificateValidationCallback;
    client.Connect ("imap.server.com", 993, true);
    // ...
}

If you don't know what the thumbprint is, you can use this custom callback to print it out for you and then just copy & paste it into the custom callback I pasted above:

bool CustomCertificateValidationCallback (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    Console.WriteLine ("Thumbprint = {0}", certificate.Thumbprint);
    return false;
}

@cklaus33
Copy link

Dear Jeffrey,

thank you very much for your quick response. Unfortunately I have some more questions:

  1. in the first code you wrote, you defined a function with a boolean return value. When you call the function, you do not transfer any values? Do I have to load the certificate from a file and use this as "certificate"? And what will be "sender"?
  2. When I try to give "client.ServerCertificationValidationCallback" a boolean value there comes an error: type boolean cannot be converted to "System.Net.Security.RemoteCertificateValidationCallback"

Kind regards

@jstedfast
Copy link
Owner

client.ServerCertificateValidationCallback is a function delegate, it is not a boolean property. You need to assign a method to it.

Then, when the SSL connection is made, the client will call that method with the certificate and such.

@jvilhuber
Copy link

I can only urge folks to NOT use self-signed certificates. There's really no need to make the NSA's (and CIA's and FBI's) job easier.

You can get FREE certificates from https://letsencrypt.org. For a linux server, it's almost trivial to set up, and you get a real (as in 'signed by a trusted CA') certificate.

Certificate pinning is a decent thing to do (which is what putting the thumb/fingerprint into the code is), but isn't entirely flexible, since you'd have to recompile (and redistribute) your code/app every time you need to change the certificate. It would be better to create your own CA certificate, save the CA-certificate's fingerprint in the code, and the CAREFULLY issue your own certificates from that CA; that would be more flexible, but a bit of work, that letsencrypt.org basically does for you.

@jstedfast
Copy link
Owner

100% agreed. 👍

@jabas06
Copy link

jabas06 commented Aug 6, 2016

I'm getting the same error (The remote certificate is invalid according to the validation procedure), but only on a Mac. On windows works fine.

This is the line that is throwing the exception on Mac

client.Connect(options.SmtpHost, 465, true);

@jstedfast
Copy link
Owner

You need to assign your own callback to client.ServerCertificateValidationCallback to validate the certificate.

Most likely the issue you are running into is that your Windows certificate store has a copy of the server's certificate (or the Root CA that signed it) while your Mac does not.

@gustavodenis
Copy link

You need ignore this validation if you had problemas with certificate.

client.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => {
return true;
};

@Coruscate5
Copy link

@gustavodenis - Well, that's one option, though the errors are generally legitimate. The whole point of callback is to validate the SSL cert being presented by the SMTP server.

Returning true will ignore the validation, yes, but if it's a self-signed cert you can resolve this without removing validation by either adding the remote cert/chain to the MailKit server's certstore (if intranet, this can be more easily resolved using a corp-wide internal CA), or getting a real cert on the SMTP server.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question A question about how to do something
Projects
None yet
Development

No branches or pull requests

8 participants