一般のご家庭でもプライベート認証局を運用したい

はじめに

特に深い意味はないのですが、宅内で運用しているサービスのURL欄に鍵マークを付けたかったのでopensslコマンドでプライベート認証局を構築する方法を確認してみました。

構築する認証局の概要

真面目に運用しようとすると結構めんどくさいのでゆるふわっと運用します。

  • Subject Alternative NameはIPアドレスにする
    • だって宅内でDNSサーバ運用していないし・・・
  • CRLとかOCSPレスポンダは運用しない
  • ルート証明書から各サーバ用の証明書を作る
    • かなり小規模なので、いざとなったら(ry

証明書発行用のサーバは証明書発行専用だし使わないときはオフラインなので、ま、多少はね?

環境

だいたい以下の感じです。

NAME="Ubuntu"
VERSION="20.04.2 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.2 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
OpenSSL 1.1.1f  31 Mar 2020
built on: Wed Feb 17 12:35:54 2021 UTC
platform: debian-amd64
options:  bn(64,64) rc4(16x,int) des(int) blowfish(ptr) 
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-g4Ey0B/openssl-1.1.1f=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_TLS_SECURITY_LEVEL=2 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAESNI_ASM -DVPAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2
OPENSSLDIR: "/usr/lib/ssl"
ENGINESDIR: "/usr/lib/x86_64-linux-gnu/engines-1.1"
Seeding source: os-specific

ルートCA

とりあえずルート証明書用の設定ファイルを書きます。

[default]
name                    = root-ca
default_ca              = ca_default
name_opt                = utf8,esc_ctrl,multiline,lname,align

[ca_dn]
countryName             = JP
stateOrProvinceName     = Ibaraki
organizationName        = Weyland-Yutani Corporation
commonName              = Weyland-Yutani RootTrust

[ca_default]
home                    = .
database                = $home/db/index
serial                  = $home/db/serial
crlnumber               = $home/db/crlnumber
certificate             = $home/$name.crt
private_key             = $home/private/$name.key
RANDFILE                = $home/private/random
new_certs_dir           = $home/certs
unique_subject          = no
copy_extensions         = copy
default_days            = 365
default_md              = sha256
policy                  = policy_c_o_match

[policy_c_o_match]
countryName             = match
stateOrProvinceName     = optional
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[req]
default_md              = sha256
utf8                    = yes
string_mask             = utf8only
prompt                  = no
distinguished_name      = ca_dn
req_extensions          = ca_ext

[ca_ext]
basicConstraints        = critical,CA:true
keyUsage                = critical,keyCertSign,cRLSign
subjectKeyIdentifier    = hash

[server_ext]
authorityKeyIdentifier  = keyid:always
basicConstraints        = critical,CA:false
extendedKeyUsage        = clientAuth,serverAuth
keyUsage                = critical,digitalSignature,keyEncipherment
subjectKeyIdentifier    = hash

そうしたら色々ディレクトリを切ったりシリアルを初期したりします。

~/ca/root$ mkdir certs db private
~/ca/root$ chmod 700 private
~/ca/root$ touch db/index
~/ca/root$ openssl rand -hex 16 > db/serial
~/ca/root$ echo 1001 > db/crlnumber

RSA 4096ビットで秘密鍵を作成し、いちおうAES 256ビットで保護しておきます。*1

~/ca/root$ openssl genpkey \
    -algorithm rsa \
    -pkeyopt rsa_keygen_bits:4096 \
    -aes256 \
    -out private/root-ca.key
........................................++++
............................................................................................................++++
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

ルート証明書CSRを作成します。

~/ca/root$ openssl req \
    -new \
    -config root-ca.conf \
    -out root-ca.csr \
    -key private/root-ca.key
Enter pass phrase for private/root-ca.key:

有効期限10年で自己署名証明書を作ります。

~/ca/root$ openssl ca \
    -selfsign \
    -config root-ca.conf \
    -in root-ca.csr \
    -out root-ca.crt \
    -days 3650 \
    -extensions ca_ext

ルート証明書側の初期化はこれで完了です。

サーバ証明書

次にサーバ証明書を作成していきます。

似たような感じに秘密鍵を作成しますが、鍵をデプロイする際に面倒なのでこっちはAESでの保護はしません。*2

~/ca/192_168_2_53$ openssl genpkey \
    -algorithm rsa \
    -pkeyopt rsa_keygen_bits:4096 \
    -out 192_168_2_53.key

CSR生成用にちょろっと設定を書きます。

[req]
prompt                  = no
distinguished_name      = dn
req_extensions          = ext

[dn]
countryName             = JP
stateOrProvinceName     = Ibaraki
organizationName        = Weyland-Yutani Corporation
commonName              = 192.168.2.53

[ext]
subjectAltName          = @san

[san]
DNS.1                   = localhost
IP.1                    = 127.0.0.1
IP.2                    = ::1
IP.3                    = 192.168.2.53

CSRを生成します。

~/ca/192_168_2_53$ openssl req \
    -new \
    -config 192_168_2_53.conf \
    -key 192_168_2_53.key \
    -out 192_168_2_53.csr

最後にルート証明書側で署名します。

~/ca/root$ openssl ca \
    -config root-ca.conf \
    -in ../192_168_2_53/192_168_2_53.csr \
    -out ../192_168_2_53/192_168_2_53.crt \
    -extensions server_ext

サーバへのインストール

サーバにサーバ証明書をインストールしつつクライアントの「信頼されたルート証明書」にルート証明書をインストールすればこうじゃ。

f:id:jyuch:20210317223924p:plain

おわり

*1:意味があるかと言われると、まぁ、その・・・

*2:詳細はよく分からないのですが、ロード済みの秘密鍵はメモリ上で目立つのでメモリダンプを採られれば一発でどれが秘密鍵かがわかるそうです。つまり、サーバに侵入された時点で詰みです。