クライアント証明書(1)
~証明書をつくる~

2020/1/26


クライアント証明書とは何ぞ?

先日、サーバー証明書をつくった。つくったと言っても「オレオレ証明書」であり、「私は正しいサーバーです。信じてください。」と言っているわけで、一応信じてみることにした。 クライアント証明書は、「私はあなたのサービスを受ける資格があります。これがその証拠の会員証です。」的なメンバーズカードみたいなものと言える。

証明書を作る手順は
ユーザサーバ管理者メモ
1.ユーザが秘密鍵・公開鍵ペアをつくる。
2.秘密鍵はユーザが大事に保管する。
3.ユーザは公開鍵をもとに証明書(クライアント証明書)を作る。
(待機)  
4.ユーザはクライアント証明書をサーバーに提出する。(待機) 正規のユーザが提出した証明書であることを確認する。
(待機) 5.クライアント認証局(オレオレサーバー)がクライアント証明書に署名する。 
(待機) 6.サーバ証明書とクライアント証明書(署名済)をクライアントに届ける。サーバ証明書はすべてのユーザに共通
クライアント証明書(署名済)はユーザを確認する。
7.ユーザはサーバ証明書とクライアント証明書(署名済)をwebブラウザにインストールする。 

だと思うが、ここで問題が発生する。

  1. ユーザが秘密鍵・公開鍵ペアを作れない。
    ユーザが作れないので、手順1〜4はサーバ側で行う。
    サーバ側で秘密鍵・公開鍵・サーバ証明書・クライアント証明書を用意して、クライアントまで安全な手段でお届けすることになるだろう。

  2. 正規のユーザであることをどのようにして保証するか。
    ユーザ数が限定的であるなら、こちらからユーザのもとに出向くこともできるだろう。鍵と証明書の輸送経路は、リアルな輸送となる。相手を確認して鍵と証明書を手渡しするなら、「正規のユーザ」として保証できるであろう。 (もしかしたら、インストール作業まですることになるかもしれない。)

  3. 秘密鍵をどのようにして輸送するか
    「httpで秘密鍵をダウンロードする」とか「メールに添付」は避けたい。ユーザに秘密鍵を送らないという選択は可能だろうか。
    「ユーザには最初にサーバ証明書をインストールしてもらい、安全な通信経路を確保した上で、いろいろダウンロードする。」が現実的な対応だろう。

認証局・証明書設定

すでにルート認証局(オレオレ認証局)でサーバ証明書(オレオレ証明書)を発行しているので、そのままDigest認証を使う。

/etc/pki/tls/openssl.cnfを修正

修正前修正後
default_days = 365default_days = 3650
countryName_default = XXcountryName_default = JP
#stateOrProvinceName_default = Default ProvincestateOrProvinceName_default = HKD
localityName_default = Default CitylocalityName_default = HKDT
0.organizationName_default = Default Company Ltd0.organizationName_default = NITH
#organizationalUnitName_default =organizationalUnitName_default =Takahashi Lab

Apacheの設定(/etc/httpd/conf.d/ssl.conf)はすでに完了している。これにクライアント認証に関する部分を追記する。

/etc/httpd/conf.d/ssl.confを修正する。

修正前修正後
サーバ証明書に関する設定(設定済)

#DocumentRoot "/var/www/html"
#ServerName www.example.com:443
#を削除, ServerNameを修正
DocumentRoot "/var/www/html"
ServerName webserver.takahashi.lab:443

#SSLProtocol all -SSLv3
#を削除, プロトコルはTLS1.3のみを許可 (-allで全て不許可した後、TLS1.3を許可)
#SSLProtocol -all +TLS1.3
#「SSLProtocol: Illegal protocol 'TLS1.3'」のときは #を削除
SSLProtocol all -SSLv3

SSLCertificateFile /etc/pki/tls/certs/localhost.crt
#PEMでエンコードした証明書
SSLCertificateFile /etc/httpd/conf/ssl.crt/server.crt

SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
#秘密鍵
SSLCertificateKeyFile /etc/httpd/conf/ssl.key/server.key

<FilesMatch "\.(cgi|shtml|phtml|php)$">>
SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/var/www/cgi-bin">
SSLOptions +StdEnvVars
</Directory>
#cgi,php他は使わないのですべてコメントアウト
#<FilesMatch "\.(cgi|shtml|phtml|php)$">>
# SSLOptions +StdEnvVars
#</FilesMatch>
#<Directory "/var/www/cgi-bin">
# SSLOptions +StdEnvVars
#</Directory>
クライアント認証に関する設定(ここから修正部分)

#SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt
#client-ca.crtはこれからつくる
SSLCACertificateFile /etc/httpd/conf/CA/client-ca.crt

#SSLVerifyClient require
#SSLVerifyDepth 10
#SSLVerifyClient require(クライアント証明書が必須)
#クライアント証明書はなくても可とするのでrequireをoptionalにする
SSLVerifyClient optional
#ルート認証局-中間認証局-..-クライアント証明書 は10階層まで探す
#SSLVerifyDepth 10
#オレオレ認証局なら1でよい
SSLVerifyDepth 1

<Location />
#SSLRequire (  %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \
#       and %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \
#       and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} \
#       and %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 \
#       and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20 ) \
#       or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/
</Location>
#アクセス制御は組織名(O)がNITHで CNが"foo"か"bar" または 特定のIPアドレス
<Location />
#subject=C = JP, ST = HKD, L = HKDT, O = NITH, OU = Takahashi Lab, CN = foo
#subject OがNITHで CNがfooかbarならOK
SSLRequire (  %{SSL_CLIENT_S_DN_O} eq "NITH" \
       and %{SSL_CLIENT_S_DN_CN} in { "foo", "bar"} \
       ) or \
         %{REMOTE_ADDR} =~ m/^192\.168\.xx\.xx/
#192.168.xx.xxはクライアント証明書なしにアクセスできる
</Location>

クライアント証明書をつくる

ユーザ(foo,bar)2人にそれぞれの証明書をつくる。
#ユーザ(foo)ごとにディレクトリを作る
mkdir -p client/foo;cd client/foo
#秘密鍵生成 秘密鍵はfooだけが知っている
openssl genrsa -out client.key 2048
#openssl genrsa 2048 > server.key

#クライアントCSR生成 オプションをつけて質問を省略 または デフォルト値(openssl.cnf)を使う
openssl req -new -key client.key -subj "/C=JP/ST=HKD/L=HKDT/O=NITH/OU=Takahashi Lab/CN=foo" -out client.csr
#デフォルト値を使うときは-subjオプションを省略
#openssl req -new -key client.key -out client.csr
#Country Name (2 letter code) [JP]: --JP,HKD,NITH,Takahashi Labはopenssl.cnfに記述したデフォルト値
#State or Province Name (full name) [HKD]:
#Locality Name (eg, city) [HKDT]:
#Organization Name (eg, company) [NITH]:
#Organizational Unit Name (eg, section) [Takahashi Lab]:
#Common Name (eg, your name or your server's hostname) []:foo  ---ここは手入力
#Email Address []:. ---.(ピリオド)を指定
#A challenge password []: --そのままEnter
#An optional company name []: --そのままEnter


#クライアント認証局client-ca.crtをつくる
openssl x509 -in client.csr -out client-ca.crt -req -signkey client.key -days 3650
#Signature ok
#subject=C = JP, ST = HKD, L = HKDT, O = NITH, OU = Takahashi Lab, CN = foo
#Getting Private key

#秘密鍵と証明書(公開鍵証明書)をパックしてクライアント証明書にする pkcs12形式で出力
openssl pkcs12 -export -inkey client.key -in client-ca.crt -out client.p12 -name "myMember"
#Enter Export Password:  --このパスワードは、クライアントPCにクライアント証明書をインポートする際に必要となる
#Verifying - Enter Export Password:

#ここまでの作業で client-ca.crt  client.csr  client.key  client.p12 ができる
同様にユーザbarについて作業をする。

認証局にファイルを配置

client-ca.crtを/etc/httpd/conf/CA/client-ca.crt にコピーする。 複数のユーザに対して証明書を発行するとクライアントcsr(cliena-ca.crt)がユーザごとにできる。 各crtを連結して、クライアント認証局にする。
#ユーザが1人(foo)のときはコピーでよい
#cp /etc/pki/foo/client-ca.crt /etc/httpd/conf/CA/client-ca.crt 
#ユーザ(foo,bar)が複数いるので、crtを連結する。
cat /etc/pki/client/foo/client-ca.crt /etc/pki/client/bar/client-ca.crt > /etc/httpd/conf/CA/client-ca.crt 

いろいろ設定項目が多かったので、設定を確認する。
#httpdの設定を確認
apache configtest
#問題がなければ、httpdを再起動
systemctl restart httpd

ユーザへお届け

ユーザへ届けるものは2つ
2つの証明書はユーザが自分のPCへインストールする。
サーバ証明書/etc/pki/myCA/server.crt
クライアント証明書
秘密鍵・公開鍵を含む
/etc/pki/xxx/client.p12
パスワードクライアント証明書をつくる際に入力したパスワード

webブラウザに証明書をインポート

これでユーザにサーバ側の準備ができた。
つぎは、webブラウザに証明書をインストールする。
webブラウザごとに手順が違うので、クライアント証明書(2)以降で個別に検証する。

[参考]
Apache > HTTP サーバ > ドキュメンテーション > バージョン 2.4 > モジュール ディレクティブ クイックリファレンス https://httpd.apache.org/docs/2.4/ja/mod/quickreference.html

Apache > HTTP サーバ > ドキュメンテーション > バージョン 2.4 > How-To / チュートリアル 認証、承認、アクセス制御 https://httpd.apache.org/docs/2.4/ja/howto/auth.html

クライアント証明書の作り方 https://y-yagi.tumblr.com/post/18179788088/%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9

オレオレ認証局でクライアント認証 ~ ウェブの Basic 認証をリプレース (https://www.webtech.co.jp/blog/optpix_labs/server/1780/)

OpenSSLコマンドの備忘録 https://qiita.com/takech9203/items/5206f8e2572e95209bbc

www.opentone.co.jp › news › seminar › ref › ref20060906-01 クライアント認証を使った アプリケーション開発 - OpenTone (https://www.opentone.co.jp/news/seminar/060906/ref/ref20060906-01.pdf)

【Apache+Tomcat】サーブレットでクライアント証明書を取得する (https://ext1.wiki.fc2.com/wiki/%E3%80%90Apache%2BTomcat%E3%80%91%E3%82%B5%E3%83%BC%E3%83%96%E3%83%AC%E3%83%83%E3%83%88%E3%81%A7%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B)