0

I'm attempting to connect to an IMAP server using the following code on a Windows Server 2019 machine:

using (var client = new ImapClient(new ProtocolLogger("protocol.log")))
 {
        var address = EnvReader.GetStringValue("EMAIL_ADDRESS");
        var password = EnvReader.GetStringValue("EMAIL_PASSWORD");
        var creds = new NetworkCredential(address, password);

        client.CheckCertificateRevocation = false;
        client.ServerCertificateValidationCallback = (s, c, h, e) =>
        {
          Console.WriteLine("ALL UP IN THIS CALLBACK" + e.ToString());
          return true;
        };
        client.Connect("outlook.office365.com", 993, SecureSocketOptions.SslOnConnect);
        client.Authenticate(address, password);
 }

On my Mac, this code runs perfectly fine, I can connect and subsequently authenticate just fine.

On the Windows machine I receive the following exception:

MailKit.Security.SslHandshakeException: An error occurred while attempting to establish an SSL or TLS connection.
This usually means that the SSL certificate presented by the server is not trusted by the system for one or more of
the following reasons:
1. The server is using a self-signed certificate which cannot be verified.
2. The local system is missing a Root or Intermediate certificate needed to verify the server's certificate.
3. A Certificate Authority CRL server for one or more of the certificates in the chain is temporarily unavailable.
4. The certificate presented by the server is expired or invalid.
5. The set of SSL/TLS protocols supported by the client and server do not match.
6. You are trying to connect to a port which does not support SSL/TLS.

See https://github.com/jstedfast/MailKit/blob/master/FAQ.md#SslHandshakeException for possible solutions

Based on the info in the linked FAQ, I added the ServerCertificateValidationCallback, however the callback is never hit (The previously mentioned exception is still thrown, the relevant console logging never occurs, and a breakpoint inside the callback is never hit while debugging).

From my reading, the ServerCertificateValidationCallback should handle cases #1-4 that the exception message mentions. The fact that I can connect on the specified port on my Mac would seem to rule out case #6 (I also tried port 143 + SecureSocketOptions.StartTls). That leaves case #5, however, I can't find any information suggesting that Windows Server 2019 can't handle SSL/TSL protocols.

Any ideas for a) dealing with this exception and/or b) figuring out why the ServerCertificateValidationCallback is not firing would be greatly appreciated.

Edit: My project is referencing .NET 5.0

D. Reagan
  • 841
  • 1
  • 8
  • 24
  • REad following : https://learn.microsoft.com/en-us/answers/questions/182090/is-imap-being-removed-regardless-of-2021-extension.html?force_isolation=true – jdweng May 26 '21 at 16:20
  • Which .NET/Core framework are you using? Is your callback printing anything out? Based on the title, it sounds like it's not getting hit, but if that's true... why do you suppose that cases #1-#4 are already covered? – jstedfast May 26 '21 at 17:34
  • Also, what #5 means is that the supported SSL/TLS versions of the client and server do not match. For example, the latest version of MailKit only enables TLS v1.2 but maybe Office365 is still on TLS v1.0? See the `client.SslProtocols` property to enable more. – jstedfast May 26 '21 at 17:35
  • jdweng, thanks for the tip, but as I said above, this code works fine on Mac. I am able to auth fine against the IMAP server on Mac. When the same code is run on Windows Server 2019, I can not connect (doesn't even get to the auth step). – D. Reagan May 26 '21 at 18:10
  • jstedfast. I am using .NET 5.0, according to my .csproj file. Seems that I am probably wrong, based on your question, but from my surface-level perusal, it looks like 1-4 all have to do with validating the certificate. If I understand correctly, my ServerCeftificateValidationCallback should force validation of the cert. – D. Reagan May 26 '21 at 18:11
  • jsdedfast. As I mention above, this code runs fine on my Mac, so I assume that it's not a question of which version of TLS Office365 is on. – D. Reagan May 26 '21 at 18:13
  • jstedfast, no, my callback is not printing anything out, nor is it hit when i set a breakpoint in it in the debugger. – D. Reagan May 26 '21 at 18:14

1 Answers1

1

Let's go through each of the possibilities:

  1. The server is using a self-signed certificate which cannot be verified.

outlook.office365.com would not be using a self-signed certificate, so that wouldn't be an issue in this case.

  1. The local system is missing a Root or Intermediate certificate needed to verify the server's certificate.

This one is very possible, but the ServerCertificateValidationCallback override should be overriding this failure. However, it's not getting hit... so it's not actually bypassing this potential error.

  1. A Certificate Authority CRL server for one or more of the certificates in the chain is temporarily unavailable.

This would be negated by client.CheckCertificateRevocation = false;

  1. The certificate presented by the server is expired or invalid.

This is not the case because the certificate does not expire until 1/21/2022.

  1. The set of SSL/TLS protocols supported by the client and server do not match.

The server supports at least TLSv1.2 which is a default TLS protocol version supported by MailKit in all target framework versions (.NET 4.5 -> 5.0 + netstandard2.x's).

  1. You are trying to connect to a port which does not support SSL/TLS.

Port 993 is the correct port and SslOnConnect is the correct option, so this is not the issue.

Assuming there isn't a bug in MailKit's SslStream.AuthenticateAsClientAsync() call that passes in the validation callback method (.NET 5.0 is different than other versions), what is the InnerException? Maybe that will provide some insight.

jstedfast
  • 35,744
  • 5
  • 97
  • 110
  • The InnerException is a System.IO.Exception: An existing connection was forcibly closed by the remote host. This InnerException also has an InnerException: System.Net.Sockets.SocektException: An existing connection was forcibly closed by the remote host – D. Reagan May 26 '21 at 18:54
  • I'm currently in the process of compiling directly against Mailkit-master, so I can set some breakpoints inside Mailkit itself. – D. Reagan May 26 '21 at 18:56
  • That would be awesome. – jstedfast May 26 '21 at 18:56
  • I also tried .NET Framework 4.8 and ran into the same exception, for what it's worth. – D. Reagan May 26 '21 at 18:57
  • FWIW, I can't seem to reproduce by running this test app on net48, netcoreapp3.1 or net50: https://gist.github.com/jstedfast/7cd36a51cee740ed84b18435106eaea5 – jstedfast May 26 '21 at 19:15
  • Ok, well that was a bit of a pain without git access on the windows machine (long story). It's crashing in IMapClient.cs on line 1325 (call to SslStream.AuthenticateAsClient). More specifically, it crashes on line 156 of NetworkStream.cs (Read method). I can't step inside SslStream.AuthenticateAsClient, but it's obviously crashing before it can call the ServerCertificateValidationCallback. I think the SslHandshakeException is a bit of a red herring, since it can't seem to read from the Socket? – D. Reagan May 26 '21 at 19:50
  • That test (gist.github.com/jstedfast/7cd36a51cee740ed84b18435106eaea5 ) runs fine on mac & on Windows 10, but dies at the very first connection attempt (imap://imap.gmail.com:993) on Windows Server 2019. Same SslHandshakeException. – D. Reagan May 26 '21 at 20:17
  • I wonder what is different on Windows Server 2019? – jstedfast May 26 '21 at 20:25
  • 1
    Yes, based on what you discovered, it seems that SslHandshakeException is a red-herring. Something is causing "System.Net.Sockets.SocektException: An existing connection was forcibly closed by the remote host"... could it be that your Windows Server 2019 machine/vm is blocked by some sort of firewall rule or by some sort of blacklist rule? THat error sounds like the server is rejecting you for some reason – jstedfast May 26 '21 at 21:34
  • https://stackoverflow.com/questions/2582036/an-existing-connection-was-forcibly-closed-by-the-remote-host – jstedfast May 26 '21 at 21:35
  • This makes me think that there might be a bug in SslStream on WS2019 because presumably it must be sending some sort of malformed packet (likely an SSL-handshake packet) that is causing this. – jstedfast May 26 '21 at 21:37
  • 1
    wow, ok, It turns out it was a 'nextgen' firewall (a Palo Alto PA850). I had asked the client multiple times if there were any firewalls blocking IMAP connections and they said no. Finally I talked with someone on their IT team and he was able to whitelist my app. Just a thought: might want to add a firewall as a potential cause of the SslHandshakeException. – D. Reagan May 26 '21 at 22:16