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
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

WebRequest error with self-signed certificate

Discussion in 'Scripting' started by kburgin1, Oct 28, 2020.

  1. kburgin1

    kburgin1

    Joined:
    Jul 10, 2019
    Posts:
    10
    We are unable to switch our local development stack to HTTPS due to errors with our Unity app. We have:
    1. Created a local Certificate Authority (ca.crt),
    2. Signed a new localhost certificate with the CA,
    3. Installed the ca.crt into the Windows Trusted Root Authority Store,
    4. Installed the localhost certificate onto the nginx reverse proxy.
    This allows all other apps to access the services securely (Chrome, Firefox, Postman, Powershell, all report no issues and can query just fine).
    We have tried using the following Unity snippet:
    Code (CSharp):
    1.  
    2. public class BypassCertificate : CertificateHandler
    3. {
    4.     protected override bool ValidateCertificate(byte[] certificateData)
    5.     {
    6.         //Simply return true no matter what
    7.         return true;
    8.     }
    9. }
    10.  
    11. public class MyThing : MonoBehaviour
    12. {
    13.     private void Start()
    14.     {
    15.             ServicePointManager.Expect100Continue = true;
    16.             ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    17.             ServicePointManager.ServerCertificateValidationCallback +=
    18.                 (sender, certificate, chain, sslPolicyErrors) => true;
    19.            
    20.             string uri = "https://localhost:443/ping";
    21.  
    22.             UnityWebRequest www =
    23.                 new UnityWebRequest(uri, "GET") {certificateHandler = new BypassCertificate()};
    24.             try
    25.             {
    26.                 await www.SendWebRequest();
    27.             }
    28.             catch (Exception e)
    29.             {
    30.                 Debug.LogException(e);
    31.             }
    32.     }
    33. }
    34.  
    We get the following errors:
    Code (csharp):
    1.  
    2. Curl error 51: Cert verify failed: UNITYTLS_X509VERIFY_FLAG_UNKNOWN_ERROR
    3.  
    4. UnityWebRequestException: Unknown Error
    5.  
    We are developing this within the Unity 2019.4.9f1 editor on Windows 10, where the certificate is installed.
    We have tried building the app, but it still errors.

    As you can see in the above snippet. We have tried all the various suggested workarounds to at least try and get a successful connection (SecurityProtocol, CertificateValidation on both UnityWebRequest and ServicePointManager, etc...). We don't want to do these workarounds anyway as we want to be able to trust our localhost CA (like other apps on our machine).

    If I can provide any more detail for someone to help, please ask. After trying all the above workarounds and having it work in Chrome, Postman... etc, we are out of ideas.
     
  2. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,642
    Which version of TLS do you use?
    Unity supports up to TLSv1.2
     
    Bunny83 likes this.
  3. kburgin1

    kburgin1

    Joined:
    Jul 10, 2019
    Posts:
    10
    We're using TLS 1.2. Here's a cut down nginx config:

    Code (csharp):
    1.  
    2. worker_processes 1;
    3.  
    4. events { worker_connections 1024; }
    5.  
    6. http {
    7.  
    8.     sendfile on;
    9.  
    10.     upstream docker-api {
    11.         server backend:80; # points at the backend docker container
    12.     }
    13.  
    14.     proxy_set_header   Host $host;
    15.     proxy_set_header   X-Real-IP $remote_addr;
    16.     proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    17.     proxy_set_header   X-Forwarded-Host $server_name;
    18.     proxy_ssl_session_reuse off;
    19.     ssl_certificate /etc/ssl/certs/nginx.crt;
    20.     ssl_certificate_key /etc/ssl/private/nginx.key;
    21.     ssl_protocols TLSv1.2;
    22.     ssl_prefer_server_ciphers on;
    23.     ssl_dhparam /etc/ssl/certs/dhparam.pem;
    24.     ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
    25.     ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
    26.     ssl_session_timeout  10m;
    27.     ssl_session_cache shared:SSL:10m;
    28.     ssl_session_tickets off; # Requires nginx >= 1.5.9
    29.     add_header X-Frame-Options DENY;
    30.     add_header X-Content-Type-Options nosniff;
    31.     add_header X-XSS-Protection "1; mode=block";
    32.  
    33.     server {
    34.         listen 443 ssl;
    35.         listen [::]:443 ssl;
    36.         server_name localhost;
    37.  
    38.         location / {
    39.             proxy_pass         http://docker-api;
    40.             proxy_redirect     off;
    41.             proxy_ssl_session_reuse off;
    42.         }
    43.     }
    44.  
    45. }
    46.  
    47.  
    As mentioned, this config works fine for other applications like Chrome and Postman. This configuration also works fine with Unity when the certificate has been externally signed. The error arises when the certificate has been self-signed. Even though the local CA has been trusted in the Windows Trusted Root Authority Store (which makes it valid with Chrome etc.). Does Unity interact with the Windows Trust Store? If so, why would it not trust the local CA I've put in there? If Unity doesn't use the Windows Trust Store, how can I trust a local CA?

    I have tried the following snippet but it appears to make no difference:
    Code (CSharp):
    1.  
    2.             var path = @"C:\Users\x\Documents\y\ca.crt"; //Path to root certificate
    3.             using (X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) {
    4.                 store.Open(OpenFlags.ReadWrite);
    5.                 store.Add(new X509Certificate2(X509Certificate2.CreateFromCertFile(path))); //where cert is an X509Certificate object
    6.             }
    7.  
     
  4. kburgin1

    kburgin1

    Joined:
    Jul 10, 2019
    Posts:
    10
    Interestingly, when I try the .NET HttpClient snippet below:
    Code (CSharp):
    1.  
    2.             try  
    3.             {
    4.                 HttpClient client = new HttpClient();
    5.                 HttpResponseMessage response = await client.GetAsync(uri);
    6.                 response.EnsureSuccessStatusCode();
    7.                 string responseBody = await response.Content.ReadAsStringAsync();
    8.                 Debug.Log(responseBody);
    9.             }
    10.             catch(HttpRequestException e)
    11.             {
    12.                 Debug.LogException(e);
    13.             }
    14.  
    I get the following error:
    Code (csharp):
    1.  
    2. TlsException: Handshake failed - error code: UNITYTLS_INTERNAL_ERROR, verify result: UNITYTLS_X509VERIFY_FLAG_UNKNOWN_ERROR
    3. Mono.Unity.Debug.CheckAndThrow (Mono.Unity.UnityTls+unitytls_errorstate errorState, Mono.Unity.UnityTls+unitytls_x509verify_result verifyResult, System.String context, Mono.Security.Interface.AlertDescription defaultAlert) (at <14e3453b740b4bd690e8d4e5a013a715>:0)
    4. Mono.Unity.UnityTlsContext.ProcessHandshake () (at <14e3453b740b4bd690e8d4e5a013a715>:0)
    5. Mono.Net.Security.MobileAuthenticatedStream.ProcessHandshake (Mono.Net.Security.AsyncOperationStatus status) (at <14e3453b740b4bd690e8d4e5a013a715>:0)
    6. (wrapper remoting-invoke-with-check) Mono.Net.Security.MobileAuthenticatedStream.ProcessHandshake(Mono.Net.Security.AsyncOperationStatus)
    7. Mono.Net.Security.AsyncHandshakeRequest.Run (Mono.Net.Security.AsyncOperationStatus status) (at <14e3453b740b4bd690e8d4e5a013a715>:0)
    8. Mono.Net.Security.AsyncProtocolRequest+<ProcessOperation>d__24.MoveNext () (at <14e3453b740b4bd690e8d4e5a013a715>:0)
    9.  
     
  5. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,642
    Now I'm a bit confused, you are mixing UnityWebRequest and .NET HttpClient.
    One thing to note is that you need to restart Editor/player, as certificates are read once and cached.
     
  6. kburgin1

    kburgin1

    Joined:
    Jul 10, 2019
    Posts:
    10
    It seems very similar to this issue: https://forum.unity.com/threads/unitywebrequest-unable-to-complete-ssl-connection.566380/
    The error message is slightly different though. The certificate works in all other software, except Unity. I have tried visiting the host using IE and Edge and it works fine, Unity still doesn't. The difference between that thread and this one is that I can see that the local CA is installed within the Windows Trust Store already and it is being accepted by other software.
     
  7. kburgin1

    kburgin1

    Joined:
    Jul 10, 2019
    Posts:
    10
    Yep, that's just for debugging. We wouldn't actually be doing that in production. Just thought extra error output would help.
     
  8. kburgin1

    kburgin1

    Joined:
    Jul 10, 2019
    Posts:
    10
    Restarted Unity Editor as well as the Windows 10 machine, still same errors.
     
  9. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,642
    Would it be possible for you to report a bug with repro steps, preferably with the test project that just works?
    We could look into it more thoroughly then.
     
  10. kburgin1

    kburgin1

    Joined:
    Jul 10, 2019
    Posts:
    10
    I will have to do that then. There is obviously non-Unity things required to reproduce (such as creating a locally trusted CA and certificate). I will try to document those processes too.

    In the meantime, I would appreciate if anyone can see anything that nginx is missing that Unity requires or maybe there's a specific requirement on SSL certs that Unity requires but other software is happy with. To be clear, when I say Unity, I understand that it is using the .NET backend, however it is currently the only bit of software that rejects the SSL certificate (if that is what it is doing).
     
  11. kburgin1

    kburgin1

    Joined:
    Jul 10, 2019
    Posts:
    10
    I have now gone through the "Report a Bug" form and submitted a sample application.

    I also tried configuring nginx to use default TLS settings (TLS 1, 1.1, or 1.2), to no avail.