今回はローカル内サーバなので問題ないが、外部に公開しリモートログインさせる場合、sslの設定だけだと心もとないので、接続元クライアントを制限したい。そういった場合は、プライベート認証局を構築し、クライアント証明書を発行することでよりセキュアなリモートログインシステムを実現する。
通常、プライベート認証局(ルート認証局)、中間認証局、サーバ証明書、クライアント証明書の順で作成していくが、今回は中間認証局を割愛する。また、通常はルート認証局と中間認証局と認証対象サーバは別物を使用するが、今回はサーバ1つしか立てられないのでルート認証局と認証対象サーバを同一のサーバに作成する。仮想マシンが使用できる人はそれを使うと良いだろう。
色々サイトがあったが、わかりやすさと情報の正確性でこれらのサイトが参考になった。上から順に個人的ランキング上位。
http://safe-linux.homeip.net/web/apache2/cliant_certify-02.html
https://stacktrace.hatenablog.jp/?page=1452342192
https://http2.try-and-test.net/openssl_simple_ca.html
https://www.robata.org/docs/openssl/ca-set.html
パスフレーズ (メモ)
ルート認証局:「rootCA」
サーバ証明書:「server」
クライアント証明書:「client」
1. ルート認証局の構築と自己署名証明書の作成
すべて認証局サーバ上で行う。
(1) 使用するツールがインストール済みか確認する
"4.Webサーバの構築" でNginxを構築したので、こいつを認証対象サーバとして機能させる。
OpenSSLはSecure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS v1) のプロトコルとそれに関係する標準的暗号を実装したツールキット。 OpenSSLの暗号ライブラリの様々な暗号機能をシェルから使うためのコマンドラインも指す。まずopensslとNginxがあるか確認する
#openssl version
OpenSSL 1.1.1k 25 Mar 2021
#nginx -v
nginx version: nginx/1.18.0
なければ
#sudo apt-get install <対象>
(2) スクリプトファイルの設定
いちいちopensslコマンドを利用して認証局構築してもいいが、kaliにはデフォルトでopenssl-perlが使えるので今回はこれにお世話になることにした。
難解な認証局構築の手順を簡素化してくれるスクリプトファイル「/usr/lib/ssl/misc/CA.pl」があるか確認する。これを利用すれば非常に簡単に自己署名証明書やサーバ証明書、クライアント証明書の発行ができる。なお、このファイルは"Do not edit!!"となっているので、バックアップをとってから編集作業を行うこと。
#sudo cp -a /usr/lib/ssl/misc/CA.pl /usr/lib/ssl/misc/CA.pl_bck
CA.plスクリプトの中身を確認すると、"$dir = ./demoCA"になっているので、証明書作成ディレクトリが現在のディレクトリと同じ階層にdemoCAとして作成されることがわかる。ディレクトリ名は任意。ここでは、ルート認証局を作成したいので分かりやすく、"./rootCA"とする。
秘密鍵だけprivateディレクトリを経てその配下に配置されるようになっている。秘密鍵だけ他と作られる階層が1つ下担っていることが分かる。
(3) 自己署名証明書の発行
スクリプトファイルに "-newca" オプションをつければ一気にCA証明書発行に必要なファイルやディレクトリ作成、秘密鍵作成、CSRの発行、署名済み証明書の発行まで片付けてくれる(なんてすばらしいんだ!!)
#sudo /usr/lib/ssl/misc/CA.pl -newca
途中、証明書にどんな情報を記載するか色々と聞かれるが、今回は自己署名証明書の作成なので適当にEnter押してスルーOK。また、パスフレーズを何回か聞かれる (Enter pass phrase for ×××:)がすべて同じフレーズを入力すること。
自分はこんな感じでinputしてみた。
最後の行に "CA certificate is in ./rootCA/cacert.pem" と表示されれば問題なく証明書発行が終わったことになる。エラーになると、どこかしらで実行が中断されているはず。どんなエラー文になっているか確認して修正する必要がある。
なお、スクリプトがどんな挙動をしているかはコンソール画面に表示される(opensslコマンドの挙動のみ表示で、それ以前のファイルやディレクトリ作成は表示されないので各自以下のように事前作成しておく必要がある)。スクリプトを使用しない場合は以下のコマンドとオプションの組み合わせを使用することで同じことが行える。
これで自己署名証明書の発行は終了した。スクリプトファイルがあればあっという間に終わることが分かる。
(4) スクリプトファイルを使用しない場合
opensslの設定ファイル「/etc/ssl/openssl.cnf」の設定から始める。いじる前にバックアップをとって編集する。
#sudo cp -a /etc/ssl/openssl.cnf /etc/ssl/openssl.cnf_bck
#sudo vi /etc/ssl/openssl.cnf
赤枠部分を変更。今回はルート認証局として分かりやすく "rootCA" とした。また、黄色枠部分からわかる通り、秘密鍵はprivateディレクトリ配下にあり、他と階層が異なるので注意。
↓今は自己書名証明書の作成であって、中間認証局の証明書ではないため。
本当に項目あってるか調べて!
さらに、事前に下記ディレクトリ構造になるように "sudo mkdir"を行う。
<CA証明書発行に必要なファイルやディレクトリ作成>
#sudo mkdir /etc/ssl/rootCA/certs
#sudo mkdir /etc/ssl/rootCA/newcerts
#sudo mkdir /etc/ssl/rootCA/crl
#sudo mkdir /etc/ssl/rootCA/private
<秘密鍵作成、CSRの発行>
#sudo openssl req -new -keyout /etc/ssl/rootCA/private/cakey.pem -out /etc/ssl/rootCA/careq.pem
-keyout:出力する秘密鍵のファイル名指定
-out:出力するCSRのファイル名指定
<署名済み証明書の発行>
#openssl ca -create_serial -out ./rootCA/cacert.pem -days 1095 -batch -keyfile ./rootCA/private/cakey.pem -selfsign -extensions v3_ca -infiles ./rootCA/careq.pem
-create_serial:serialファイルを作成 →何のためにあるのか現時点で不明
-out:出力する証明書のファイル名指定
-days:証明書の有効期限
-batch:対話を行わずにバッチモードで指定
-keyfile:CSRに署名するための秘密鍵を指定
-selfsign:自己署名の場合に指定
-extensions:設定ファイル「openssl.cnf」の拡張セクションを指定
-infiles:CSRを指定。最後に記載した場合は複数のCSRを指定可能。
これで自己署名証明書の作成が終了した。ここまでの操作でツリー構造は以下のようになった。
作成された証明書、CSR、秘密鍵の中身(いずれもテキストファイルなのでcatでも閲覧可能だが、人が見て分かりやすい形式で表示されない)を見たいときは以下のコマンドで確認ができる。
[基本形]
#sudo openssl <種類(x509|req|rsa)> -in <パス> -text
<証明書の確認>
#cd /etc/ssl/rootCA →証明書を作成したディレクトリまで移動してから
#sudo openssl x509 -in cacert.pem -text
またはファイル名のところを絶対パスで指定すればcdの必要なし
#sudo openssl x509 -in /etc/ssl/rootCA/cacert.pem -text
<CSRの確認>
# openssl req -in /etc/pki/CA/careq.pem -text
<秘密鍵の確認> →パスフレーズ求められる
# openssl rsa -in /etc/pki/CA/private/cakey.pem -text
以下は自己署名(オレオレ)証明書を確認したときのキャプチャ。
最後に各ディレクトリのパーミッションの変更(644→600)をして終了。
#sudo chmod 600 /etc/ssl/rootCA/certs
#sudo chmod 600 /etc/ssl/rootCA/newcerts
#sudo chmod 600 /etc/ssl/rootCA/crl
#sudo chmod 600 /etc/ssl/rootCA/private
これでrootユーザ以外は読み書き実行ができなくなったので、一般ユーザはそのディレクトリに入ることすら不可能になった。
[参考サイト]
http://safe-linux.homeip.net/web/apache2/cliant_certify-02.html
2. サーバ証明書の発行
(1) 認証対象サーバの秘密鍵作成
通常、サーバ証明書の秘密鍵とCSRの作成は認証対象サーバ内で行う。したがって、認証局と別にサーバを立てられている人はそのサーバにログインして以降の操作を行う。今回は、冒頭に書いたとおり諸事情により認証局と認証対象サーバを同一のサーバ内に作成するため、さきほどと同じ認証局サーバ内で行っている。
今回の認証対象サーバはNginxで構築したWebサーバなので、区別しやすいように諸々のファイルを保管するディレクトリを「/etc/nginx/ssl」とし、そこに秘密鍵を作成する。ここではスクリプトファイルを使用しない。
<証明書発行に必要なディレクトリの作成>
#sudo mkdir /etc/nginx/ssl
<秘密鍵作成>
#cd /etc/nginx/ssl
#sudo openssl genrsa -out server.key -des3 2048 →パスフレーズ求められる
(2) 認証局へ送付するCSRの作成
#cd /etc/nginx/ssl
#openssl req -new -key server.key -outform PEM -keyform PEM -sha256 -out server.csr
ルート認証局で作成した自己署名証明書と同じく色々聞かれるので証明書に記載したい内容を答える。
(3) 作成したCSRを認証局へ送付
今回作成したCSRは認証局に送付し、証明書を発行してもらう必要がある。ではどの部分を送付するのか?ということで、以下の部分が送付箇所になる。
#cd /etc/nginx/ssl
#sudo openssl req -in server.csr -text
※きちんと "----BEGIN CERTIFICATE REQUEST----" 〜 "----END CERTIFICATE REQUEST----"の文字も送付する必要がある!
上記だけをコピペでもいいし、cpコマンドですべてコピーしてもOK。今回は同一サーバ内に構築しているので、認証局のディレクトリの方へコピーした。→ 通常はメールか何かで認証局へ送る
#sudo cp -a /etc/nginx/ssl/server.csr /etc/ssl/rootCA/server.csr
(4) 認証局が認証対象サーバからのCSRをもとに証明書の発行を行う
#cd /etc/ssl/rootCA
#sudo openssl x509 -req -in server.csr -sha256 -CA cacert.pem -CAkey ./private/cakey.pem -set_serial 01 -days 365 -out server.pem
※秘密鍵はディレクトリ階層が他と異なるので注意!
現在のディレクトリに認証局が署名した証明書「cacert.pem」が新しく作成されたことが確認できる。
(5) 認証局が署名したサーバ証明書の返送
#sudo cp -a /etc/ssl/rootCA/server.pem /etc/nginx/ssl/server.pem
これで認証局が署名したサーバ証明書が認証対象サーバに格納された。
3. サーバにサーバ証明書をセット
今回、デフォルトで入っていたApacheが80ポートをすでに使用していたため、バッティングしないようにNginxでは82ポートを使用することにした。
#sudo vi /etc/nginx/nginx.conf
※nginx の設定では複数の server block を書くことが通常だが, どれか一つでもこれらが有効になっていると、すべての server について有効になってしまう。上記のようにhttp block に書くのがよい。
設定完了したら、再起動しエラーが出ないことを確認する。
\#systemctl restart nginx
または
\#sudo nginx -t
こっちの方がどこがエラーなのかわかりやすく表示してくれるのでおすすめ。
上記は、最初にサーバーの秘密鍵作成時に1024bitで作成したことが原因で"鍵長が短すぎる"とエラーになっているケース。
以下のようになれば認証成功している。→ パスフレーズには認証対象サーバの署名パスフレーズを入力。
あとはサービス再起動してブラウザからアクセスするだけ!
#systemctl restart nginx
おやおや・・・なんかエラーになるジャン!ミカサ・・・。
※ログが長すぎてコンソールからはみ出る場合は "-l" オプションでカーソルキー移動が可能になり、以下のように最後まで閲覧できる。
色々調べたところ、以下の事実を知る。
おれおれ証明書を作成したときに(秘密鍵を作成するときに)パスフレーズが設定されていると以下のようなエラーがでます。
そもそも、秘密鍵にパスフレーズが設定されていることが原因で、参照できなくなっているので、パスフレーズを外してしまいましょう。
引用:(https://pig-log.com/nginx-https-certificate/)
ということなので、opensslコマンドで入力と出力に同じ秘密鍵を指定して、パスフレーズを外す。その前にバックアップ。
#cd /etc/nginx/ssl
#sudo cp -a server.key server.key_bck
#sudo openssl rsa -in server.key -out server.key →パスフレーズ入力
さて、ローカルネットワーク内の他の端末(Windows10)から "https://192.168.1.8"へアクセスしてみる。オレオレ証明の初回接続なので警告が出るが、無視して接続。
上記URLバーの左(赤丸)から詳細を開いて・・・
上記証明書ボタンを押すと・・・以下のように証明書が確認できる。自分で作成した ルート認証局(Tokyo)"sample" とサーバの署名(Osaka) "sample2"であることが確認できる。
これでサーバ証明書は完了だぁ!お疲れ様です。
最近では、クライアント側で証明書の登録をしていないサーバにアクセスしても例外として登録できるようになっている(ex.Firefox)が、署名済みサーバ証明書をブラウザに埋め込んで、接続時に以下のような警告が出ないようにもできる。
※このキャプチャではlocalhostに接続しているが、今回のサーバのアドレスは "192.168.8.1" であるので注意
おまけ
☆ 自己署名証明書の作成ができたか確認するコマンド
#sudo openssl s_client --connect 192.168.1.8:443 -showcerts
ブラウザで確認した証明書と同じ内容のものをコンソールに出力した感じのものが出力される。
☆ 使用するポートがopenであるか確認するコマンド
#nmap -p 0-500 <server address> →今回は192.168.1.8
表示されるhtmlファイルは「/var/www/html」に格納されるため、自分で作成したhtmlファイルもここに格納すればアクセス時に表示させることが可能となる。
参考サイト(https://wireless-network.net/linux-apache/)
4. クライアント証明書
クライアント用の証明書 (参照:http://safe-linux.homeip.net/web/apache2/cliant_certify-02.html) ではないので注意。ここで作成するのはこっち (参照:http://safe-linux.homeip.net/web/apache2/cliant_certify-04.html) 。この証明書を持たないユーザーに対し、サーバーが指定したディレクティブへのアクセスを制限する事が可能となる。
(1) クライアント証明書の作成
まだ編集中・・・。
最後に、サーバにアクセスする端末の正当性をサーバに対し証明する "クライアント証明書" の発行を行う。秘密鍵とCSRの作成まではサーバと同じ手順である。CSRにCAが署名をしてクライアント証明書を発行する。
クライアント証明書はサーバ証明書の署名と内容が一致しているかで接続を許可するか否かを決定するため、そこからCAを発行するのに入力するデータは接続先サーバと同じデータを入力する必要がある--->ほんとか??
#cd /etc/ssl
<秘密鍵とCSRの作成>
#sudo openssl req -new -keyout ./rootCA/client.key -out ./rootCA/client.csr
<クライアント証明書の作成>
----> CSRにCAが署名をする
#sudo openssl ca (or newreq) -out ./rootCA/client.pem -infiles ./rootCA/client.csr
→ あってないかも。
最後に "Data Base Updated" と表示されれば証明書作成完了!
クライアント証明書作成してからnginxがconfig errorで起動できない。パスワードがマッチしていないらしいけど・・・どこで間違えた?泣
(2) 形式変換
Windows OSの場合、暗号化や出どころの証明などに使われる電子証明書は、「証明書ストア」と呼ばれる場所に保存されている。そこに証明書や秘密鍵をインストールしたり、逆にエクスポートしてバックアップしたりする場合、「.cer」「.p7b」「.pfx」といった拡張子のファイルに証明書や秘密鍵を格納して取り扱う。
一方、UNIX/Linuxの世界では、「PEM(Privacy Enhanced Mail)」と呼ばれる形式で証明書をファイルに保存して取り扱うことが比較的多い(PEM自体はエンコードにBase64を用いるデータフォーマットであり、証明書以外のデータにも利用されている)。それにWindowsとUNIX/Linuxとでは、証明書/秘密鍵の取り扱い方に違いがある(詳細は後述するが、仕様が同じ部分もある)。そのため、一方から他方へ証明書を移行する際、ファイルフォーマットの変換が必要になることがある。引用(https://www.atmarkit.co.jp/ait/articles/1602/05/news039.html)
<DER形式に変換>
#sudo openssl x509 -outform der -in selfsignedCA.pem -out selfsignedCA.der
<PKCS形式に変換>
#sudo openssl pkcs12 -export -in ./rootCA/client.pem -inkey ./rootCA/client.key -out ./rootCA/client.pfx
<証明書方式>
☆ PEM
Base64 ASCIIエンコーディング形式。最も一般的に使用される。PEMファイルは、プレーンテキストのヘッダーとフッター(ex. -----BEGIN CERTIFICATE----- -----END CERTIFICATE-----)を持ち、 "cat" などで閲覧可能。 単一のPEMファイルには、エンドエンティティ証明書、秘密鍵、または完全な信頼チェーンを形成する複数の証明書を含めることが可能。
☆ DER
バイナリエンコーディング形式。PEMとは異なり、DERでエンコードされたファイルは "cat" などで閲覧できない。テキスト形式で閲覧するには以下のコマンドを使用する。
#sudo openssl x509 -inform der -in CERTIFICATE.der -text -noout
☆ PKC12
鍵や証明書をひとつのファイルにまとめて取り扱うための形式。
[参考サイト]
(https://www.ssl.com/ja/%E3%82%AC%E3%82%A4%E3%83%89/pem-der-crt%E3%81%8A%E3%82%88%E3%81%B3cer-x-509%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89%E3%81%A8%E5%A4%89%E6%8F%9B/)
(https://shinkufencer.hateblo.jp/entry/2019/07/12/000000)
(3) クライアント端末のブラウザに証明書を埋め込む
通常はメールか何かで配布するのかな?今回はgoogle driveにアップロードして渡した。→なぜかできなかった。っていうかpfxファイルLinux側でも読み込み不可になってんだけどwww
おまけ
証明書のお手本として他のサイトの情報をみておくとよい。
#openssl s_client -connect www.google.co.jp:443
[参考書籍]
徹底攻略 改訂新版 LPIC303教科書+問題集
[参考サイトリンク集]
https://http2.try-and-test.net/openssl_simple_ca.html →一番分かりやすいかも
CentOSのプライベート認証局構築 (https://blog.apar.jp/linux/14057/)
Debianのプライベート認証局(Apache2)構築:1番正確かも!?
(https://stacktrace.hatenablog.jp/?page=1452342192)
(https://www.nslabs.jp/pki-making-two-tier-ca.rhtml)
(https://qiita.com/bashaway/items/ac5ece9618a613f37ce5)
(https://kitsune.blog/nginx-ssl)
Debianのfirewall/ufw
(https://www.mk-mode.com/blog/2015/06/19/debian-8-nginx-installation-by-official-apt/)
(https://www.hiroom2.com/2017/07/20/kalilinux-2017-1-ufw-ja/)