TLSクライアントの動作確認や、証明書の設定確認のために、手元にTLSサーバーを立てたいときは、openssl s_serverを使う。Nginxなどのサーバーソフトを設定しなくても、証明書と秘密鍵の2つのファイルだけでHTTPSサーバーを起動できる。
$ openssl s_server -accept 4433 -cert server.crt -key server.key -www
-acceptで待ち受けるポート、-certと-keyでサーバー証明書と秘密鍵を指定する。-wwwを付けると、接続したクライアントに、接続情報を表示するHTTPのページを返す。
別のターミナルから接続して確認する。
$ curl --cacert server.crt https://localhost:4433/
<HTML><BODY BGCOLOR="#ffffff">
<pre>
s_server -accept 4433 -cert server.crt -key server.key -www
Secure Renegotiation IS supported
...
実行例はUbuntu 24.04のOpenSSL 3.0.13のもの。証明書は、localhost用の自己署名の証明書を使った。
参考: 【openssl】自己署名の証明書を作成する (req -x509)
s_clientで接続して確認する
openssl s_clientで接続すると、サーバー証明書の検証結果や、ネゴシエーションされたTLSバージョンを確認できる。
$ echo | openssl s_client -connect localhost:4433 -CAfile server.crt -verify_hostname localhost -brief
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: CN = localhost
...
Verification: OK
自己署名の証明書を信頼するために、-CAfileで証明書自身を指定している。
-wwwのページは、s_clientからHTTPリクエストを送っても取得できる。
$ printf 'GET / HTTP/1.0\r\n\r\n' | openssl s_client -connect localhost:4433 -quiet 2>/dev/null
HTTP/1.0 200 ok
Content-type: text/html
...
参考: 【openssl】SNIやTLSバージョンを指定してサーバーに接続する (s_client)
証明書を信頼していないクライアントの動作を確認する
CA証明書を渡さずに接続すると、クライアント側で検証に失敗する。curlは、自己署名の証明書を拒否する。
$ curl https://localhost:4433/
curl: (60) SSL certificate problem: self-signed certificate
s_serverの側には、クライアントが証明書を信頼しなかった理由が、アラートとして表示される。
ACCEPT
...error:0A000418:SSL routines:ssl3_read_bytes:tlsv1 alert unknown ca:...:SSL alert number 48
tlsv1 alert unknown caは、クライアントが証明書の発行者(CA)を信頼していないことを表す。クライアントの信頼設定が原因の問題を切り分けるときに、このメッセージが手がかりになる。
curl -kで検証を無効にすると、接続できる。
$ curl -sk -o /dev/null -w '%{http_code}\n' https://localhost:4433/
200
-kは、ほかの原因を切り分けるための一時的な確認にとどめる。
ファイルを配信する
-wwwの代わりに-WWWを指定すると、カレントディレクトリのファイルを配信する。
$ mkdir web && cd web
$ echo '<h1>hello</h1>' > index.html
$ echo 'file content' > a.txt
$ openssl s_server -accept 4434 -cert ../server.crt -key ../server.key -WWW
$ curl --cacert server.crt https://localhost:4434/a.txt
file content
存在しないファイルを要求すると、エラー内容が本文に入った応答が返る。ステータスコードは404ではなく、200になる。
$ curl -i --cacert server.crt https://localhost:4434/nofile.txt
HTTP/1.0 200 ok
Content-type: text/plain
Error opening 'nofile.txt' mode='r'
s_serverは、検証のための簡易なサーバーである。Webサーバーとしての機能は不足しており、本番の用途には使わない。カレントディレクトリのファイルをそのまま配信するため、秘密鍵や機密ファイルがあるディレクトリでは-WWWを使わない。
TLSのバージョンや暗号スイートを制限する
s_serverにも、s_clientと同じように、バージョンや暗号スイートのオプションがある。-tls1_2を指定すると、TLS 1.2だけを受け付ける。
$ openssl s_server -accept 4437 -cert server.crt -key server.key -tls1_2 -www
TLS 1.3で接続しようとするクライアントは、接続に失敗する。
$ openssl s_client -connect localhost:4437 -CAfile server.crt -tls1_3 -brief
error:0A00042E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version:...:SSL alert number 70
$ openssl s_client -connect localhost:4437 -CAfile server.crt -brief 2>&1 | grep 'Protocol version'
Protocol version: TLSv1.2
特定のバージョンに対応していないクライアントの動作を確認する場合に使える。
ALPNを設定する
-alpnで、サーバーが受け付けるプロトコルを指定する。クライアントが提示したプロトコルのうち、サーバーの指定と一致するものが選ばれる。
$ openssl s_server -accept 4438 -cert server.crt -key server.key -alpn h2,http/1.1 -www
$ echo | openssl s_client -connect localhost:4438 -CAfile server.crt -alpn http/1.1 2>&1 | grep ALPN
ALPN protocol: http/1.1
クライアント証明書を要求する
-Verifyを指定すると、クライアントに証明書を要求して、検証する。数字は、許可する証明書チェーンの深さである。-CAfileで、クライアント証明書を発行したCAの証明書を渡す。自己署名のクライアント証明書の場合は、その証明書自身を渡す。
$ openssl s_server -accept 4442 -cert server.crt -key server.key -Verify 1 -CAfile client.crt -www
クライアント証明書を提示しないと、TLS 1.2では接続に失敗する。
$ openssl s_client -connect localhost:4442 -CAfile server.crt -tls1_2 -brief
error:0A000410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:...:SSL alert number 40
-certと-keyで、クライアント証明書と秘密鍵を提示すると、接続できる。
$ openssl s_client -connect localhost:4442 -CAfile server.crt -cert client.crt -key client.key -brief
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
...
TLS 1.3では、クライアント証明書を提示しなくても、s_client側ではCONNECTION ESTABLISHEDと表示される。TLS 1.3では、ハンドシェイクの完了後にクライアント証明書の検証が行われるためである。証明書が原因で拒否されたかどうかは、s_serverのログで確認する。
verify depth is 1, must return a certificate
...error:0A0000C7:SSL routines:tls_process_client_certificate:peer did not return a certificate:...
起動時のエラー
ポートが使用中
指定したポートをほかのプロセスが使用している場合は、起動に失敗する。
$ openssl s_server -accept 4439 -cert server.crt -key server.key -www
...error:80000062:system library:BIO_bind:Address already in use:...
別のポートを指定するか、使用中のプロセスを終了する。
証明書と秘密鍵が対応していない
証明書と秘密鍵が対応していない場合は、起動時にエラーで終了する。
$ openssl s_server -accept 4440 -cert server.crt -key other.key -www
error setting private key
...error:05800074:x509 certificate routines:X509_check_private_key:key values mismatch:...
参考: 【openssl】秘密鍵と証明書が対応しているか確認する (pkey -pubout)
1回の接続で終了する
-naccept 1を付けると、1回の接続を処理して終了する。スクリプトの中で、一時的にサーバーを立てたい場合に使う。
$ openssl s_server -accept 4441 -cert server.crt -key server.key -www -naccept 1
対話的にやり取りする
-wwwなどを指定しない場合、s_serverは接続したクライアントとの間で、文字列をやり取りする。クライアントから送った文字列は、s_serverの標準出力に表示される。
$ echo 'hello from client' | openssl s_client -connect localhost:4436 -CAfile server.crt -quiet
s_serverの出力に、hello from clientと表示される。
バージョンによる違い
OpenSSL 3.6でも、-wwwによる配信と、-tls1_2で制限した場合のエラーは同じである。
macOS標準の/usr/bin/openssl(LibreSSL 3.3.6)でも、-wwwや-WWWで起動して、s_clientやcurlで接続できる。ただし、s_serverのオプションはOpenSSLと完全には一致しない。使うオプションは、openssl s_server -helpで確認する。
