お金をかけずにサーバーの勉強をしよう

プライベート認証局

2023年4月17日

メニューへ戻る

お勉強環境の WEBサーバーで httpsを使いたい時、WEBブラウザでサーバー証明の注意が出るのを回避します。

サーバーのお勉強をしていると、WEBサーバーで httpsを使いたいことが多々あります。

特に昨今の WEBブラウザはセキュリティ対策を厳しくしてきていますので、httpsかつサーバー証明書も正当なものじゃないと面倒だったりします。


インターネットに公開しているサーバーであれば Let's Encriptでフリーのサーバー証明書を取るのですが、自宅LANに作っているテストサーバーではドメインもありませんのでこれは使えません。

そこで自宅LANにプライベート認証局を作って、テスト用サーバーの証明をしてあげようというのがここでの試みです。


本物の認証局は国(日本はデジタル庁らしい)などの厳格な審査を通っている会社で、サーバー証明書に付随させる署名を売っています。

例えば、私はお客さんの依頼で DegiCertって所から買った事があります。

手続きの過程で、この会社(デジサート・ジャパン合同会社だと思います)から、お客さんの所に電話で「あなたは○○社の△△様ですよね?」って確認の問い合わせがあったようです。

そんな手続きを踏んで、

と明らかになっています。

関係性はよくこんな図で説明されています。
認証局の関係 1

httpsの通信では、WEBサーバーから「ここの認証局に署名してもらいました」というデータが届くのですが、この際に③で貰った認証局の証明書と突合させてその正当性を確認します。

でもこれまでインターネットで散々 httpsを使って通信していますが、認証局の証明書なんて貰った覚えはありません。

なので以前私は https通信時に認証局にも通信していると誤認していました。
実際の通信における登場人物はこの二者のみです。
認証局の関係 2

どうりで認証局の証明書を貰った覚えがないはずです。
WEBブラウザに予め入っているのですから。

これは Firefoxの例です。

[設定]メニューから [プライバシーとセキュリティ]を選ぶと [証明書]って段落があります。
そこの証明書を表示…を押します。
Firefoxの公開鍵 1

[認証局証明書]タブに予め入っている証明書が羅列されます。
上に出てきた DigiCertがあることも分かります。
Firefoxの公開鍵 2

こうして有名な認証局(そもそも数が多くない)の証明書は WEBブラウザと一緒に入っているので、我々は特に気にせず上の仕組みを利用できるのでした。


さて、翻って本物の認証局を使わずに、家庭内LANで同じ仕組みを実現する(プライベート認証局と言います)にはどうするかなんですが、実は本物の認証局がやってる上の図の②を自分でやるのです。
そして肝は、③が手動になることです。

③が手動になるということは、使用する WEBブラウザ毎に手作業をしなければなりません。

家庭内LANではやり切れますが、小さな企業でも難しいと思います。
これが企業内LANでプライベート認証局が使われない理由の一つかと思います。


ここの実験では、上の図の関係を2つの OS環境で実現することにします。
ルート証明書の実験環境
現実の環境を考えると本来は認証局と WEBブラウザを動かすクライアントPCは別にするべきなんでしょうが、認証局って署名のデータを作る作業場であって OSとして独立していなければいけない訳でない概念的なものですので。

ここからの記述は便宜上以下とします。

Lubuntu 22.04(マシン名は Lubuntu2204) → Lubuntu
Ubuntu Server 22.04.2(マシン名は UbuntuServer2204-1) → Ubuntu Server
プライベート認証局 → 認証局
では、具体的にやってみます。


1.プライベート認証局を作る

Lubuntuでの作業になります。

ブログ等では認証局を「作る」「立てる」とかいう言葉で説明されることが多くて私は混乱しました。
読み進めるうち難しい観念を露わにしないように言葉を選んでいることは分かりましたが、中々に難解です。

結局のところ具体的には以下2つのことをしているだけです。

本物の認証局も根本的にはこれと同じ事をしていて「本物の認証局が信用できるって誰が担保してんの?」という疑問が湧きますが、国家等が担保しているって偉い人が書いていました。
(詳しくは知りません)

認証局のデータを作るにあたり、Lubuntu・Ubuntu Serverともに open-sslのセットと思われる [/usr/lib/ssl/misc/CA.pl]ファイルという open-sslを簡単に使うための Perlスクリプトがあります。

Canonicalが出してる Ubuntu用のマニュアルはこちら。
CA.pl

これを使うと認証局としてのデータを管理するディレクトリ階層とファイル作成を自動で作ってくれます。
open-sslを使う限りこのディレクトリ階層で管理をした方が汎用性があると思われます。

認証局を作るブログ記事を見ていると、多くの人が [root]ユーザーで作業していますが、必須ではなさそうなのであえて一般ユーザーの [subro] でやってみました。
ユーザーに合わせて、デフォルトの [/etc/ssl]ディレクトリの代わりに [/home/subro/work/ssl]ディレクトリを作業場所としています。

subro@Lubuntu2204:~/work/ssl$ /usr/lib/ssl/misc/CA.pl -newca
CA certificate filename (or enter to create)Enterを押す

Making CA certificate ...
====
秘密鍵ファイルを作る & SSLサーバ証明申請(CSR)ファイルを作る
openssl req  -new -keyout ./demoCA/private/cakey.pem -out ./demoCA/careq.pem
..+...++++++++++++++++++++++++++++++++++++ 〜〜〜 長いので省略 〜〜〜
Enter PEM pass phrase:秘密鍵ファイルの新規パスワードを設定
Verifying - Enter PEM pass phrase:パスワードをもう一回
多分この時点で秘密鍵ファイルが demoCA/private/cakey.pem としてできている
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
認証局(Lubuntu)のSSLサーバ証明申請(CSR)ファイルに埋め込む情報です。
適当に入れていますが、[Common Name]だけは何か入ってないとダメでした。
Country Name (2 letter code) [AU]:JA
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Chiyoda-ku
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Subro Inc.
Organizational Unit Name (eg, section) []:Management
Common Name (e.g. server FQDN or YOUR name) []:Lubuntu2204
Email Address []:空にしました

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:空にしました
An optional company name []:空にしました
==> 0
====
====
上のSSLサーバ証明申請(CSR)に対して、上で作った秘密鍵ファイルを使って証明(署名)。
ここら辺が俺俺。
openssl ca  -create_serial -out ./demoCA/cacert.pem -days 1095 -batch -keyfile ./demoCA/private/cakey.pem -selfsign -extensions v3_ca -infiles ./demoCA/careq.pem
Using configuration from /usr/lib/ssl/openssl.cnf
Enter pass phrase for ./demoCA/private/cakey.pem:上で設定した秘密鍵ファイルのパスワードを入れる
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number:
            72:0d:75:0a:2c:57:95:46:91:ff:37:e6:67:6e:56:a0:31:05:18:61
        Validity
            Not Before: Apr 17 06:52:56 2023 GMT
            Not After : Apr 16 06:52:56 2026 GMT
        Subject:
            countryName               = JA
            stateOrProvinceName       = Tokyo
            organizationName          = Subro Inc.
            organizationalUnitName    = Management
            commonName                = Lubuntu2204
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                8B:D6:C9:48:68:6A:EC:F2:BF:C5:07:F9:89:F9:0D:B0:94:0D:AC:FE
            X509v3 Authority Key Identifier:
                8B:D6:C9:48:68:6A:EC:F2:BF:C5:07:F9:89:F9:0D:B0:94:0D:AC:FE
            X509v3 Basic Constraints: critical
                CA:TRUE
Certificate is to be certified until Apr 16 06:52:56 2026 GMT (1095 days)

Write out database with 1 new entries
Data Base Updated
==> 0
====
CA certificate is in ./demoCA/cacert.pem

できました。

結果このようなものが。

subro@Lubuntu2204:~/work/ssl$ tree
.
└── demoCA
    ├── cacert.pem  自分で署名した(俺俺)自分のサーバー証明書
    ├── careq.pem  このサーバーのSSLサーバ証明申請(CSR)
    ├── certs
    ├── crl
    ├── crlnumber
    ├── index.txt  証明局として証明した履歴のデータベース
    ├── index.txt.attr
    ├── index.txt.old
    ├── newcerts
    │   └── 720D750A2C57954691FF37E6676E56A031051861.pem  cacert.pemと同じもの。署名したものの履歴と思われる。
    ├── private
    │   └── cakey.pem  このサーバーの秘密鍵
    └── serial

[demoCA] というディレクトリ名はマニュアルを読む限り固定のようでした。
この名前は変えてしまっても良いようです。

[index.txt]ファイルが簡易データベースらしいので中を見てみました。

subro@Lubuntu2204:~/work/ssl$ cat index.txt
V       260416065256Z           720D750A2C57954691FF37E6676E56A031051861        unknown /C=JA/ST=Tokyo/O=Subro Inc./OU=Management/CN=Lubuntu2204

何となく…分かるような分からないような。


2.サーバー証明書の証明(署名)依頼を出す

Ubuntu Server(WEBサーバー)での作業です。

こちらではこの OS環境に対して 1回作業すれば良いので、[root]ユーザーでやっています。
ファイルの配置場所もデフォルトの [/etc/ssl]ディレクトリにしています。

subro@UbuntuServer2204-1:/etc/ssl$ sudo /usr/lib/ssl/misc/CA.pl -newreq
Use of uninitialized value $1 in concatenation (.) or string at /usr/lib/ssl/misc/CA.pl line 145.
====
Ubuntu Serverの秘密鍵を作る & このサーバーのSSLサーバ証明申請(CSR)を作る
openssl req  -new  -keyout newkey.pem -out newreq.pem -days 365
Ignoring -days without -x509; not generating a certificate
..+..+............+......++++++++++++  〜〜〜 長いので省略 〜〜〜
Enter PEM pass phrase:Ubuntu Serverの秘密鍵の新規パスワードを入れる
Verifying - Enter PEM pass phrase:パスワードをもう一回
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
このサーバーのSSLサーバ証明申請(CSR)に埋め込む情報です。本当はちゃんと入れないとダメなところ。
家庭内LAN用だから適当に入れていますが、Common Nameだけは実体に沿ったものにしないといけません。
もしこの WEBサーバーに https://ubuntuserver2204-1/ とアクセスするなら、「ubuntuserver2204-1」とします。
Country Name (2 letter code) [AU]:JA
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Minato-ku
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Subro WebService Ltd.
Organizational Unit Name (eg, section) []:Web Developer
Common Name (e.g. server FQDN or YOUR name) []:ubuntuserver2204-1  ←ここが重要
Email Address []:空にしました

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:空にしました
An optional company name []:空にしました
==> 0
====
Request is in newreq.pem, private key is in newkey.pem

できました。

このようなものが。

subro@UbuntuServer2204-1:/etc/ssl$ ls -l
合計 40
drwxr-xr-x 3 root root     12288  3月 10 19:28 certs
-rw------- 1 root root      1854  4月 17 21:25 newkey.pem  Ubuntu Serverの秘密鍵
-rw-r--r-- 1 root root      1045  4月 17 21:26 newreq.pem  Ubuntu Serverの SSLサーバ証明申請(CSR)
-rw-r--r-- 1 root root     12419  2月  7 02:57 openssl.cnf
drwx--x--- 2 root ssl-cert  4096  4月 17 21:25 private

subro@UbuntuServer2204-1:/etc/ssl$ sudo ls -l private
合計 4
-rw-r----- 1 root ssl-cert 1704  3月 10 19:10 ssl-cert-snakeoil.key

このうち [newkey.pem]ファイルの内容は外に漏れるとマズいので、[private]ディレクトリの下(rootしか見ることができない)に移動しつつ名前も [private.pem] と変更しました。

subro@UbuntuServer2204-1:/etc/ssl$ sudo mv newkey.pem ./private/private.pem

subro@UbuntuServer2204-1:/etc/ssl$ sudo ls -l private
合計 8
-rw------- 1 root root     1854  4月 17 21:25 private.pem
-rw-r----- 1 root ssl-cert 1704  3月 10 19:10 ssl-cert-snakeoil.key

移動とファイル名変更ができました。

[ssl-cert]グループのメンバがこのファイルを利用できるよう、[private.pem]ファイルのグループとパーミッションも変更します。
(後で NGINXがこのファイルを読めるようにするため)

subro@UbuntuServer2204-1:/etc/ssl$ sudo chgrp ssl-cert ./private/private.pem

subro@UbuntuServer2204-1:/etc/ssl$ sudo chmod 640 ./private/private.pem

subro@UbuntuServer2204-1:/etc/ssl$ sudo ls -l private
合計 8
-rw-r----- 1 root ssl-cert 1854  4月 17 16:07 private.pem
-rw-r----- 1 root ssl-cert 1704  3月 10 19:10 ssl-cert-snakeoil.key



3.プライベート認証局で署名してもらう

Lubuntuでの作業になります。

Ubuntu Serverにできた SSLサーバ証明申請(CSR)である [newreq.pem]ファイルをどのような方法でも良いので Lubuntuに持っていきます。
私は scpで持っていきました。

この通り Lubuntuの認証局のディレクトリ [/home/subro/work/ssl] に持ってきました。

subro@Lubuntu2204:~/work/ssl$ ls -l
total 8
drwxrwxr-x 6 subro subro 4096  4月 17 16:33 demoCA
-rw-r--r-- 1 subro subro 1045  4月 17 21:42 newreq.pem

新しい WEBブラウザ相手ですと、SANs(Subject Alternative Names)というのを設定しておかないと正しい証明書とみなされないようなので、これの対応をします。

まず任意の名前のファイル(ここでは [sans.ext] としました)を作り、以下の内容を書きます。

subro@Lubuntu2204:~/work/ssl$ cat sans.ext
subjectAltName=DNS:ubuntuserver2204-1

それから CA.plを経て open-sslにこのファイルを読ませるオプションを渡すため、[OPENSSL_CONFIG]環境変数を設定します。

subro@Lubuntu2204:~/work/ssl$ export OPENSSL_CONFIG="-extfile sans.ext"

署名します。
CA.pl ではカレントディレクトリにある [newreq.pem]ファイルをデフォルトで読むようになっています。

subro@Lubuntu2204:~/work/ssl$ /usr/lib/ssl/misc/CA.pl -sign
====
open-sslコマンドに[sans.ext]ファイルを読ませるオプションが渡っています
openssl ca -extfile sans.ext -policy policy_anything -out newcert.pem -infiles newreq.pem
Using configuration from /usr/lib/ssl/openssl.cnf
Enter pass phrase for ./demoCA/private/cakey.pem:認証局の秘密鍵のパスワードを入れる
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number:
            72:0d:75:0a:2c:57:95:46:91:ff:37:e6:67:6e:56:a0:31:05:18:65
        Validity
            Not Before: Apr 17 14:01:59 2023 GMT
            Not After : Apr 16 14:01:59 2024 GMT
        Subject:
            countryName               = JA
            stateOrProvinceName       = Tokyo
            localityName              = Minato-ku
            organizationName          = Subro SebService Ltd.
            organizationalUnitName    = Web Developer
            commonName                = ubuntuserver2204-1
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                DNS:ubuntuserver2204-1
Certificate is to be certified until Apr 16 14:01:59 2024 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
==> 0
====
Signed certificate is in newcert.pem

できました。

署名つきのサーバー証明書である [newcert.pem]ファイルができています。

subro@Lubuntu2204:~/work/ssl$ ls -l
total 20
drwxrwxr-x 6 subro subro 4096  4月 17 23:02 demoCA
-rw-rw-r-- 1 subro subro 4546  4月 17 23:02 newcert.pem
-rw-r--r-- 1 subro subro 1045  4月 17 21:42 newreq.pem
-rw-rw-r-- 1 subro subro   38  4月 17 22:41 sans.ext

サーバー証明した履歴のデータベースファイルはこのようになっていました。

subro@Lubuntu2204:~/work/ssl$ cat ./demoCA/index.txt
V       260416065256Z           720D750A2C57954691FF37E6676E56A031051861        unknown /C=JA/ST=Tokyo/O=Subro Inc./OU=Management/CN=Lubuntu2204
V       240416140159Z           720D750A2C57954691FF37E6676E56A031051865        unknown /C=JA/ST=Tokyo/L=Minato-ku/O=Subro SebService Ltd./OU=Web Developer/CN=ubuntuserver2204-1

この [newcert.pem]ファイルをどうにかして Ubuntu Serverに持っていきます。
私はscp(以下略)

Ubuntu Serverでは今回作ったものと分かるよう、新規作成した [/etc/ssl/ubuntuserver2204-1]ディレクトリに入れ、オーナー・グループ・パーミッションを秘密鍵と同じにしました。

subro@UbuntuServer2204-1:/etc/ssl$ ls -l ubuntuserver2204-1
合計 8
-rw-r----- 1 root ssl-cert 4546  4月 17 23:04 newcert.pem



4.NGINXでサーバー証明書を使う

Ubuntu Serverでの作業になります。

NGINXが Ubuntu Serverで動いている前提です。
なお NGINXのインストールについては「NGiNXサーバーを作る」に書いております。

NGINXで SSLを有効にするため、手っ取り早く [/etc/nginx/sites-available/default] という設定ファイルをイジってしまいます。

server {
        #listen 80 default_server;    コメントアウト
        #listen [::]:80 default_server;  コメントアウト
        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;
        server_name _;
        ssl on;    SSL有効化
        ssl_certificate     /etc/ssl/ubuntuserver2204-1/newcert.pem;    Lubuntuで署名してもらった Ubuntu Serverのサーバー証明書
        ssl_certificate_key /etc/ssl/private/private.pem;         Ubuntu Serverの秘密鍵
        ssl_password_file   /etc/nginx/ssl/ubuntuserver2204-1.password;  Ubuntu Serverの秘密鍵のパスワードが書いてあるファイル

        location / {
                try_files $uri $uri/ =404;
        }
}

Ubuntu Serverの秘密鍵 [/etc/ssl/private/private.pem]ファイルはパーミッションをかなりキツくしているので、NGINXのワーカープロセスを実行している [www-data]ユーザーから読めません。
先程 [ssl-sert]グループのメンバなら読めるようにしておいたので、このグループに [www-data]ユーザーを追加します。
[/etc/group]ファイルの [ssl-cert]グループの行を編集します。

ssl-cert:x:119:www-data

Ubuntu Serverの秘密鍵 [/etc/ssl/private/private.pem]ファイルはパスワード設定されていますので、利用時にパスワードを入れないと使うことができません。

NGINXでは当該パスワードを記載したファイルを作っておくことでこの作業を自動でやってくれます。
このために任意のファイルを作って、そこにパスワードをテキストで書いておきます。
ここでは [/etc/nginx/ssl/ubuntuserver2204-1.password]ファイルとしました。
大事なパスワードなのでこのファイルもまたオーナー・グループ・パーミッションをキツくしています。

subro@UbuntuServer2204-1:/etc/ssl$ ls -l /etc/nginx/ssl
合計 4
-rw-r----- 1 root ssl-cert 10  4月 17 23:27 ubuntuserver2204-1.password

NGINXを再起動します。

subro@UbuntuServer2204-1:/etc/ssl$ sudo systemctl restart nginx

試しに WEBブラウザでアクセスしてみます。
我が家では [https://UbuntuServer2204-1] です。
安全ではありません
とりあず NGINXは httpsでのアクセスを受けてはいますが、サーバー証明書が信用できない状態です。

これは WEBブラウザに上で作ったプライベート認証局(Lubuntu)の証明書が無いからですね。


5.Firefoxにプライベート認証局の証明書を仕込む

Lubuntuでの作業になります。

現在の認証局のディレクトリ [/home/subro/work/ssl] の内容はこう。

subro@Lubuntu2204:~/work/ssl/demoCA$ ls -l
total 56
-rw-rw-r-- 1 subro subro 4421  4月 17 15:52 cacert.pem  認証局のサーバー証明書
-rw-rw-r-- 1 subro subro 1017  4月 17 15:52 careq.pem
drwxrwxr-x 2 subro subro 4096  4月 17 15:51 certs
drwxrwxr-x 2 subro subro 4096  4月 17 15:51 crl
-rw-rw-r-- 1 subro subro    3  4月 17 15:51 crlnumber
-rw-rw-r-- 1 subro subro  279  4月 17 23:02 index.txt
-rw-rw-r-- 1 subro subro   21  4月 17 23:02 index.txt.attr
-rw-rw-r-- 1 subro subro   21  4月 17 21:45 index.txt.attr.old
-rw-rw-r-- 1 subro subro  123  4月 17 23:01 index.txt.old
drwxrwxr-x 2 subro subro 4096  4月 17 23:02 newcerts
drwxrwxr-x 2 subro subro 4096  4月 17 15:51 private
-rw-rw-r-- 1 subro subro   41  4月 17 23:02 serial
-rw-rw-r-- 1 subro subro   41  4月 17 21:45 serial.old

Firefoxの [設定]メニューから [プライバシーとセキュリティ]を選ぶと [証明書]って段落があります。
そこの証明書を表示…を押します。
Firefoxに認証局の証明書を取り込み 1

[認証局証明書]タブを開きインポートを押します。
Firefoxに認証局の証明書を取り込み 2
ファイラーが出てくるので、[/home/subro/work/ssl/demoCA/cacert.pem]ファイルを指定しました。

[この認証局によるウェブサイトの識別を信頼する]にチェックし、OKを押します。
Firefoxに認証局の証明書を取り込み 3

しますと証明書一覧に「Subro. Inc.」が現れました。ハハッ ワロス
Firefoxに認証局の証明書を取り込み 4

それで改めてアクセスしてみましたら、この通り警告なしで NGINXの画面になりました。
警告なしのアクセス完了

赤いところをクリックするとこのような表示が出ました。
一応 Firefoxが最初から持っている証明書以外のものという区別はされているようです。
証明書の説明

欲しい結果が得られました。


==========
かなり長くなってしまいました。
結構難しくて、実際ここまで完遂させるまでに結構ハマっています。

色々なブログやら公式のマニュアルやらを読んでどうにかここまできましたが、理解を難しくしていたのは「どこで其の作業をするのか?」という点がどうにもハッキリしていなかったことです。

1つの OS上でできはするんですが、ここの実験のように認証局・WEBサーバー・WEBブラウザを分離した場合、それぞれの作業が必須のものなのか同一の OS上でやっているからそうなのかの切り分けが中々できませんでした。

この実験を通してどうにか理解することができたのですが、まだまだ理解が浅いと感じましたね。

でもまぁとりあえずの目的は果たせて、今後のお勉強時に httpsを使えるようになったと思いますので、またここに各ネタを増やせそうです。


技術書典で出てる同人誌みたい。