先日リリースされたNode v12はTLS v1.3に対応しているらしい。というわけで早速試してみた。
サーバ証明書の作成
まずテスト用に自己署名サーバ証明書を作成する。ここでは楕円曲線secp521r1*1のecdsaの秘密鍵を作成した。鍵を作成できるサブコマンドは何種類かあるようだが、今は genpkey サブコマンドが推奨されているらしい。
openssl genpkey -algorithm EC -out server.key -pkeyopt ec_paramgen_curve:secp521r1
次に作成した秘密鍵からCSRを作成する。各種パラメータの入力を求められるが、Common Name を localhost として、それ以外は適当に入力しておく。
openssl req -new -key server.key -out server.csr
CSRに自己署名する。自己署名の場合 -signkey で秘密鍵を指定するらしい。
openssl x509 -req -signkey server.key -days 30 -sha256 -in server.csr -out server.pem
server.pemが証明書、server.keyが秘密鍵。
テストサーバ実装
Node.jsでTLS v1.3対応のhttpsサーバを書く。ドキュメントのサンプルコード通りで問題ないが多少手を加えている。
const https = require("https"); const fs = require('fs'); const options = { key: fs.readFileSync('server.key'), cert: fs.readFileSync('server.pem'), maxVersion: 'TLSv1.3', minVersion: 'TLSv1.3', ciphers: 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256' }; https.createServer(options, (req, res) => { res.writeHead(200, {"Content-Type": "text/html"}); res.end('<html>Hello TLS 1.3</html>\n'); }).listen(8443, (err) => { if (err) { console.log(err); process.exit(1); } console.log("listen 8443") });
optionsのkeyに先ほど作成した秘密鍵、certにサーバ証明書を指定する。TLSv1.3のみで接続するため、maxVersionとminVersionにTLSv1.3を指定する。minVersionのデフォルトはTLSv1.2、maxVersionはTLSv1.3らしい。ciphersには以下の3つの暗号スイートを指定した。TLS v1.3で使用できるのはこの3つしかない。
CHACHA20_POLY1305という暗号はあまり聞きなれないが、暗号界隈のスーパーヒーロー、ダニエル・バーンスタインが開発し、Googleが一押ししているストリーム暗号である。
接続
最新のFirefox(ChromeやSafariで使えない楕円曲線で証明書を作ってしまったのでFirefoxのみ・・・)であればTLS v1.3で接続できるが、ブラウザで見ても違いがわからないのでopensslコマンドで接続
openssl s_client -connect localhost:8443 -tls1_3
以下のような出力があり、TLSv1.3で接続できているらしいことが確認できる
Post-Handshake New Session Ticket arrived: SSL-Session: Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384
上記コマンドはTLSハンドシェイクしか行わず、HTTPリクエストは送られていない。入力待ちになっているのでGET /
とタイプし、改行を2回打ってHTTPリクエストを送ろう。
GET / HTTP/1.1 200 OK Content-Type: text/html Date: Sun, 28 Apr 2019 16:55:36 GMT Connection: close <html>Hello TLS 1.3</html> closed
HTTPレスポンスが得られました。