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

Make SSL handshake failures diagnosable #29518

Closed
alexkeh opened this issue May 10, 2019 · 33 comments · Fixed by dotnet/corefx#42226
Closed

Make SSL handshake failures diagnosable #29518

alexkeh opened this issue May 10, 2019 · 33 comments · Fixed by dotnet/corefx#42226
Labels
area-System.Net.Security enhancement Product code improvement that does NOT require public API changes/additions os-mac-os-x macOS aka OSX
Milestone

Comments

@alexkeh
Copy link

alexkeh commented May 10, 2019

We are testing Oracle's ADO.NET provider for .NET Core on macOS. I'm on the Oracle team myself and am looking for some direction on whether this is a .NET Core bug or something my team needs to change to support Apple SSL.

The provider hits a failure during SSL handshake. We have tested on Windows and Linux with TLS/SSL successfully with the same code.

We are using .NET Core 2.2.105, macOS 10.13.6, and TLS 1.2. The full stack trace is below.

Unhandled Exception: Oracle.ManagedDataAccess.Client.OracleException: ORA-00542: Failure during SSL handshake 
---> OracleInternal.Network.NetworkException: ORA-00542: Failure during SSL handshake 
---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. 
---> Interop+AppleCrypto+SslException: handshake failure
   --- End of inner exception stack trace ---
   at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo 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.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.Security.SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions)
   at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
   at OracleInternal.Network.TcpsTransportAdapter.Negotiate(ConnectionOption conOption)
   --- End of inner exception stack trace ---
   at OracleInternal.Network.OracleCommunication.DoConnect(String tnsDescriptor)
   at OracleInternal.ServiceObjects.OracleConnectionImpl.Connect(ConnectionString cs, Boolean bOpenEndUserSession, OracleConnection connRefForCriteria, String instanceName)
   --- End of inner exception stack trace ---
   at OracleInternal.ConnectionPool.PoolManager`3.Get(ConnectionString csWithDiffOrNewPwd, Boolean bGetForApp, OracleConnection connRefForCriteria, String affinityInstanceName, Boolean bForceMatch)
   at OracleInternal.ConnectionPool.OraclePoolManager.Get(ConnectionString csWithNewPassword, Boolean bGetForApp, OracleConnection connRefForCriteria, String affinityInstanceName, Boolean bForceMatch)
   at OracleInternal.ConnectionPool.OracleConnectionDispenser`3.Get(ConnectionString cs, PM conPM, ConnectionString pmCS, SecureString securedPassword, SecureString securedProxyPassword, OracleConnection connRefForCriteria)
   at Oracle.ManagedDataAccess.Client.OracleConnection.Open()
   at ODPC.Program.Main(String[] args) in /<directory location>/Program.cs:line 12
@stephentoub
Copy link
Member

cc: @bartonjs

@alexkeh, to help with more info, could you try with .NET Core 3 Preview 5? It might be instructive to see whether it still repros.

@alexkeh
Copy link
Author

alexkeh commented May 11, 2019

@stephentoub
We don't have an ADO.NET provider working on .NET Core 3 yet, which makes us unable to try SSL on it.

@stephentoub
Copy link
Member

@alexkeh, thanks, though that's interesting in and of itself. Your library that runs on a previous .NET Core version doesn't "just work" on 3.0? What fails?

@alexkeh
Copy link
Author

alexkeh commented May 13, 2019

@stephentoub
My team tried .NET Core SDK Version: 3.0.100-preview5-011568, which resulted in the same error:

Unhandled Exception: Oracle.ManagedDataAccess.Client.OracleException: ORA-00542: Failure during SSL handshake ---> OracleInternal.Network.NetworkException: ORA-00542: Failure during SSL handshake ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> Interop+AppleCrypto+SslException: handshake failure

I spoke too soon about not being able to run our library on the .NET Core 3.0. Since we support .NET Standard 2.1, we can run on .NET Core 3.0 after all. Nonetheless, we see the same error as when running on .NET Core 2.x.

@wfurt
Copy link
Member

wfurt commented May 13, 2019

what exact version of 2.x do you use @alexkeh ?
Some ssl fixes were back-ported to 2.x so make use you use latest version.

cc: @bartonjs

@alexkeh
Copy link
Author

alexkeh commented May 13, 2019

@wfurt
We're using .NET Core SDK 2.2.105.

@wfurt
Copy link
Member

wfurt commented May 13, 2019

I assume the server is not publicly reachable, right?
It may be handy to get packet capture. That at least should show server certificate.

@wfurt
Copy link
Member

wfurt commented May 22, 2019

We will need more information @alexkeh how to reproduce it. Preferably sample code or specific steps.
Without it, this is not actionable.

BTW how did you establish trust between client and server?

@alexkeh
Copy link
Author

alexkeh commented May 22, 2019

@wfurt
The server is not publicly reachable. I created a MS case number 119052125003241 yesterday to enabling sharing of diagnostics and code that cannot be done in the public domain.

@ScotMac
Can you answer @wfurt's questions?

@ScotMac
Copy link

ScotMac commented May 23, 2019

I believe we should be able to either produce a diag patch to get the SSL packets or do some type of packet sniffing (wireshark). Would that be sufficient?

In terms of the trust, i believe it is verisign trusted root, but i need to verify.

Though the server is not publicly reachable, @wfurt is it possible that we could get some get some type of more private communication channel open, that we could use to transfer the server connect details to you? eg, we could post one of our email addresses here, and you could initiate a conversation via that email address.

@wfurt
Copy link
Member

wfurt commented May 23, 2019

wireshark may help. At least we would see certificate and part of the handshake.

The question is if this is ADO.NET specific or not.
Can you reproduce it with just ssl streams?
You can also try to connect with HttpClient or Safari.
It will fail on L7 protocol level but at least you would see if you can complete TLS handshake.

We can certainly set up alternative communication channel if you think that is useful.

@ScotMac
Copy link

ScotMac commented May 24, 2019

The trust is explicitly setting a trustpoint for a digicert root in the cert of the server and client. Not using default CA's.

@ScotMac
Copy link

ScotMac commented May 24, 2019

We are trying the connection establishment from our Oracle provided C client, sqlplus, to verify whether the problem is associated w/ .NET or the MacOS C libraries.

@ScotMac
Copy link

ScotMac commented May 24, 2019

Looks like it is a Core issue. It doesn't happen for sqlplus (C client), and does happen on linux with a pretty similar stack.

Linux failure:

Unhandled Exception: Oracle.ManagedDataAccess.Client.OracleException: ORA-00542: Failure during SSL handshake ---> OracleInternal.Network.NetworkException: ORA-00542: Failure during SSL handshake ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> Interop+Crypto+OpenSslCryptographicException: error:2006D002:BIO routines:BIO_new_file:system lib
   at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle)
   at Internal.Cryptography.Pal.StorePal.LoadMachineStores()
   at Internal.Cryptography.Pal.StorePal.FromSystemStore(String storeName, StoreLocation storeLocation, OpenFlags openFlags)
   at System.Security.Cryptography.X509Certificates.X509Store.Open(OpenFlags flags)
   at Internal.Cryptography.Pal.OpenSslX509ChainProcessor.FindCandidates(X509Certificate2 leaf, X509Certificate2Collection extraStore, HashSet`1 downloaded, HashSet`1 systemTrusted, TimeSpan& remainingDownloadTime)
   at Internal.Cryptography.Pal.ChainPal.BuildChain(Boolean useMachineContext, ICertificatePal cert, X509Certificate2Collection extraStore, OidCollection applicationPolicy, OidCollection certificatePolicy, X509RevocationMode revocationMode, X509RevocationFlag revocationFlag, DateTime verificationTime, TimeSpan timeout)
   at System.Security.Cryptography.X509Certificates.X509Chain.Build(X509Certificate2 certificate, Boolean throwOnException)
   at System.Security.Cryptography.X509Certificates.X509Chain.Build(X509Certificate2 certificate)
   at System.Net.Http.TLSCertificateExtensions.BuildNewChain(X509Certificate2 certificate, Boolean includeClientApplicationPolicy)
   at Interop.OpenSsl.AllocateSslContext(SslProtocols protocols, SafeX509Handle certHandle, SafeEvpPKeyHandle certKeyHandle, EncryptionPolicy policy, SslAuthenticationOptions sslAuthenticationOptions)
   at System.Net.Security.SafeDeleteSslContext..ctor(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions)
   at System.Net.Security.SslStreamPal.HandshakeInternal(SafeFreeCredentials credential, SafeDeleteContext& context, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
   --- End of inner exception stack trace ---
   at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo 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.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.Security.SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions)
   at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
   at OracleInternal.Network.TcpsTransportAdapter.Negotiate(ConnectionOption conOption)
   --- End of inner exception stack trace ---
   at OracleInternal.Network.OracleCommunication.DoConnect(String tnsDescriptor)
   at OracleInternal.ServiceObjects.OracleConnectionImpl.Connect(ConnectionString cs, Boolean bOpenEndUserSession, OracleConnection connRefForCriteria, String instanceName)
   --- End of inner exception stack trace ---
   at OracleInternal.ConnectionPool.PoolManager`3.Get(ConnectionString csWithDiffOrNewPwd, Boolean bGetForApp, OracleConnection connRefForCriteria, String affinityInstanceName, Boolean bForceMatch)
   at OracleInternal.ConnectionPool.OraclePoolManager.Get(ConnectionString csWithNewPassword, Boolean bGetForApp, OracleConnection connRefForCriteria, String affinityInstanceName, Boolean bForceMatch)
   at OracleInternal.ConnectionPool.OracleConnectionDispenser`3.Get(ConnectionString cs, PM conPM, ConnectionString pmCS, SecureString securedPassword, SecureString securedProxyPassword, OracleConnection connRefForCriteria)
   at Oracle.ManagedDataAccess.Client.OracleConnection.Open()

@wfurt
Copy link
Member

wfurt commented May 24, 2019

The information about certificate trust does not have enough details. There are several recent posts similar to this where the the issue is server certificate. Windows implementation seems to be more liberal but OpenSSL correct v3 flags are required.

You can try this (on Linux) to verify that your general trust is correct:

openssl s_client -connect HOST:PORT -showcerts

post output of that command @ScotMac if it connects successfully but SslStream does not.

@ScotMac
Copy link

ScotMac commented May 25, 2019

@wfurt I believe our server requires two way auth. ie, the client will also need t authenticated. I am not that familiar w/ openssl, but it appears the s_client command take a -cert arg param. I believe the arg is a filename? Can the filename be a pkcs12 file?

@wfurt
Copy link
Member

wfurt commented May 27, 2019

no, I don't think it can take pkcs12. https://www.openssl.org/docs/manmaster/man1/s_client.html
But you can extract certificate and key from pkcs12 using openssl pkcs12
You can also look at examples here: https://www.feistyduck.com/library/openssl-cookbook/online/ch-testing-with-openssl.html

@wfurt
Copy link
Member

wfurt commented May 27, 2019

BTW I also noticed that the new stack fails on LoadMachineStores(). Since you mentioned pkcs12: did you import it via X509Certificate2.Import? As I mentioned before, you can share simple SsslStream example of your code without any private ADO.NET as well as describe all steps you did to establish trust @ScotMac

@ScotMac
Copy link

ScotMac commented May 28, 2019

Yes, i looked at exactly that manpage before i send the last message. It has no details of what the -cert takes. But i found here a good description of how to convert the pkcs12 to something the s_client will take here:

https://techdrawer.wordpress.com/2014/10/07/testing-ssl-with-your-certificate-using-s_client/

Specifically, i will try the following:

  1. The resulting pcks12 (.pfx, .p12) can be converted to PEM format
    openssl pkcs12 -in <.p12 filename> -out -nodes
  2. export the private key from the pkcs12 with the -nocerts flag
    openssl pkcs12 -in <.p12 filename> -out -nodes -nocerts
  3. test with openssl s_client
    openssl s_client -connect host:port -cert <cert.pem> -key <key.pem> [-CApath ]

@bartonjs
Copy link
Member

@ScotMac for your Linux on:

Interop+Crypto+OpenSslCryptographicException: error:2006D002:BIO routines:BIO_new_file:system lib
at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle)
at Internal.Cryptography.Pal.StorePal.LoadMachineStores()

Seems like there's a file in /etc/ssl/certs/ (or wherever the system trust lives on your particular distro) that can't be opened by the current user.

@ScotMac
Copy link

ScotMac commented May 30, 2019

Yes, the linux issue appears to be environmental, and we are now able to connect successfully from Linux.

However, the MacOS issue remains. Would you like me to try the openSSL command from MacOS?

@wfurt
Copy link
Member

wfurt commented May 30, 2019

on MacOS we do not use OpenSSL for SslStream so it is not quite valid test. For client cert, I assume you use Import function as on Linux, right?

You never answer question about what certificate server uses and how do you set up trust. If that is certificate signed by well known CA, you should have it already in your KeyChain but it would be good to verify that.

You can do Wireshark trace from Linux (working) and OSX (notWorking) Looking at the difference may provide some clues.

@ScotMac
Copy link

ScotMac commented May 30, 2019

Regardless whether you use OpenSSL on MacOS or not, i assumed you wanted to see the server cert via openssl. ie, that -showcerts option you indicated.

In terms of the trust, the certificate is digicert and both client and server are adding an EXPLICIT trustpoint for the digicert CA/root. We are not relying on "well known CAs" at all. Sorry if my previous response was not clear.

@wfurt
Copy link
Member

wfurt commented May 30, 2019

ok. So you add it to System Root or System KeyChain on OSX?

@ScotMac
Copy link

ScotMac commented May 30, 2019

No. Sorry if i wasn't clear again. We are adding that EXPLICIT trustpoint to the client and server certs, not the O/S.

@alexkeh
Copy link
Author

alexkeh commented Jun 7, 2019

I uploaded the test case to the MS Support case this morning. You can use to reproduce the issue and perform further diagnostics.

@karelz
Copy link
Member

karelz commented Sep 5, 2019

To our knowledge it turned out to be PFX that Mac OS didn't like.

We should have sufficient logging that enables us to diagnose these kind of problems easily - renaming the issue

@karelz karelz changed the title Failure during SSL handshake - .NET Core on macOS Make SSL handshake failures diagnosable Sep 5, 2019
@karelz
Copy link
Member

karelz commented Sep 5, 2019

Duplicate of #2359

@karelz karelz closed this as completed Sep 5, 2019
@ScotMac
Copy link

ScotMac commented Sep 5, 2019

This issue is associated w/ .NET Core SSL on MacOS failing to parse a perfectly valid pkcs12 file. ie has almost nothing to do w/ "exceptions and logging".

@wfurt
Copy link
Member

wfurt commented Sep 5, 2019

The goal is to make such cases easier to diagnose.

@alexkeh
Copy link
Author

alexkeh commented Sep 5, 2019

@karelz I filed the original issue. You changed the issue title, immediately afterwards classified it as a duplicate, then closed it. I don't understand the purpose of doing all that.

The new title does not reflect the fundamental issue: .NET Core SSL on macOS is not working under certain conditions. I am not asking for improved logging to diagnose SSL handshake exceptions. And once this improved logging is added, will the macOS SSL .NET Core I identified be fixed (Issue #2359)? No.

Please re-open the issue and provide a title more reflective of the fundamental issue, such as the original one I used. @bartonjs has scheduled this issue to be fixed in .NET 5. I do not want him to get the mistaken impression that the issue no longer exists now that it is "closed" when it still does exist.

Thanks!

@bartonjs
Copy link
Member

bartonjs commented Sep 5, 2019

@alexkeh My portion of the work is dotnet/corefx#40539, and won't involve touching SslStream at all.

I presume @karelz's assessment is that once the "Apple doesn't find the private key from Oracle PFXes" is mitigated that the only thing left from this issue is that it was hard to find out where things fell apart... and that improving diagnosability was already tracked by somewhere else.

@karelz
Copy link
Member

karelz commented Sep 6, 2019

Correct. That is what my comment meant. I am curious which part was not clear.

Rejection of the PFX is done at OS level (not something .NET Core can influence). The remaining work is to make sure it is easier to debug when it happens next time -- which is tracked already by existing issue we discovered during triage and duplicated against.

@msftgits msftgits transferred this issue from dotnet/corefx Feb 1, 2020
@msftgits msftgits added this to the 5.0 milestone Feb 1, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 13, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net.Security enhancement Product code improvement that does NOT require public API changes/additions os-mac-os-x macOS aka OSX
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants