テスト用のCA認証局をDockerで作ってテストしてみたいです。手順を教えて下さい

了解しました。修正後(server.conf/etc/nginx/conf.d/default.conf にマウント)の前提で、最初から通しで動く手順をまとめ直します。最小構成の“OpenSSL製ローカルCA(Docker)”でサーバ証明書・クライアント証明書を発行し、Nginx で TLS と(任意の)mTLS を確認します。


0) 前提

  • ホスト:Linux/WSL/PowerShell いずれでも可(以下は Linux/WSL 体裁)
  • 作業ディレクトリ:~/ca-docker
mkdir -p ~/ca-docker && cd ~/ca-docker

1) ファイル一式の作成

1-1) Dockerfile(CA用コンテナ)

# ./Dockerfile
FROM alpine:3.20
RUN apk add --no-cache bash openssl coreutils
WORKDIR /ca

1-2) docker-compose.yml

# ./docker-compose.yml
services:
  ca:
    build: .
    container_name: local-openssl-ca
    working_dir: /ca
    command: bash -lc "sleep infinity"
    volumes:
      - ./ca:/ca   # 生成物をホストへ

  nginx:
    image: nginx:1.27-alpine
    container_name: mtls-nginx
    depends_on: []
    ports:
      - "8443:8443"
    volumes:
      - ./server.conf:/etc/nginx/conf.d/default.conf:ro
      - ./ca/certs:/certs:ro
      - ./ca/private:/private:ro

1-3) Nginx サーバ設定(conf.d 用に server ブロックのみ)

# ./server.conf
server {
  listen 8443 ssl;
  ssl_certificate     /certs/server.local.cert.pem;
  ssl_certificate_key /private/server.local.key.pem;

  # mTLS(クライアント証明書)検証:optional(= 任意)。必須化は 'on'
  ssl_client_certificate /certs/ca.cert.pem;
  ssl_verify_client optional;

  ssl_protocols TLSv1.2 TLSv1.3;

  location / {
    add_header Content-Type text/plain;
    return 200 "OK (mTLS optional)\n";
  }
}

1-4) OpenSSL 設定(CA用)

# ./ca/openssl.cnf
[ ca ]
default_ca = CA_default

[ CA_default ]
dir               = /ca
certs             = $dir/certs
crl_dir           = $dir/crl
new_certs_dir     = $dir/newcerts
database          = $dir/index.txt
serial            = $dir/serial
crlnumber         = $dir/crlnumber
RANDFILE          = $dir/private/.rand
private_key       = $dir/private/ca.key.pem
certificate       = $dir/certs/ca.cert.pem
default_md        = sha256
default_days      = 825
policy            = policy_loose
email_in_dn       = no
unique_subject    = no
copy_extensions   = copy
crl               = $dir/crl/ca.crl.pem

[ policy_loose ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied

[ req ]
default_bits       = 4096
distinguished_name = req_distinguished_name
string_mask        = utf8only
default_md         = sha256
x509_extensions    = v3_ca

[ req_distinguished_name ]
commonName = Common Name (eg, your name)

[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, keyCertSign, cRLSign

[ v3_server ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

[ v3_client ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth

1-5) サーバ証明書の CSR 用設定(SAN はここで指定)

# ./ca/server.req.cnf
[ req ]
distinguished_name = dn
req_extensions = v3_req
prompt = no

[ dn ]
CN = localhost

[ v3_req ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = localhost
IP.1  = 127.0.0.1

メモ:openssl.cnf[CA_default] copy_extensions = copy により、CSR 側(server.req.cnf)の SAN が証明書へコピーされます。SAN を変える場合はこのファイルを編集します。


2) CA コンテナ起動&CA 初期化

docker compose up -d ca
docker compose exec ca bash

(以降、CA コンテナ内

set -eux
mkdir -p private certs csr newcerts crl
chmod 700 private
: > index.txt
echo 1000 > serial
echo 1000 > crlnumber

3) ルートCA 作成(自己署名)

# ルートCA鍵
openssl genrsa -out private/ca.key.pem 4096
chmod 600 private/ca.key.pem

# ルートCA証明書(10年)
openssl req -config openssl.cnf \
  -key private/ca.key.pem \
  -new -x509 -days 3650 -sha256 \
  -subj "/CN=Local Test Root CA" \
  -extensions v3_ca \
  -out certs/ca.cert.pem

# 確認
openssl x509 -in certs/ca.cert.pem -noout -text | head -n 20

4) サーバ証明書の発行(SAN= localhost / 127.0.0.1)

# 秘密鍵
openssl genrsa -out private/server.local.key.pem 2048
chmod 600 private/server.local.key.pem

# CSR(SAN は server.req.cnf)
openssl req -new -key private/server.local.key.pem \
  -out csr/server.local.csr.pem \
  -config server.req.cnf

# 署名(server 用拡張)
openssl ca -batch -config openssl.cnf \
  -extensions v3_server -days 825 -notext -md sha256 \
  -in csr/server.local.csr.pem \
  -out certs/server.local.cert.pem

# 検証
openssl verify -CAfile certs/ca.cert.pem certs/server.local.cert.pem

5) クライアント証明書の発行(curl/ブラウザ用)

# 秘密鍵
openssl genrsa -out private/client1.key.pem 2048
chmod 600 private/client1.key.pem

# CSR
openssl req -new -key private/client1.key.pem \
  -out csr/client1.csr.pem \
  -subj "/CN=client1"

# 署名(client 用拡張)
openssl ca -batch -config openssl.cnf \
  -extensions v3_client -days 825 -notext -md sha256 \
  -in csr/client1.csr.pem \
  -out certs/client1.cert.pem

# PKCS#12(ブラウザ取込用。パスワードなし)
openssl pkcs12 -export -inkey private/client1.key.pem \
  -in certs/client1.cert.pem -certfile certs/ca.cert.pem \
  -out certs/client1.p12 -password pass:

CA コンテナから抜ける:

exit

6) Nginx 起動(TLS / mTLS optional)

docker compose up -d nginx
docker logs mtls-nginx --tail=50

7) 動作確認(ホスト側)

  • CA を信頼してアクセス(クライアント証明書なし)
curl -v --cacert ./ca/certs/ca.cert.pem https://localhost:8443/
  • mTLS(クライアント証明書あり)
curl -v --cacert ./ca/certs/ca.cert.pem \
  --cert ./ca/certs/client1.cert.pem \
  --key  ./ca/private/client1.key.pem \
  https://localhost:8443/
  • クライアント証明書を必須にする場合:server.conf
    ssl_verify_client on;
    

    に変更して

    docker compose restart nginx
    

    クライアント証明書なしアクセスは 400/403 になります。


よくあるハマりどころ

  • 「Are you trying to mount a directory onto a file?」
    ホスト側の ./server.confファイルである必要があります。ls -ld server.conf で確認。./ca/certs./ca/privateディレクトリである必要があります。
  • openssl ca の初期化エラー
    index.txtserialcrlnumber を作っていないと失敗します(手順 2 で作成済み)。
  • ブラウザで警告回避
    テスト用途に限って OS/ブラウザの「信頼済みルート」に ./ca/certs/ca.cert.pem をインポートします。

注意

本手順は検証用です。実運用では中間CAの分離、鍵保護(HSM 等)、失効運用(CRL/OCSP)、監査・権限分離などが必須です。


現在システム構成要素は、Webクライアント、Webサーバ、CAサーバの3つで良いですか
はい、この検証環境ではその3要素でOKです。
  • Webクライアント(curl/ブラウザ):通信開始&サーバ証明書検証、必要ならクライアント証明書を提示。

  • Webサーバ(Nginx):8443でTLS終端、必要に応じてmTLSでクライアント証明書を検証。

  • CAサーバ(OpenSSLコンテナ):証明書の発行・更新用。実通信のデータ平面には登場しません。

補足:失効確認を厳密にやるなら CRL/OCSP配布(+中間CA/オフラインRoot、HSM等)を追加しますが、今回の最小構成では不要です。

内容が難しいです。もっと簡単に説明して下さい
めっちゃ噛み砕くと、登場人物は3つだけです。
  • Webクライアント(ブラウザ / curl)=「お客さん」

  • Webサーバ(Nginx)=「お店」

  • CAサーバ(OpenSSL)=「役所(身分証を発行するだけ)」※通信中は出てきません

通信の流れ(超ざっくり)

  1. お客さん→お店:「話していい?」(TLS開始)

  2. お店→お客さん:「これがお店の身分証」(サーバ証明書)

  3. お客さん:「役所の印鑑で本物か確認」(--cacert で検証)

  4. (必要なら)お客さん→お店:「これが私の身分証」(--cert/--key=mTLS)

  5. 互いにOKなら会話開始(HTTP 200 が返る)

つまり、役所(CA)は身分証を作るだけ、実際の会話はお客さん↔お店の2者で進みます。

なるほど、分かってきました。お客さんとお店の身分証は、どのディレクトリのどのファイルになりますか?

はい、プロジェクト直下(~/ca-docker)から見た場所は次のとおりです。

  • お店(Webサーバ=Nginx)の身分証

    • 証明書:./ca/certs/server.local.cert.pem

    • 秘密鍵:./ca/private/server.local.key.pem

  • お客さん(クライアント)の身分証

    • 証明書:./ca/certs/client1.cert.pem

    • 秘密鍵:./ca/private/client1.key.pem

    • (ブラウザ取り込み用)PKCS#12:./ca/certs/client1.p12

参考(役所=CA)

  • ルートCA証明書:./ca/certs/ca.cert.pemcurl --cacert や OSの信頼登録で使用)

  • ルートCA秘密鍵:./ca/private/ca.key.pem(厳重保管)

※ Nginx コンテナ内では、これらが /certs/.../private/... にマウントされています。

なるほど、今は便宜的に、お店とお客さんの身分証が同じ場所に配置されているけど、本来は別のホストで管理されているものですね

はい、その理解で正しいです。実運用では保管場所を分けます。

  • サーバ側(お店の身分証)

    • 保管先:Webサーバのホストだけ

    • ファイル:server.local.cert.pem(公開鍵証明書)、server.local.key.pem(秘密鍵・600権限)

    • 備考:クライアント鍵は絶対に置かない

  • クライアント側(お客さんの身分証)

    • 保管先:各クライアント端末だけ

    • ファイル:client1.p12(OSの証明書ストアにインポート)※または client1.cert.pemclient1.key.pem

    • 備考:サーバ鍵は絶対に置かない

  • CA側(役所)

    • 保管先:CA専用ホスト(できればオフライン/HSM)

    • ファイル:ca.key.pem(厳重保管)、ca.cert.pem(配布用)

    • 備考:ca.cert.pem は“信頼の根”としてサーバとクライアントに配布

今は検証の便宜上、同じ ./ca/ にありますが、本来は上記のようにホスト単位で分離します。

CA認証局に、自分(お客でもお店)を登録して貰うには、どうすれば良いのでしょうか

はい、「CAに登録=証明書を発行してもらう」手順は“鍵は自分で作る→CSR提出→CAが署名→証明書を受け取る”です。サーバ(お店)でもクライアント(お客)でも基本は同じ。秘密鍵(.key)は絶対にCAへ渡さないのが大原則です。

流れ(共通の考え方)

  1. 自分の端末で秘密鍵 .key を作る(鍵は外に出さない)

  2. 署名要求 .csr を作る(サーバは SAN にホスト名/IP を入れる)

  3. .csr を CA に渡して署名してもらう

  4. 返ってきた証明書 .cert.pem(や .p12)を自分の端末にインストール


このDocker環境での具体例

A. サーバ(お店)を登録(=サーバ証明書)

(サーバ側ホストで)

bash
# 1) 秘密鍵(手元にだけ保存)
openssl genrsa -out server2.key.pem 2048
# 2) CSR(SANにFQDNやIPを入れる例:web01.local / 127.0.0.1)
cat > server2.req.cnf <<'EOF'
[ req ]
distinguished_name = dn
req_extensions = v3_req
prompt = no
[ dn ]
CN = web01.local
[ v3_req ]
subjectAltName = @alt
[ alt ]
DNS.1 = web01.local
IP.1 = 127.0.0.1
EOFopenssl req -new -key server2.key.pem -out server2.csr.pem -config server2.req.cnf

(CSRをCAへ。今回は簡便のため ./ca/csr/ に置く)

bash
cp server2.csr.pem ./ca-docker/ca/csr/
cd ./ca-docker
docker compose exec ca sh -lc \
'openssl ca -batch -config openssl.cnf -extensions v3_server -days 825 -notext -md sha256 \
-in csr/server2.csr.pem -out certs/server2.cert.pem'

(発行物をサーバに配備してNginxで使う)

bash
# 証明書は ./ca/certs/server2.cert.pem に生成
# Nginxの設定で /certs/server2.cert.pem と /private/server2.key.pem を指すよう変更→再起動

B. クライアント(お客)を登録(=クライアント証明書)

(クライアント側ホストで)

bash
# 1) 秘密鍵
openssl genrsa -out client2.key.pem 2048
# 2) CSR(CNだけでOKなことが多い)
openssl req -new -key client2.key.pem -out client2.csr.pem -subj "/CN=client2"

(CSRをCAへ。今回は ./ca/csr/ に置いて署名)

bash
cp client2.csr.pem ./ca-docker/ca/csr/
cd ./ca-docker
docker compose exec ca sh -lc \
'openssl ca -batch -config openssl.cnf -extensions v3_client -days 825 -notext -md sha256 \
-in csr/client2.csr.pem -out certs/client2.cert.pem'

(ブラウザで使うならPKCS#12にまとめる)

bash
docker compose exec ca sh -lc \
'openssl pkcs12 -export -inkey private/client2.key.pem \
-in certs/client2.cert.pem -certfile certs/ca.cert.pem \
-out certs/client2.p12 -password pass:'

# ./ca/certs/client2.p12 をクライアントOS/ブラウザにインポート

ポイント

  • 送るのは .csr だけ.key は絶対に外に出さない。

  • サーバ証明書は SAN(SubjectAltName)必須DNS=ホスト名, IP=…)。

  • 「登録済み」かどうかは、CAの台帳(./ca/index.txt)に記録され、証明書が発行されていることで判断できます。

  • クライアント証明書をブラウザで使う場合は .p12 をインポート、curl なら --cert--key を使用。

  • 相手の証明書を検証するために、ルートCA(ca.cert.pem)を相手側に配ることも忘れずに(テスト環境限定でOSの信頼ストアに登録可)。

いや、これまで作ってきた構成を変えるのは面倒なので、これから作るものだけディレクトリを別途切りたいだけです

現状の構成はそのままにして、これから作る分だけ分離する最小手順です(docker-compose.yml は変更不要)。

方針

  • CAはこれまで通り ./ca/ を使う。

  • 新しいサーバ/クライアントは、鍵とCSRと受け取った証明書を専用フォルダに保管。

  • 署名のときだけ、CSRを一時的に ./ca/csr/... に置いて使う(終わったら削除OK)。

例:新サーバ web02 を分離保管

# 1) 専用フォルダ
mkdir -p entities/server_web02/{private,csr,certs}

# 2) 鍵とCSR(鍵は専用フォルダだけに置く)
openssl genrsa -out entities/server_web02/private/server_web02.key.pem 2048

cat > entities/server_web02/csr/server_web02.req.cnf <<'EOF'
[ req ] distinguished_name = dn; prompt = no; req_extensions = v3_req
[ dn ]  CN = web02.local
[ v3_req ] subjectAltName = @alt
[ alt ] DNS.1 = web02.local
        IP.1  = 127.0.0.1
EOF

openssl req -new \
  -key entities/server_web02/private/server_web02.key.pem \
  -out entities/server_web02/csr/server_web02.csr.pem \
  -config entities/server_web02/csr/server_web02.req.cnf

# 3) 署名前の一時配置(CA側に見せるだけ)
mkdir -p ca/csr/server_web02 ca/certs/server_web02
cp entities/server_web02/csr/server_web02.csr.pem ca/csr/server_web02/

# 4) CAで署名(既存のCAコンテナでOK)
docker compose exec ca sh -lc \
'openssl ca -batch -config /ca/openssl.cnf -extensions v3_server -days 825 -md sha256 \
 -in /ca/csr/server_web02/server_web02.csr.pem \
 -out /ca/certs/server_web02/server_web02.cert.pem'

# 5) 交付物を専用フォルダへコピー(CA側は残しても/消しても可)
cp ca/certs/server_web02/server_web02.cert.pem entities/server_web02/certs/

で、
mkdir -p ca/csr/server_web02 ca/certs/server_web02

に失敗します。権限エラーは、./ca 配下が CAコンテナの root 所有だからです。いまは構成を崩さず進めたいとのことなので、CAコンテナ側で作成→docker cp でやり取りするのが安全・最短です。

# 1) CAコンテナ内にディレクトリを作成
docker compose exec ca sh -lc 'mkdir -p /ca/csr/server_web02 /ca/certs/server_web02'

# 2) CSR をコンテナ側へコピー
docker cp entities/server_web02/csr/server_web02.csr.pem \
  local-openssl-ca:/ca/csr/server_web02/

# 3) 署名(コンテナ内で実行)
docker compose exec ca sh -lc \
'openssl ca -batch -config /ca/openssl.cnf -extensions v3_server -days 825 -md sha256 \
 -in /ca/csr/server_web02/server_web02.csr.pem \
 -out /ca/certs/server_web02/server_web02.cert.pem'

# 4) 発行済み証明書をホストの専用フォルダへ取り出し
docker cp local-openssl-ca:/ca/certs/server_web02/server_web02.cert.pem \
  entities/server_web02/certs/

で対応。

例:新クライアント alice を分離保管

mkdir -p entities/client_alice/{private,csr,certs}

openssl genrsa -out entities/client_alice/private/client_alice.key.pem 2048

openssl req -new \
  -key entities/client_alice/private/client_alice.key.pem \
  -out entities/client_alice/csr/client_alice.csr.pem \
  -subj "/CN=client_alice"

mkdir -p ca/csr/client_alice ca/certs/client_alice
cp entities/client_alice/csr/client_alice.csr.pem ca/csr/client_alice/

docker compose exec ca sh -lc \
'openssl ca -batch -config /ca/openssl.cnf -extensions v3_client -days 825 -md sha256 \
 -in /ca/csr/client_alice/client_alice.csr.pem \
 -out /ca/certs/client_alice/client_alice.cert.pem'

cp ca/certs/client_alice/client_alice.cert.pem entities/client_alice/certs/

# (ブラウザ用P12は自分の端末で作成)
openssl pkcs12 -export -inkey entities/client_alice/private/client_alice.key.pem \
 -in entities/client_alice/certs/client_alice.cert.pem \
 -certfile ca/certs/ca.cert.pem \
 -out entities/client_alice/certs/client_alice.p12 -password pass: で、やはり、

ebata@DESKTOP-1QS7OI7:~/ca-docker$ mkdir -p ca/csr/client_alice ca/certs/client_alice
mkdir: cannot create directory ‘ca/csr/client_alice’: Permission denied
mkdir: cannot create directory ‘ca/certs/client_alice’: Permission denied

で失敗します。原因はこれまでと同じで、./ca 配下がコンテナの root 所有=ホスト一般ユーザでは書けないためです。以下のどちらかで進めてください。

# 0) コンテナ名を変数に(既定ならこれ)
CONTAINER=local-openssl-ca

# 1) CAコンテナ内にディレクトリ作成
docker compose exec ca sh -lc 'mkdir -p /ca/csr/client_alice /ca/certs/client_alice'

# 2) CSRをコンテナへコピー
docker cp entities/client_alice/csr/client_alice.csr.pem \
  $CONTAINER:/ca/csr/client_alice/

# 3) 署名(client用途)
docker compose exec ca sh -lc \
'openssl ca -batch -config /ca/openssl.cnf -extensions v3_client -days 825 -md sha256 \
 -in /ca/csr/client_alice/client_alice.csr.pem \
 -out /ca/certs/client_alice/client_alice.cert.pem'

# 4) 発行済み証明書を取り出し
docker cp $CONTAINER:/ca/certs/client_alice/client_alice.cert.pem \
  entities/client_alice/certs/

# 5) (任意)ブラウザ用P12はホスト側で作成
openssl pkcs12 -export \
 -inkey entities/client_alice/private/client_alice.key.pem \
 -in    entities/client_alice/certs/client_alice.cert.pem \
 -certfile ca/certs/ca.cert.pem \
 -out   entities/client_alice/certs/client_alice.p12 \
 -password pass: ============

ebata@DESKTOP-1QS7OI7:~/ca-docker$ tree
.
├── Dockerfile
├── ca
│   ├── certs
│   │   ├── ca.cert.pem
│   │   ├── client1.cert.pem
│   │   ├── client1.p12
│   │   ├── client_alice
│   │   │   └── client_alice.cert.pem
│   │   ├── server.local.cert.pem
│   │   └── server_web02
│   │   └── server_web02.cert.pem
│   ├── crl
│   ├── crlnumber
│   ├── csr
│   │   ├── client1.csr.pem
│   │   ├── client_alice
│   │   │   └── client_alice.csr.pem
│   │   ├── server.local.csr.pem
│   │   └── server_web02
│   │   └── server_web02.csr.pem
│   ├── index.txt
│   ├── index.txt.attr
│   ├── index.txt.attr.old
│   ├── index.txt.old
│   ├── newcerts
│   │   ├── 1000.pem
│   │   ├── 1001.pem
│   │   ├── 1002.pem
│   │   ├── 1003.pem
│   │   ├── 1004.pem
│   │   └── 1005.pem
│   ├── openssl.cnf
│   ├── private [error opening dir]
│   ├── serial
│   ├── serial.old
│   └── server.req.cnf
├── docker-compose.yml
├── docker-compose.yml~
├── entities
│   ├── client_alice
│   │   ├── certs
│   │   │   ├── client_alice.cert.pem
│   │   │   └── client_alice.p12
│   │   ├── csr
│   │   │   └── client_alice.csr.pem
│   │   └── private
│   │   └── client_alice.key.pem
│   └── server_web02
│   ├── certs
│   │   └── server_web02.cert.pem
│   ├── csr
│   │   ├── server_web02.csr.pem
│   │   └── server_web02.req.cnf
│   └── private
│   └── server_web02.key.pem
├── server.conf
└── server.conf~

20 directories, 38 files

現在、ここまで作成できました。この環境を使って、server_web02とclient_aliceで通信するプログラムを作成して下さい

了解です。いま作った entities/server_web02entities/client_alice の証明書・鍵をそのまま使って、**相互TLS(mTLS)で通信する最小のPythonプログラム(サーバ+クライアント)**を用意します。Nginxは触らず、別ポート 9443 を使います。

※ 実行は ~/ca-docker をカレントにしてください。

サーバ(server_web02 側)

server_web02_https.py を作成

# server_web02_https.py
from http.server import HTTPServer, BaseHTTPRequestHandler
import ssl

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        cert = self.connection.getpeercert()
        # クライアント証明書のCNを表示(任意)
        client_cn = None
        if cert and "subject" in cert:
            subject = dict(x[0] for x in cert["subject"])
            client_cn = subject.get("commonName")

        self.send_response(200)
        self.send_header("Content-Type", "text/plain; charset=utf-8")
        self.end_headers()
        self.wfile.write(f"Hello from server_web02\nclient_cn={client_cn}\n".encode("utf-8"))

if __name__ == "__main__":
    httpd = HTTPServer(("0.0.0.0", 9443), Handler)

    # 相互TLS用コンテキスト
    ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    # サーバ側の証明書/鍵(server_web02)
    ctx.load_cert_chain(
        certfile="entities/server_web02/certs/server_web02.cert.pem",
        keyfile="entities/server_web02/private/server_web02.key.pem",
    )
    # クライアント証明書の検証に使うCA
    ctx.load_verify_locations(cafile="ca/certs/ca.cert.pem")
    ctx.verify_mode = ssl.CERT_REQUIRED  # クライアント証明書必須

    httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True)
    print("server_web02: https://127.0.0.1:9443 で待機中(mTLS 必須)")
    httpd.serve_forever() 起動(ターミナル1): cd ~/ca-docker
python3 server_web02_https.py


クライアント(client_alice 側)

client_alice_request.py を作成 
# client_alice_request.py
import ssl
import http.client

if __name__ == "__main__":
    # サーバ検証用CA(自作CA)+ クライアント証明書/鍵(client_alice)
    ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="ca/certs/ca.cert.pem")
    ctx.load_cert_chain(
        certfile="entities/client_alice/certs/client_alice.cert.pem",
        keyfile="entities/client_alice/private/client_alice.key.pem",
    )

    # 証明書のSANに IP=127.0.0.1 を入れているため、ホスト名は 127.0.0.1 を使う
    conn = http.client.HTTPSConnection("127.0.0.1", 9443, context=ctx)
    conn.request("GET", "/")
    resp = conn.getresponse()
    body = resp.read().decode("utf-8", errors="replace")
    print(resp.status, resp.reason)
    print(body)
    conn.close() 実行(ターミナル2):
cd ~/ca-docker
python3 client_alice_request.py

期待結果:

  • クライアント側に 200 OK と本文

Hello from server_web02
client_cn=client_alice
  • サーバ側のログにはアクセスが1件記録されます。

(補足)

  • 接続先は 127.0.0.1 を使ってください。 証明書の SAN に IP:127.0.0.1 を入れているため、localhost で接続するとホスト名検証に失敗します。

  • すでに python3 が入っていれば追加ライブラリ不要(標準ライブラリのみ)です。

  • もし CERTIFICATE_VERIFY_FAILED が出る場合は、ca/certs/ca.cert.pem のパス誤りや権限を確認してください。

未分類

Posted by ebata