The Curse of Mono =\

A couple weeks ago I was tasked with building a plugin for Unity that would allow testers and developers to take screenshots, comment and submit tickets to our bug tracker, JIRA. Our instance is in the cloud and behind HTTPS, which are generally great things. I have used JIRA for Gneu, at Blinkx and even a few of my previous game dev related projects; I had even built a few other tools to move tickets into, around and out of JIRA, so it sounded like a reasonable request from Unity.

Unfortunately, the estimates were far too optimistic.

I did some preemptive research on this and found that using C# to reach out to an HTTPS hosted web server was simple, so I built out the library. I had 100% test coverage and even threw in some JSON serialization & deserialization to make things easy on the maintenance front. I decided to use a WebRequest which was surely made for this exact context. In all, it took about 5 hours to write the code & tests to be able to handle all of the REST based API calls into our JIRA instance.

I spent some time cleaning up the code and was satisfied with how it turned out so I dropped the dll into the Unity project loaded it up. Of course, every single test was failing and there was no obvious road to take. I went to bed.

Clearly something was up, but finding the cause was not going to be easy. I was back up at 6AM, compiling, debugging and trying to find the source of the issue. In all, I spent the better part of 15 hours chasing the rabbit, and unfortunately the issue resides in the version of Mono Unity bundles itself with, and even more unfortunate is that it’s not within my hands to fix.

When I built the dll out, I used Visual Studio to create the project/solution and tests projects. This meant that I was actually using .net libraries and not Mono, though I could have, and probably should have, caught the issue earlier by building on the appropriate platform.

If you spend some time googling you will find that there are many people reporting issues with their HTTPS queries, and specifically the exception you will see time and again is typically, at least according to the forums and answer hub for Unity3D, tied to SSL Certificate Authorities not working correctly… Lets look deeper.

Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server.

Mono, for those of you that don’t know, is the platform that was fulfilling the original promise from Microsoft, that .net would be cross platform. As such, they have had to take “roll your own” to a whole new level. the library literally reverse engineers and re-implements nearly the entirety of .Net, and it does it in a way that works on Mac and Linux, so your code written on Mono works on Mac and Linux. This was lofty and it meant that they had to, again unfortunately, do things in their own way. One such place where this is a problem is the way SSL certificates are tested.

SSL Certificates are issued by Certificate Authorities. These authorities are of course internet companies, and many of them are up to date with their security, which we will come back to later. When your machine reaches out to a website, like https://www.google.com/?s=42, it makes a handshake, exchanges information and one of those pieces of information is the SSL certificate. When you have surfed previously you have undoubtedly run across websites with expired, self-signed or unsigned certificates which your browser now reports to you with a huge full page notice warning you about the site being unsafe. Each certificate is signed by a signing authority and issued for use on a given host, which in turn is passed to you.

Well, Mono and .Net are no different. When you make a WebRequest and call for the response object the query is executed and the certificate is checked. With .Net, your certificate is confirmed against a Certificate store that is maintained by the operating system; with Mono, your certificate store is entirely your own and most likely to be entirely empty unless you have already run across this issue and corrected it, but judging by the amount of interest people have in the topic that is incredibly unlikely.

With an empty certificate authority store you have no authorities that you trust, and so every HTTPS query you make will fail. This is of course terrible and likely to cause you a lot of frustration when debugging because all you will see is an exception telling you that the host certificate is bad.

Weighing the options, Atlassian – a well known company, with tools in use by Fortune 500 companies and with their software hosting a good portion of the world’s repos – either has a bad certificate that even Google’s Chrome has failed to catch or Mono, and in turn Unity, has a problem that needs to be fixed.

Of course, this is probably the dumbest dichotomy to run into, considering it’s obviously not a bad certificate.

The common solution you will see on the forums and in the answer hub community to this is actually a kludge. The fix is to effectively overwrite the way that certificate authorities are authorized, either by the simple approach of just accepting true or the slightly better route of checking some of the configurations from the certificate. The issue here is that you are compiling this into your application and this is not something to evolve over time. This is not by any means the optimal approach as it introduces security concerns that we shouldn’t need to worry about. Fortunately for me, my project is internal and not likely to become a problem, so lets trudge forward.

If you are interested in attempting to configure the certificate authorities appropriately, please read the Mono FAQ Security page.

I have hooked up the terrible fix. I take the dive and see what comes… only I am still getting a failed request. If ever there has been a problem that didn’t make sense… this is the place.

Looking deeper, there was a comment that caught my eye:

I’ve done a little digging and discovered that Mono.Runtime.GetDisplayName returns “2.6.5 (tarball)”. Is Unity 5 still using such an ancient version of Mono? I checked the source for Mono 2.8 and, sure enough, the VerifySignature method mentioned in the exception doesn’t recognize SHA-2 algorithms:
[Answers]

Lets check our build…

G:\Unity\Editor\Data\Mono\bin>monop2 --runtime-version mono.exe
runtime version: 2.0.50727.1433

Pop over to the Mono project, swap to the file that actually sets this up and…

case "sha256":
// sha256WithRSAEncryption  OBJECT IDENTIFIER ::= { pkcs-1 11 }
    return "1.2.840.113549.1.1.11";

Pulling the latest certificate from Atlassian shows that they are indeed using SHA-256 v.2.16.840.1.114412.1.1 encryption and that sums everything up. In fact, if you were to search through the entirety of the Mono project, even on the latest default branch, you wont find this version of the SHA encryption algorithm available, let alone mentioned.

For those of you who have made it this far you are likely looking for a solution, and the truth is there is currently only a half solution available – WWW. This is a class provided by Unity itself to make web requests and although the internals aren’t exactly visible… it can POST to modern HTTPS URI, and that is our bare minimum for the task at hand.

There is something to be said about this, though. It is clearly not using WebRequests and has to be able to decrypt/encrypt SHA2 which means it is providing all of the features we are looking for, right? I wish it were so simple. My project is ultimately going to be using the PUT and DELETE methods of the REST API, and again with the unfortunate nature of deviating from standard implementations this is not available by default. There are some posts on the answer hub about people using the X-HTTP-Method-Override header to get around it but I have not confirmed that just yet.

What all of this means, for those of you with glazed over eyes or a rather pronounced frown, is that there are subtle and complicated differences you need to be aware of when using Mono on Windows, and specifically so when it comes to the implementations & dependencies that aren’t well trodden. Chances are you are not going to run into these issues, but you should be aware of the possibility.

The project is coming along using WWW, although not nearly as well as I had hoped now that I am having to reapproach it without the battery of tests I had initially. Hopefully this week I’ll be able to proceed with testing this further, within Unity itself.

2 thoughts on “The Curse of Mono =\”

  1. Hi! Have you had any luck with this? I chanced the REST rabbit for a while with WWW before finding Atlassian .Net, which threw a security exception and failed to connect. This lead me here 🙂

    1. Sure have.

      I was able to handle all of the standard interactions with our OnDemand instance using the WWW interface, I just wised up and moved over to the JSON serializer here: https://github.com/jacobdufault/fullserializer
      Once i had that in place i was able to get things moving again pretty quickly. If you need further help just let me know, but using this serializer was a huge leap in the right direction.

      =) Hope this helps!

Leave a Reply