RickardPettersson / swish-api-csharp

Swish For Merchant API Client .Net Standard Library
MIT License
28 stars 13 forks source link

Ssl-problem vid anrop till client.MakePaymentRequest från console-appen #3

Closed bjorkqvist closed 4 years ago

bjorkqvist commented 4 years ago

Jag får ssl-problem vid anrop till client.MakePaymentRequest från console-appen. Några idéer om vad som kan vara problemet?

Stacktrace: "System.Net.WebException: The SSL connection could not be established, see inner exception. Authentication failed, see inner exception. ---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> System.ComponentModel.Win32Exception: Det mottagna meddelandet var oväntat eller felaktigt formaterat\r\n --- End of inner exception stack trace ---\r\n at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)\r\n at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.PartialFrameCallback(AsyncProtocolRequest asyncRequest)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Net.Security.SslState.ThrowIfExceptional()\r\n at System.Net.Security.SslState.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)\r\n at System.Net.Security.SslState.EndProcessAuthentication(IAsyncResult result)\r\n at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)\r\n at System.Net.Security.SslStream.<>c.b__47_1(IAsyncResult iar)\r\n at System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult iar, Func2 endFunction, Action1 endAction, Task1 promise, Boolean requiresSynchronization)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)\r\n --- End of inner exception stack trace ---\r\n at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)\r\n at System.Threading.Tasks.ValueTask1.get_Result()\r\n at System.Net.Http.HttpConnectionPool.CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Threading.Tasks.ValueTask1.get_Result()\r\n at System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync(ValueTask1 creationTask)\r\n at System.Threading.Tasks.ValueTask1.get_Result()\r\n at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)\r\n at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)\r\n at System.Net.HttpWebRequest.SendRequest()\r\n at System.Net.HttpWebRequest.GetResponse()\r\n --- End of inner exception stack trace ---\r\n at System.Net.HttpWebRequest.GetResponse()\r\n at RestSharp.Http.GetRawResponse(HttpWebRequest request)\r\n at RestSharp.Http.GetResponse(HttpWebRequest request)"

jfalameda commented 4 years ago

I am experiencing the same issue, did you find out how to fix it?

RickardPettersson commented 4 years ago

I have tried to find the problem but can not do and the help from GetSwish AB is bad.

They like me to get throw a supplier of the integration instead do it my self.

But i have figured it out with there help that they only see one certificate of the 3 that they should, more then that i have not got help with.

jfalameda commented 4 years ago

Tack för svaret! Jag ska försöka ändra HTTP clienten och se om det gör någon skillnad. Fungerar det bra med prod certificates?

RickardPettersson commented 4 years ago

Det är med production certifikaten GetSwish AB säger att dem ser bara ett certifikat i requesten jag gör.

Men antar det är samma fel i test miljön.

jfalameda commented 4 years ago

Precis, så är det.

RickardPettersson commented 4 years ago

Hittar du lösning så skriv gärna här eller gör pull request och jag gör självklart samma sak.

jfalameda commented 4 years ago

Vet du när slutade fungera ung.?

jfalameda commented 4 years ago

I replaced the http client library for HttpClient, i also ensured that tls1.1 is used and I am skipping CA validation with the exact same results. This is very strange, I do not encounter any issues using curl. Interesting problem.

This is my implementation

public HttpClient CreateRestClient()
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11;
            // Create up a client certificate collection and import the certificate to it
            X509Certificate2Collection clientCertificates = new X509Certificate2Collection();
            clientCertificates.Import(_certDataBytes, _certificatePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);

            HttpClientHandler handler = new HttpClientHandler
            {
                SslProtocols = SslProtocols.Tls11,
                ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => true
            };

            foreach (X509Certificate2 x509 in clientCertificates)
            {
                handler.ClientCertificates.Add(x509);
            }

            HttpClient client = new HttpClient(handler);
            return client;
        }
RickardPettersson commented 4 years ago

Yea, i have spent 7-10 hours trying to find a solution without any good result.

jfalameda commented 4 years ago

I contacted Swish and they gave me a sort of alike response. They only see 2 of the 3 certificates. This makes no sense as the 3 certificates in the collection are appended. Also, I bypass Server SSL validation and I added all certificates to my keychain store and trusted them all.

I do believe there is something on their side but they cannot come back with any useful feedback.

RickardPettersson commented 4 years ago

Intresting and i have the same feeling but they dont want to support small developers....

jfalameda commented 4 years ago

They have forwarded the issue to their developers, let's see if this helps.

RickardPettersson commented 4 years ago

Nothing new @jfalameda ?

jfalameda commented 4 years ago

Unfortunately no. I haven't got any answer from Swish yet.

jfalameda commented 4 years ago

@RickardPettersson Any progress from your side?

jfalameda commented 4 years ago

I still haven't got any response from Swish.

RickardPettersson commented 4 years ago

I did some more tests and let GetSwish AB tech support check and they asked some question but still didnt give me a good answer.

I have not got good time for this after the small more tests.

RickardPettersson commented 4 years ago

I got answer today from GetSwish AB tech support that the developers now working on getting some code examples to give me so maybe soon we can get this to work..

jfalameda commented 4 years ago

Hi, Have you heard anything from Swish? They haven't come back to me yet.

RickardPettersson commented 4 years ago

No, i have asked the tech support like 4 times after 1 April whats happening and they can not say more then they waiting for the developers to do there work. :) I guessing they find it not good as we have done :)

monobjorn commented 4 years ago

But i have figured it out with there help that they only see one certificate of the 3 that they should, more then that i have not got help with.

I contacted Swish and they gave me a sort of alike response. They only see 2 of the 3 certificates. This makes no sense as the 3 certificates in the collection are appended. Also, I bypass Server SSL validation and I added all certificates to my keychain store and trusted them all.

@jfalameda @RickardPettersson I've been looking into this as well. I can call Swish test API with curl (version 7.70 on Win10, 7.55 did not work):
curl -s -S -i --cert Swish_Merchant_TestCertificate_1234679304.p12:swish --cert-type p12 --tlsv1.2 --header "Content-Type:application/json" https://mss.cpc.getswish.net/swish-cpcapi/api/v1/paymentrequests --data-binary @jsondata.json

However when I try to use .net core built-in HttpClient I get the same error as you: "The message received was unexpected or badly formatted."

I then moved on to comparing the calls made from curl and from .net in Wireshark and I found one difference that caught my eye (even though I was just guessing since this is not really my field of expertise).
Call from curl:
curl

Call from .net core:
netcore_err

The difference is that when a "Certificate"-call/request is made, curl supplies all three certificates from the certificate file (Swish_Merchant_TestCertificate_1234679304.p12) whereas .net core just supplies one.

So I went ahead and looked at the source code for the HttpClient and found that it called CertificateHelper.GetEligibleClientCertificate(ClientCertificates) to get one valid client certificate.
I'm not sure if this is why is the actual reason to why only one certificate is sent from .net core or not, but it could be.

When I was looking in the source code for HttpClient I also saw that if you set handler.ClientCertificateOptions = ClientCertificateOption.Automatic then the certificates are loaded from the "MY" certificate store (which is equal to "Personal certificate" for "Current User" in Windows). I went ahead and imported the certificate and tried again but with no luck (got another error message). But after changing back to handler.ClientCertificateOptions = ClientCertificateOption.Manual it now worked. I looked at the call in Wireshark and now there were three certificates sent:
netcore_ok

From the "Personal certificates" I could delete the one named 1234679304 and just keep to two CA certificates:
cert

To sum it up: It looks as if .net core needs the CA certificates to be present in the certificate store. If they just are in the certificate file used as "Client certificate" it wont work.

I will do some additional digging to see if this is the way it is in .net core or if there is some other way to make it behave like curl (include the CA certificates).

monobjorn commented 4 years ago

I contacted Swish and they gave me a sort of alike response. They only see 2 of the 3 certificates. This makes no sense as the 3 certificates in the collection are appended. Also, I bypass Server SSL validation and I added all certificates to my keychain store and trusted them all.

I do believe there is something on their side but they cannot come back with any useful feedback.

@jfalameda I just found another issue here on Github with the same problem that might interest you: https://github.com/dotnet/runtime/issues/29653#issuecomment-495667695

(Also, they had done the exact same network sniffing I spent some hours on doing myself. Typical ;-) )

RickardPettersson commented 4 years ago

This is very intresting @monobjorn Becouse thats can mean that .Net Standard that the whole library working on has the same problem as the .Net Core HttpClient.....

So if we moving the whole code to a .Net Framework project... then it maybe working..

Need to test, comming back with what i finding.

Also seeems very wrong of .Net Core to required to have the certificates installed.

monobjorn commented 4 years ago

I just posted a question on SO regarding whether or not .net core (HttpClient) requires the certificates to be installed or not: Can a .p12 file with CA certificates be used in C# without importing them to certificate store

RickardPettersson commented 4 years ago

I tested convert all projects to .Net Framework 4.8 and got same problem to test environemnt and tested convert to 4.6.1 to have a old version also same result.

But still go on your idea becouse can be different problems.

RickardPettersson commented 4 years ago

I got it to work with the answer @monobjorn got on https://stackoverflow.com/questions/61677247/can-a-p12-file-with-ca-certificates-be-used-in-c-sharp-without-importing-them-t/61681840#61681840

I going to implement the certificate handle code in my library and push up the code, work in progress to get all code over but i got a payment in production to work more tests and result comming as soon as i got all code over.

Can not say if its going to be today or tomorrow but its comming.

Thanks @monobjorn and everyone else trying so hard to get it to work.

RickardPettersson commented 4 years ago

Now i have pushed the latest code working for me on both my computers without installing the certificate both with the test enviroment that are in the code but even with my production account i got all to work.

I have some code left to update and document about QR Codes but the part of make payment, check payment status, make refund and check refund status now working.

Hope this help people again!

And to clarify i got the console test application in the github repository to work not tested more then that but guesing it going to work overall now.

RickardPettersson commented 4 years ago

I letting this be open and i like to get information from you all that this working?

patric1025 commented 4 years ago

Hi,

I just downloaded your latest code and tested the console app. I get the same SSL error as you guys. I also tried to put the CA cert in Personal and Trusted root certificates without success.

Any other suggestions?

RickardPettersson commented 4 years ago

I read about that they updated the test certificate for some days ago or something, i going to update but if you like to test before that download latest simulator package from swish site

patric1025 commented 4 years ago

Yeah I've done that since I was using my own code. Then I came across this issue and started Googling and found your code. I've also tested that certificate with you code. Same result...

monobjorn commented 4 years ago

@PJzyber Did you add it to the "current user" certificate store? Adding it to the "local machine" certificate store wont help.

From the "Personal certificates" I could delete the one named 1234679304 and just keep to two CA certificates: cert

RickardPettersson commented 4 years ago

In order to get all to work you need the latest test certificate from GetSwish AB and i have now pushed it to the repository so should be good to pull the latest code and get the console test app to work.

RickardPettersson commented 4 years ago

I know closing this issue down becouse the problem this was created for is fixed. If someone else get problems create a new issue please.