javascriptopensslssl-certificatedenoquic

Why WebTransport cannot connect to my server?


When I'm trying to make a simple QUIC connection via new WebTransport('https://localhost') this request never reaches my server and immediately terminating in Chrome with Failed to establish a connection to https://localhost/: net::ERR_CONNECTION_RESET. and Uncaught (in promise) WebTransportError: Opening handshake failed. console messages.

In Firefox things slightly different and request do not terminating immediately but after some time (approximately 10sec) I'm getting instance of WebTransporError in the console with a message: WebTransport connection rejected but yet again no signs of this request reaching my server.


So at this point I can think of two different sources for this problem: my SSL and my server code. Let's start with the former. As a server I'm using Deno under the --unstable-net flag with this simplified code:

const key = Deno.readTextFileSync('./SSL/Self-signed.key'),
cert = Deno.readTextFileSync('./SSL/Self-signed.crt')

Deno.serve({ port: 443, key, cert }, ()=>new Response('Hello World!'))

const endpoint = new Deno.QuicEndpoint,
listener = endpoint.listen({ alpnProtocols: ['h2', 'h3', 'idc'], key, cert }),
conn = await listener.incoming()
console.log('New connection: ', conn)

This code works for the purpose of serving HTTPS connections because I'm getting this Hello World! message on my browser screen when navigate to https://localhost. But the final line of the code is never reached which means that this code fails for the purpose of serving QUIC connections. I can't really tell where the problem might be because I've simplified this code as far as possible.


And now for certificates. Im working in Windows and generating self-signed certificates through this handwritten batch file that is again simplified and now also commented for the research purpose:

@echo off
set OPENSSL_HOME=%~dp0openssl-3
set OPENSSL_CONF=%OPENSSL_HOME%/ssl/openssl.cnf
PATH=%PATH%;"%OPENSSL_HOME%/x64/bin"

::Generate signatory ECDSA key
openssl ecparam -genkey -out "%~dp0rootKey.key" -name prime256v1

::Generate authority from key
openssl req -x509 -new -nodes -key "%~dp0rootKey.key" -days 13 -out "%~dp0rootCA.pem" -subj "/C=UA/ST=UA/O=UA"

::Adding authority to the Windows trust store so that browsers can obey
certutil -addstore -f -enterprise -user root "%~dp0rootCA.pem"

::Generate new sign request and public key
openssl req -new -nodes -out "%~dp0signRequest.csr" -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -keyout "%~dp0/../Self-signed.key" -subj "/C=UA/ST=UA/O=UA"

::Generate new self-signed ECDSA certificate
openssl x509 -req -in "%~dp0signRequest.csr" -CA "%~dp0rootCA.pem" -CAkey "%~dp0rootKey.key" -out "%~dp0/../Self-signed.crt" -days 13 -extfile "%~dp0extfile"

::Content of the "extfile":

::authorityKeyIdentifier=keyid,issuer
::basicConstraints=CA:FALSE
::keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment
::subjectAltName=IP:127.0.0.1,DNS:localhost,DNS:*.localhost

::Saving certificates HASH
openssl x509 -noout -fingerprint -sha256 -noout -in "%~dp0/../Self-signed.crt">"%~dp0HASH"
set /p fp=<"%~dp0HASH"
echo %fp:~19%>"%~dp0HASH"

pause

With this pair of key and certificate im able to serve HTTPS connections without adding any flags to the browsers but no luck for QUIC connections. On MDN page about WebTransport there is a list of requirements for a certificate:

The certificate must be an X.509v3 certificate that has a validity period of less that 2 weeks, and the current time must be within that validity period. The format of the public key in the certificate depends on the implementation, but must minimally include ECDSA with the secp256r1 (NIST P-256) named group, and must not include RSA keys. An ECSDA key is therefore an interoperable default public key format.

So i can assume that my certificate should be passable under those requirements but still it seems like browsers are refusing to establishing QUIC connection with my server despite having no problems with HTTPS ones.


As my last resort i tried this serverCertificateHashes option for the WebTransport instance. Im not really sure what this option doing and especially what am i doing but i found this piece of code below somewhere in the internet and combine it with my certificate hash which was produced by the last command of my batch file and throw it all into the browsers console for this final picture:

function base64ToArrayBuffer(base64) {
  var binaryString = atob(base64);
  var bytes = new Uint8Array(binaryString.length);
  for (var i = 0; i < binaryString.length; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  return bytes.buffer;
}
new WebTransport('https://localhost', {
  serverCertificateHashes: [
    {
      algorithm: 'sha-256',
      value: base64ToArrayBuffer(btoa(`92:49:72:24:FB:2D:84:D2:0B:5B:A0:1A:F3:0A:6D:60:B5:E2:12:25:7D:2B:60:47:FF:DD:BF:32:33:57:60:53`))
    }
  ]
});

But yet again no luck here. Results are exactly the same in both browsers. What can I try next?


Solution

  • It seems like i found the problem. One just always should mention a port number upon construction of Deno.QuicEndpoint despite it being an optional option. So upon construction with the port number 443 like this new Deno.QuicEndpoint({port:443}), any HTTPS requests through WebTransport will reach their destination automatically just like regular HTTPS requests and it seems that HTTPS server and QUIC one do not even block each other when they are hosted on the same port number.