Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

How to allow self signed certificate

Discussion in 'Scripting' started by goddatr, Mar 16, 2018.

  1. goddatr

    goddatr

    Joined:
    Mar 16, 2018
    Posts:
    26
    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 :
    Code (CSharp):
    1.  
    2. IEnumerator GetAPI(string uri)
    3.     {
    4.         Debug.Log(uri);
    5.         ServicePointManager.ServerCertificateValidationCallback = TrustCertificate;
    6.  
    7.  
    8.         HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    9.         Debug.Log(request.Address);
    10.         HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    11.  
    12.         Stream dataStream = response.GetResponseStream();
    13.         StreamReader reader = new StreamReader(dataStream);
    14.         string responseFromServer = reader.ReadToEnd();
    15.  
    16.         Debug.Log("responseFromServer=" + responseFromServer);
    17.  
    18.         yield return 0;
    19.     }
    20.  
    This works fine with a test API ( http://jsonplaceholder.typicode.com/posts ) but when trying with the actual server I get the following error :
    A request to send or receive data was disallowed because the socket is not connected
    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.
     
  2. nilsdr

    nilsdr

    Joined:
    Oct 24, 2017
    Posts:
    374
    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.
     
  3. goddatr

    goddatr

    Joined:
    Mar 16, 2018
    Posts:
    26
    Trust certificate is just a function that always returns true no matter the certificate.

    Code (CSharp):
    1.  
    2. private static bool TrustCertificate(object sender, X509Certificate x509Certificate, X509Chain x509Chain, SslPolicyErrors sslPolicyErrors) {
    3.        // all certificates are accepted
    4.         return true;
    5.    }
    6.  
    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.
     
  4. MrMatthias

    MrMatthias

    Joined:
    Sep 18, 2012
    Posts:
    191
    Can you add it to the trusted root certificates of the system you are testing on?
     
  5. goddatr

    goddatr

    Joined:
    Mar 16, 2018
    Posts:
    26
    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
     
  6. goddatr

    goddatr

    Joined:
    Mar 16, 2018
    Posts:
    26
    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 :
    Code (CSharp):
    1. void GetRequest(string uri)
    2.     {
    3.         ServicePointManager.ServerCertificateValidationCallback = TrustCertificate;
    4.  
    5.         HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    6.         request.BeginGetResponse(ResponseCallback, request);
    7.  
    8.     }
    9.  
    10.     private void ResponseCallback(IAsyncResult result)
    11. {
    12.         Debug.Log("Response CB");
    13.         HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
    14.         Debug.Log("1");
    15.         Stream dataStream = response.GetResponseStream();
    16.         Debug.Log("2");
    17.         StreamReader reader = new StreamReader(dataStream);
    18.         Debug.Log("3");
    19.         string responseFromServer = reader.ReadToEnd();
    20.         Debug.Log("4");
    21.  
    22.         Debug.Log("responseFromServer=" + responseFromServer);
    23.     }
    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...
     
    Last edited: Mar 21, 2018
  7. goddatr

    goddatr

    Joined:
    Mar 16, 2018
    Posts:
    26
    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 ?
     
  8. nilsdr

    nilsdr

    Joined:
    Oct 24, 2017
    Posts:
    374
    Try changing scripting backend to .net 4.6 and setting the validationcallback on the httprequest itself instead of the service point manager.
     
  9. goddatr

    goddatr

    Joined:
    Mar 16, 2018
    Posts:
    26
    I already have .NET set to 4.6 in the player settings.
    Setting the validationcallback on the httprequest didn't change anything :/

    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)
     
  10. nilsdr

    nilsdr

    Joined:
    Oct 24, 2017
    Posts:
    374
    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.

    Code (CSharp):
    1. HttpWebRequest request = (HttpWebRequest) WebRequest.Create(apiURL );
    2. request.ContentType = "application/json";
    3. request.Method = "POST";
    4. request.ServerCertificateValidationCallback += CertificateValidationCallback;
    5.  
    6. public bool CertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    7. {
    8. return certificate.Equals(cert);
    9. }
     
  11. goddatr

    goddatr

    Joined:
    Mar 16, 2018
    Posts:
    26
    Doesn't work for me either, I still got the SecureChannelFailure.

    What version of TLS is your server running ?
     
  12. nilsdr

    nilsdr

    Joined:
    Oct 24, 2017
    Posts:
    374
    hayabu4i likes this.
  13. goddatr

    goddatr

    Joined:
    Mar 16, 2018
    Posts:
    26
    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
     
  14. goddatr

    goddatr

    Joined:
    Mar 16, 2018
    Posts:
    26
    Indeed it works well with the Beta, thank you very much !
     
  15. nilsdr

    nilsdr

    Joined:
    Oct 24, 2017
    Posts:
    374
    We haven't made any changes to force Unity to use a specific version. Glad it worked for you.
     
  16. andreasreich

    andreasreich

    Unity Technologies

    Joined:
    Sep 24, 2017
    Posts:
    55
    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:
    Code (CSharp):
    1.  
    2. class NoCheckCertificatePolicy : System.Net.ICertificatePolicy
    3. {
    4.    public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem)
    5.    {
    6.       return true;
    7.    }
    8. }
    9. // ...
    10. System.Net.ServicePointManager.CertificatePolicy = new NoCheckCertificatePolicy();
    This requires Api Compatibility Level .Net 4.x though, but worked when I tested it a month ago.
     
    jlrueda, sivrikaya and DKasper_3DQR like this.
  17. KubekSzklany

    KubekSzklany

    Joined:
    Mar 9, 2020
    Posts:
    9
    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?
     
  18. AritaDev

    AritaDev

    Joined:
    Aug 13, 2019
    Posts:
    12
    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.
     
  19. NoTuxNoBux

    NoTuxNoBux

    Joined:
    Oct 2, 2020
    Posts:
    33
    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.
     
    CrandellWS likes this.