How to allow self signed certificate

I’m trying to connect to a REST api host on my company’s development server. This server has a self signed certificate and therefore Unity refuses to accept the connexion.
I tested my GET request uri using postman and it worked well as soon as I disabled the certificate validation so I think it is indeed a certificate problem.

I searched for solution and found that some people use HttpWebRequest with a redefinition of the ServicePointManager’s certificate validation callback.
I tried this :

IEnumerator GetAPI(string uri)
    {
        Debug.Log(uri);
        ServicePointManager.ServerCertificateValidationCallback = TrustCertificate;


        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        Debug.Log(request.Address);
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();

        Stream dataStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(dataStream);
        string responseFromServer = reader.ReadToEnd();

        Debug.Log("responseFromServer=" + responseFromServer);

        yield return 0;
    }

This works fine with a test API ( http://jsonplaceholder.typicode.com/posts ) but when trying with the actual server I get the following error :

and (when sending on a datagram socket using a sendto call) no address was supplied.```

Does someone have any clue on what could be going on ? Thanks in advance.

I must add that I do not have any access to the server excluding the API so I cannot setup a valid certificate.

So where is your TrustCertificate function? Also, if you’re just gonna yield return 0 theres no point in using an IEnumerator, it’s not gonna be async.

Trust certificate is just a function that always returns true no matter the certificate.

private static bool TrustCertificate(object sender, X509Certificate x509Certificate, X509Chain x509Chain, SslPolicyErrors sslPolicyErrors) {
       // all certificates are accepted
        return true;
   }

I know that yield return 0 is not very relevant in an IEnumerator but it is easier to read debug logs when it is synchrone.

Can you add it to the trusted root certificates of the system you are testing on?

I only have the .crt Firefox got me when I connected to the API with it and already added it to windows 10 trusted root certificates but it didn’t changed anything.
I also tried to add it to Mono’s certificate store but with no success

It seems that the error I got is due to the response stream was not received at the time the code is trying to access it with the StreamReader.

Thus I tried with this solution, using BeginGetResponse instead of GetResponse :

void GetRequest(string uri)
    {
        ServicePointManager.ServerCertificateValidationCallback = TrustCertificate;

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        request.BeginGetResponse(ResponseCallback, request);

    }

    private void ResponseCallback(IAsyncResult result)
{
        Debug.Log("Response CB");
        HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
        Debug.Log("1");
        Stream dataStream = response.GetResponseStream();
        Debug.Log("2");
        StreamReader reader = new StreamReader(dataStream);
        Debug.Log("3");
        string responseFromServer = reader.ReadToEnd();
        Debug.Log("4");

        Debug.Log("responseFromServer=" + responseFromServer);
    }

This time the code stalls after Debug.Log(“Response CB”) and I have no idea why it does.
The server’s .crt is in Windows’ trust store, in Mono’s AddressBook store and in all my browsers store.

For now we allow HTTP access as a workaround but it won’t be acceptable for long…

After further investigation it seems that the problem is not from my code: I tested it with a simple C# console project in Visual Studio (just changed the Debug.Log with Console.WriteLine) and it worked perfectly fine, I was able to connect to the server.

I also added a Debug.Log(“In the callback”) in the TrustCertificate callback and it seems that it is not called with Unity at all.
With Visual Studio i can see “In the callback” written in the console each time I make a request.

How comes that Unity doesn’t calls the callback ?

Try changing scripting backend to .net 4.6 and setting the validationcallback on the httprequest itself instead of the service point manager.

I already have .NET set to 4.6 in the player settings.
Setting the validationcallback on the httprequest didn’t change anything :confused:

By adding a try catch block in the GetRequest method I discovered that the code was stalling line 13. because of an exception silently catched by Unity.
printing that error gave me this : SecureChannelFailure (The authentication or decryption has failed)

For what its worth, the below code works for us, but only after changing to 4.6 and forgetting about the service point manager (as posted before). We use the most recent unity version.

HttpWebRequest request = (HttpWebRequest) WebRequest.Create(apiURL );
request.ContentType = "application/json";
request.Method = "POST";
request.ServerCertificateValidationCallback += CertificateValidationCallback;

public bool CertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return certificate.Equals(cert);
}

Doesn’t work for me either, I still got the SecureChannelFailure.

What version of TLS is your server running ?

It’s running TLS 1.2

Unity beta 2018.1 adds custom certificate verification to unitywebrequest, you can try that instead if you don’t mind going beta.

Docs here Unity - Scripting API: Networking.UnityWebRequest.certificateHandler

1 Like

How did you manage to make Unity actually uses TLS 1.2 ? When I look in Wireshark it only uses TLS 1.0 ?

Thanks for the info on the beta, I’ll give it a look

Indeed it works well with the Beta, thank you very much !

We haven’t made any changes to force Unity to use a specific version. Glad it worked for you.

Since 2018.2 we have a shared TLS backend for new mono (.Net 4+) and UnityWebRequest. This backend supports TLS 1.2 and verifies against a platform dependent certificate store.

Due to a bug in Mono ServicePointManager.ServerCertificateValidationCallback does not work with their modern TLS bindings which we use to connect to our new backend.

Another way to work around this other than UnityWebRequest is by using the deprecated CertificatePolicy setting like this:

class NoCheckCertificatePolicy : System.Net.ICertificatePolicy
{
   public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem)
   {
      return true;
   }
}
// ...
System.Net.ServicePointManager.CertificatePolicy = new NoCheckCertificatePolicy();

This requires Api Compatibility Level .Net 4.x though, but worked when I tested it a month ago.

4 Likes

Hello. I have the same problem, but i want to use sslstream. After copying above code, that @andreasreich posted, it connecting, but i cant send any message, because i have this error: This operation is only allowed using a successfully authenticated context. I already have:
Certificate = new X509Certificate2(“./server.pfx”);
SslStream.AuthenticateAsServer(Certificate, false, false);
in server and
SslStream.AuthenticateAsClient(“192.168.0.4”);
in client. Can someone help me?

Hi, I tried both ServicePointManager.ServerCertificateValidationCallback & ServicePointManager.CertificatePolicy in Unity2019.4.16f1(.Net 4.x), but they all didn’t work. Is there any other way to make a global setting for certificate validation.

I can confirm the proposed solution by @andreasreich works in Unity 2020.3.15f1 on Windows x64 as well as ARM64 UWP HoloLens 2. However, it does not work in the Linux editor on desktop, and by consequence also not in CI environments using it.

I can’t find any way to allow self-signed certificates using HttpClient on Linux, since the intended approach using ServerCertificateCustomValidationCallback is “not implemented” and the above workaround appears to be Windows-only.

1 Like

In case anyone has a similar issue as me and arrives here via google search. I was getting the error “Curl error 60: Cert verify failed. Certificate Common Name(CN) does not match with the expected CN. UnityTls error code: 7” because UnityWebRequest wouldnt accept my local dev API server (python/flask) fake cert. Making an override handler that always accepts worked:

#if DEBUG 
    private class DebugCertificateHandler : CertificateHandler
    {
        protected override bool ValidateCertificate(byte[] certificateData)
        {
            return true;
        }
    }
#endif

Then wire it up to your requests

#if DEBUG          
            request.certificateHandler = new DebugCertificateHandler();
#endif
1 Like