Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unity - Unable to send HTTPS request due to CryptographicException

I'm trying to send an https request to my remote server but I'm always getting the following exception:

Exception: System.IO.IOException: The authentication or decryption has failed. ---> System.ArgumentException: certificate ---> System.Security.Cryptography.CryptographicException: Unsupported hash algorithm: 1.2.840.113549.1.1.11
  at Mono.Security.X509.X509Certificate.VerifySignature (System.Security.Cryptography.RSA rsa) [0x00000] in <filename unknown>:0 
  at Mono.Security.X509.X509Certificate.VerifySignature (System.Security.Cryptography.AsymmetricAlgorithm aa) [0x00000] in <filename unknown>:0 
  at System.Security.Cryptography.X509Certificates.X509Chain.IsSignedWith (System.Security.Cryptography.X509Certificates.X509Certificate2 signed, System.Security.Cryptography.AsymmetricAlgorithm pubkey) [0x00000] in <filename unknown>:0 
  at System.Security.Cryptography.X509Certificates.X509Chain.Process (Int32 n) [0x00000] in <filename unknown>:0 
  at System.Security.Cryptography.X509Certificates.X509Chain.ValidateChain (X509ChainStatusFlags flag) [0x00000] in <filename unknown>:0 
  at System.Security.Cryptography.X509Certificates.X509Chain.Build (System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---
  at System.Security.Cryptography.X509Certificates.X509Chain.Build (System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) [0x00000] in <filename unknown>:0 
  at System.Net.Security.SslStream+<BeginAuthenticateAsClient>c__AnonStorey7.<>m__A (System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Int32[] certErrors) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.SslClientStream.OnRemoteCertificateValidation (System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Int32[] errors) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.SslStreamBase.RaiseRemoteCertificateValidation (System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Int32[] errors) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.SslClientStream.RaiseServerCertificateValidation (System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Int32[] certificateErrors) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.validateCertificates (Mono.Security.X509.X509CertificateCollection certificates) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.ProcessAsTls1 () [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.Handshake.HandshakeMessage.Process () [0x00000] in <filename unknown>:0 
  at (wrapper remoting-invoke-with-check) Mono.Security.Protocol.Tls.Handshake.HandshakeMessage:Process ()
  at Mono.Security.Protocol.Tls.ClientRecordProtocol.ProcessHandshakeMessage (Mono.Security.Protocol.Tls.TlsStream handMsg) [0x00000] in <filename unknown>:0 
  at Mono.Security.Protocol.Tls.RecordProtocol.InternalReceiveRecordCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---
  at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 

At first I thought it was a Certificate validation issue, so I added the following code before sending the request:

ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, ssl) => true;

Turns out that the ServerCertificateValidationCallback wasn't even being invoked and the exception was related to 'Plain SSL' or 'SSL ciphers'.

Note that my remote server URL is https://_my_app_.herokuapp.com/_path_. Strangely enough, if I change the URL to any other https server such as https://www.google.com or https://www.facebook.com I don't get the Exception and the request succeeds.

Looks like the Mono implementation used by Unity does not support heroku's cipher suites (I can guarantee that because I recently developed a MonoTouch application that was able to connect to the same server).

Is it a client or server issue? Should I add some hacky code on the client side or should the server's configuration be changed?

like image 252
Eduardo Coelho Avatar asked Feb 13 '14 19:02

Eduardo Coelho


3 Answers

I don't have a complete answer, but it's not a ciphersuite issue; the stacktrace shows it is a cert validation issue at the low level of verifying the signature before your callback would be involved. OID 1.2.840.113549.1.1.11 is SHA256withRSA, and the security provider(s?) in the client isn't handling it. You work okay with mainstream servers like google and facebook because they continue to use certs signed SHA1withRSA, precisely because client/browser support for SHA256+RSA is not yet universal although increasingly common. https://cabforum.org/baseline-requirements-documents/ requires RSA2048 this year (Jan 2014) unconditionally, but allows SHA1 "until SHA-256 [verify] is supported widely by browsers used by a substantial portion of relying-parties worldwide."

OTOH SHA1 doesn't have very much safety margin left, many people are pushing for SHA256, and Digicert seems to be among the harder pushers: http://www.digicert.com/transitioning-to-sha-2.htm . The cert I see from herokuapp.com was issued recently (2014-01-21) so this could be a deliberate choice "let's improve security" or just "use what is convenient and Digicert gave us this". FWIW I observe id.heroku.com and www.heroku.com use a SHA1+RSA(2048) cert from 2013-10-12. If you can't get your client fixed for SHA256+RSA and heroku is willing, they could get and use a SHA1+RSA cert for now, but you'll likely still need a real fix later.

As to that, How can I sign a file using RSA and SHA256 with .NET? indicates that for (MS) dot-net signing with SHA256+RSA needs to fiddle a non-default crypto provider, and IME verification is usually the same as signing. I don't use Mono or know if the same or similar issue applies there, but it's the area where I would look first.

like image 80
dave_thompson_085 Avatar answered Nov 13 '22 16:11

dave_thompson_085


As of Unity 4.3, Unity's version of Mono does not support SHA-256 for SSL certs. The "best" supported is SHA-1. We had used GoDaddy (I know, I know) to issue the cert and fortunately they had a web page where we could re-key the cert from SHA-256 to SHA-1. Now the SSL cert works great.

It will be near impossible to find a CA that will issue an SHA-1 cert so you will have to re-key it.

Unity's WWW class is written in C++ and does not go through mono. That's why you can connect to https through WWW but not through the UnityHTTP plugin.

like image 38
Chris Masterton Avatar answered Nov 13 '22 17:11

Chris Masterton


I could not find the reason but I found the solution for the problem.

I was using UnityHTTP - a "TcpClient-based HTTP library" - to perform HTTP requests. By switching to Unity's WWW class all https requests sent to my heroku server succeeded. I found out that when using UnityHTTP library Unity was attempting to perform the crossdomain.xml validation on the 843 port, meanwhile when using WWW class performed such validation on port 80.

Maybe UnityHTTP's implementation deals with low-level stuff that is currently not compatible with heroku servers, I'll not further investigate this but I'll open an issue on the UnityHTTP's github repository anyway.

like image 25
Eduardo Coelho Avatar answered Nov 13 '22 17:11

Eduardo Coelho