We are unable to switch our local development stack to HTTPS due to errors with our Unity app. We have:
- Created a local Certificate Authority (ca.crt),
- Signed a new localhost certificate with the CA,
- Installed the ca.crt into the Windows Trusted Root Authority Store,
- 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:
public class BypassCertificate : CertificateHandler
{
protected override bool ValidateCertificate(byte[] certificateData)
{
//Simply return true no matter what
return true;
}
}
public class MyThing : MonoBehaviour
{
private void Start()
{
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => true;
string uri = "https://localhost:443/ping";
UnityWebRequest www =
new UnityWebRequest(uri, "GET") {certificateHandler = new BypassCertificate()};
try
{
await www.SendWebRequest();
}
catch (Exception e)
{
Debug.LogException(e);
}
}
}
We get the following errors:
Curl error 51: Cert verify failed: UNITYTLS_X509VERIFY_FLAG_UNKNOWN_ERROR
UnityWebRequestException: Unknown Error
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.
Which version of TLS do you use?
Unity supports up to TLSv1.2
1 Like
We’re using TLS 1.2. Here’s a cut down nginx config:
worker_processes 1;
events { worker_connections 1024; }
http {
sendfile on;
upstream docker-api {
server backend:80; # points at the backend docker container
}
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_ssl_session_reuse off;
ssl_certificate /etc/ssl/certs/nginx.crt;
ssl_certificate_key /etc/ssl/private/nginx.key;
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:smile:HE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:smile:HE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name localhost;
location / {
proxy_pass http://docker-api;
proxy_redirect off;
proxy_ssl_session_reuse off;
}
}
}
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:
var path = @"C:\Users\x\Documents\y\ca.crt"; //Path to root certificate
using (X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) {
store.Open(OpenFlags.ReadWrite);
store.Add(new X509Certificate2(X509Certificate2.CreateFromCertFile(path))); //where cert is an X509Certificate object
}
Interestingly, when I try the .NET HttpClient snippet below:
try
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Debug.Log(responseBody);
}
catch(HttpRequestException e)
{
Debug.LogException(e);
}
I get the following error:
TlsException: Handshake failed - error code: UNITYTLS_INTERNAL_ERROR, verify result: UNITYTLS_X509VERIFY_FLAG_UNKNOWN_ERROR
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)
Mono.Unity.UnityTlsContext.ProcessHandshake () (at <14e3453b740b4bd690e8d4e5a013a715>:0)
Mono.Net.Security.MobileAuthenticatedStream.ProcessHandshake (Mono.Net.Security.AsyncOperationStatus status) (at <14e3453b740b4bd690e8d4e5a013a715>:0)
(wrapper remoting-invoke-with-check) Mono.Net.Security.MobileAuthenticatedStream.ProcessHandshake(Mono.Net.Security.AsyncOperationStatus)
Mono.Net.Security.AsyncHandshakeRequest.Run (Mono.Net.Security.AsyncOperationStatus status) (at <14e3453b740b4bd690e8d4e5a013a715>:0)
Mono.Net.Security.AsyncProtocolRequest+<ProcessOperation>d__24.MoveNext () (at <14e3453b740b4bd690e8d4e5a013a715>:0)
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.
It seems very similar to this issue: UnityWebRequest - Unable to complete SSL connection
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.
Yep, that’s just for debugging. We wouldn’t actually be doing that in production. Just thought extra error output would help.
Restarted Unity Editor as well as the Windows 10 machine, still same errors.
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.
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).
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.