title: "T04. mTLSを設定する" order: 45
通常のTLSはサーバー証明書だけを検証しますが、mTLS(mutual TLS、相互TLS)ではクライアントも証明書を提示し、サーバーがそれを検証します。API間通信のゼロトラスト化や、社内システムの認証でよく使われるパターンです。
SSLServerのコンストラクタ第3、第4引数に、クライアント証明書を検証するためのCA証明書を渡します。
httplib::SSLServer svr(
"server-cert.pem", // サーバー証明書
"server-key.pem", // サーバー秘密鍵
"client-ca.pem", // クライアント証明書を検証するCA
nullptr // CAディレクトリ(省略)
);
svr.Get("/", [](const httplib::Request &req, httplib::Response &res) {
res.set_content("authenticated", "text/plain");
});
svr.listen("0.0.0.0", 443);
この設定だと、クライアント証明書がclient-ca.pemで署名されていない接続はハンドシェイクの段階で拒否されます。ハンドラまで到達した時点で、クライアントはすでに認証済みです。
httplib::SSLServer::PemMemory pem{};
pem.cert_pem = server_cert.data();
pem.cert_pem_len = server_cert.size();
pem.key_pem = server_key.data();
pem.key_pem_len = server_key.size();
pem.client_ca_pem = client_ca.data();
pem.client_ca_pem_len = client_ca.size();
httplib::SSLServer svr(pem);
環境変数やシークレットマネージャから読み込む場合はこちらが便利です。
クライアント側では、SSLClientのコンストラクタにクライアント証明書と秘密鍵を渡します。
httplib::SSLClient cli("api.example.com", 443,
"client-cert.pem",
"client-key.pem");
auto res = cli.Get("/");
ClientではなくSSLClientを直接使う点に注意してください。秘密鍵にパスワードがある場合は第5引数で渡せます。
クライアント側にも同じPemMemory構造体があり、メモリ上のPEMからクライアント証明書を設定できます。
httplib::SSLClient::PemMemory pem{};
pem.cert_pem = client_cert.data();
pem.cert_pem_len = client_cert.size();
pem.key_pem = client_key.data();
pem.key_pem_len = client_key.size();
httplib::SSLClient cli("api.example.com", 443, pem);
auto res = cli.Get("/");
WebSocketクライアント(
wss://)でmTLSを使う場合はW05. wss接続でTLSを設定するを参照してください。
ハンドラの中で、どのクライアントが接続してきたかを確認したいときはreq.peer_cert()を使います。詳しくはT05. サーバー側でピア証明書を参照するを参照してください。
Note: クライアント証明書の発行と失効管理は、普通のパスワード認証より運用コストが高いです。内部PKIを回すか、ACME(Let's Encryptなど)系のツールで自動化する体制が必要です。