使用TLS 1.3遇到的几个问题

手头上有台服务器,想使用TLS 1.3,配置好了Nginx,浏览器也升级到了最新版本,但是一直无法使用TLS 1.3作为默认TLS版本,前前后后花了点时间解决,解决的同时也了解了下相关的知识,这里记录一下。

无法使用TLS 1.3协议

根据发行日志,Nginx自从1.13开始就支持Nginx 1.13,但是我服务器上安装了Nginx 1.14.2版本,配置文件也开启了TLSv1.3,但是在最新版的Chrome的Security选项卡界面还是显示使用的是TLS 1.2,如下:
chrome-security-tls1.2-tls1.3
其中有一段内容:

The connection to this site is encrypted and authenticated using TLS 1.2, ECDHE_RSA with X25519, and AES_128_GCM.

说明还是使用的TLS 1.2,但是我Nginx配置文件明明是启用了TLS 1.3,配置如下:

        ssl on;
        ssl_certificate /path/ssl/cloudbool-ecc.crt;
        ssl_certificate_key /path/ssl/cloudbool-ecc.key;
        ssl_session_cache        shared:SSL:10m;
        ssl_session_timeout      10m;
        ssl_session_tickets      on;
        resolver                 119.29.29.29 223.5.5.5  valid=5s;
        resolver_timeout         10s;
        ssl_session_cache builtin:1000 shared:SSL:10m;
        ssl_dhparam /path/ssl/dhparam.pem;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_stapling on;
        ssl_stapling_verify on;
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

百思不得其解的时候,翻了下OpenSSL的文档,介绍TLS 1.3的文档有句话如下:

The OpenSSL git master branch (and the 1.1.1-pre9 beta version) contain our development TLSv1.3 code which is based on the final version of RFC8446 and can be used for testing purposes

意思是,从1.1.1-pre9版才包含TLS 1.3最终版相关的开发代码,那如果要开启TLS 1.3,那编译Nginx时使用的OpenSSL版本必须是1.1.1-pre9版本之后的了,查看Nginx的编译信息:

root@cloudbool:~# nginx -V
nginx version: nginx/1.14.2
built by gcc 6.3.0 20170516 (Debian 6.3.0-18+deb9u1)
built with OpenSSL 1.1.0j  20 Nov 2018
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fdebug-prefix-map=/root/new/nginx_rebulid/nginx-1.14.2=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC -Wno-error' --with-ld-opt='-specs=/usr/share/dpkg/no-pie-link.specs -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie' --add-module=/usr/local/src/ngx_http_google_filter_module --add-module=/usr/local/src/ngx_http_substitutions_filter_module --add-module=/usr/local/src/ngx_cache_purge

看来问题就是出在OpenSSL版本上,这台服务器所使用的Nginx的openssl版本还是1.1.0,所以默认在chrome下不会启用TLS 1.3进行连接。解决办法也很简单,只需要升级下OpenSSL的版本呢并重新编译一下Nginx就行。
升级OpenSSL之后,重新用Chrome打开发现成功启用了TLS 1.3,如图所示:

解决问题的过程中,了解到其实除了浏览器,我们也可以使用curl和openssl对服务器进行TLS 1.3通信,下面是一些相关命令。

curl使用TLS 1.3

根据curl作者的一篇关于TLS 1.3的博客,curl从7.54.0版本开始支持使用TLS1.3进行请求,但是我在macOS使用brew安装的curl进行TLS 1.3请求时,curl报错如下:

➜  ~ curl -Ivvv --tlsv1.3 https://cloudbool.com
curl: (4) LibreSSL was built without TLS 1.3 support

而在Deiban 9下的情况是:

zoco@DebianLocal:~$ curl --tlsv1.3 https://cloudbool.com
curl: (4) OpenSSL was built without TLS 1.3 support
zoco@DebianLocal:~$ curl -V
curl 7.52.1 (x86_64-pc-linux-gnu) libcurl/7.52.1 OpenSSL/1.0.2r zlib/1.2.8 libidn2/0.16 libpsl/0.17.0 (+libidn2/0.16) libssh2/1.7.0 nghttp2/1.18.1 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL

为了方便,我是直接使用了Debian 10系统下安装的curl,结果如下:

zoco@Debian10:~$ curl --tlsv1.3 -Ivvv https://cloudbool.com
...
*   Trying 111.231.xxx.xxx...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x55d6f358cdd0)
* Connected to cloudbool.com (111.231.xxx.xxx) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: OU=Domain Control Validated; OU=PositiveSSL; CN=cloudbool.com
*  start date: Mar 20 00:00:00 2019 GMT
*  expire date: Mar 19 23:59:59 2020 GMT
*  subjectAltName: host "cloudbool.com" matched cert's "cloudbool.com"
*  issuer: C=GB; ST=Greater Manchester; L=Salford; O=Sectigo Limited; CN=Sectigo ECC Domain Validation Secure Server CA
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x55d6f358cdd0)
> HEAD / HTTP/2
> Host: cloudbool.com
> User-Agent: curl/7.64.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
HTTP/2 200
< server: nginx
server: nginx
< date: Fri, 19 Apr 2019 03:53:09 GMT
date: Fri, 19 Apr 2019 03:53:09 GMT
< content-type: text/html; charset=UTF-8
content-type: text/html; charset=UTF-8
< vary: Accept-Encoding, Cookie
vary: Accept-Encoding, Cookie
< cache-control: max-age=3, must-revalidate
cache-control: max-age=3, must-revalidate
< strict-transport-security: max-age=63072000; includeSubDomains; preload
strict-transport-security: max-age=63072000; includeSubDomains; preload
<
* Connection #0 to host cloudbool.com left intact

如果需要指定cipher,可以加上–ciphers参数或者–tls13-ciphers
经过我的实际测试,如果curl支持TLS 1.3,默认就是使用TLS1.3进行请求。

OpenSSL使用TLS 1.3进行请求

除了使用curl,其实使用OpenSSL也是可以直接发起TLS1.3请求的,命令格式如下:

openssl s_client ip:port [-cipher ‘cipher_name’]

举例如下:

zoco@Debian10:~$ openssl s_client cloudbool.com:443
CONNECTED(00000003)
depth=2 C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust ECC Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = Sectigo Limited, CN = Sectigo ECC Domain Validation Secure Server CA
verify return:1
depth=0 OU = Domain Control Validated, OU = PositiveSSL, CN = cloudbool.com
verify return:1
...
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2451 bytes and written 384 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: 6DBC42697BE57168F47532E8116A0222C44B21D8C00B5AAFD90E5DE7940DFBB8
    Session-ID-ctx:
    Resumption PSK: B8107D92D2CDC418FEAC15BA3F7482A0F4087FFB1239E3C89C1CCE00378D8E3A3BF764F5A45709536066B8C33E13A957
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 600 (seconds)
    TLS session ticket:
...
    Start Time: 1555646308
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: 4A3B6BA3FCBEFB6E180F441FDF0E58DB9BDC621621915411AAE5D78947B16A3F
    Session-ID-ctx:
    Resumption PSK: 3CFA3B3D78D7EC579E5C5A21BE5E4531DF4FF5ACDD70E95DCF821775A069742FCF0CA679029F89C838594AC10249FCBC
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 600 (seconds)
    TLS session ticket:
...
    Start Time: 1555646308
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
r
HTTP/1.1 400 Bad Request
Server: nginx
Date: Fri, 19 Apr 2019 03:58:32 GMT
Content-Type: text/html
Content-Length: 166
Connection: close
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
<html>
<head><title>400 Bad Request</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx</center>
</body>
</html>
read:errno=0