When I access https://example.com
on my company's laptop that has a X509 client certificate installed, using my home internet connection, without using a VPN, the requested page is signed using the company's Root CA (which was distributed to my company's laptop through group policies I assume).
When I access https://example.com
on my private laptop that has no client certificate installed, with the same connection, I get an access error (403) instead with a Cloudflare logo on it (the address bar is still https://example.com
). That error is expected because I have no client certificate. Interestingly, the error page is signed by Cloudflare's own intermediate CA.
How does Cloudflare switch server certificates during TLS handshake depending on the existence of a client certificate? The server certificate is sent before the client certificate. It is brilliant how this all works, but I don't get how they managed to implement it.
What seems to make a difference is the TLS version. If I use a browser without TLS 1.3, all content is signed with the Cloudflare certificate, regardless if I have a client certificate or not. Is there a certificate renegotiation protocol in 1.3? (I can't find the specs)
Company Computer (connected to same ISP, VPN off)
curl -v https://somecompany.com > page.html
* Rebuilt URL to: https://somecompany.com/
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying xx.xx.xx.xx...
* TCP_NODELAY set
* Connected to somecompany.com (xx.xx.xx.xx) port 443 (#0)
* schannel: SSL/TLS connection with somecompany.com port 443 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 183 bytes...
* schannel: sent initial handshake data: sent 183 bytes
* schannel: SSL/TLS connection with somecompany.com port 443 (step 2/3)
* schannel: failed to receive handshake, need more data
* schannel: SSL/TLS connection with somecompany.com port 443 (step 2/3)
* schannel: encrypted data got 3148
* schannel: encrypted data buffer: offset 3148 length 4096
* schannel: sending next handshake data: sending 3501 bytes...
* schannel: SSL/TLS connection with somecompany.com port 443 (step 2/3)
* schannel: encrypted data got 3394
* schannel: encrypted data buffer: offset 3394 length 4096
* schannel: SSL/TLS handshake complete
* schannel: SSL/TLS connection with somecompany.com port 443 (step 3/3)
* schannel: stored credential handle in session cache
> GET / HTTP/1.1
> Host: somecompany.com
> User-Agent: curl/7.55.1
> Accept: */*
>
* schannel: client wants to read 102400 bytes
* schannel: encdata_buffer resized 103424
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: encrypted data got 37765
* schannel: encrypted data buffer: offset 37765 length 103424
* schannel: decrypted data length: 1369
* schannel: decrypted data added: 1369
* schannel: decrypted data cached: offset 1369 length 102400
(... more similar stuff here, encrypted data/decrypted data...)
< HTTP/1.1 200 OK
(...)
Private Computer (connected to same ISP)
curl -v https://somecompany.com > page.html
* Rebuilt URL to: https://somecompany.com/
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying xx.xx.xx.xx...
* TCP_NODELAY set
* Connected to somecompany.com (xx.xx.xx.xx) port 443 (#0)
* schannel: SSL/TLS connection with somecompany.com port 443 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 183 bytes...
* schannel: sent initial handshake data: sent 183 bytes
* schannel: SSL/TLS connection with somecompany.com port 443 (step 2/3)
* schannel: failed to receive handshake, need more data
* schannel: SSL/TLS connection with somecompany.com port 443 (step 2/3)
* schannel: encrypted data got 3148
* schannel: encrypted data buffer: offset 3148 length 4096
* schannel: a client certificate has been requested
* schannel: SSL/TLS connection with somecompany.com port 443 (step 2/3)
* schannel: encrypted data buffer: offset 3148 length 4172
* schannel: sending next handshake data: sending 100 bytes...
* schannel: SSL/TLS connection with somecompany.com port 443 (step 2/3)
* schannel: encrypted data got 258
* schannel: encrypted data buffer: offset 258 length 4172
* schannel: SSL/TLS handshake complete
* schannel: SSL/TLS connection with somecompany.com port 443 (step 3/3)
* schannel: stored credential handle in session cache
> GET / HTTP/1.1
> Host: somecompany.com
> User-Agent: curl/7.55.1
> Accept: */*
>
* schannel: client wants to read 102400 bytes
* schannel: encdata_buffer resized 103424
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: encrypted data got 12572
* schannel: encrypted data buffer: offset 12572 length 103424
* schannel: decrypted data length: 1369
* schannel: decrypted data added: 1369
* schannel: decrypted data cached: offset 1369 length 102400
(... more similar stuff here, encrypted data/decrypted data...)
< HTTP/1.1 403 Forbidden
(...)