easy-rsaでプライベートCA(認証局)を作ってオレオレ証明書ではないローカル用TLS証明書の管理をする

Posted by 雅楽斎 on Sunday, January 24, 2021

TOC

オレオレ証明書だとどうしてもうまく行かないTLS証明書

昨今は全ての通信を暗号化する潮流になっており、この流れ自体は今後も変わらないことが想定されます。

インターネット上の通信は暗号化したほうが良いというのは説得力がありますが、LAN内の通信まで暗号化する必要があるのかという点には多少疑問があります。

インターネット上に公開しているサイトであればLet’s EncryptZeroSSLという無料のTLS証明書を発行してくれるサービスを使用できますが、インターネットに公開しない前提で運用するものが暗号化を要求した場合、基本的に自分で暗号化する以外の選択肢がありません。

オレオレ証明書と呼ばれる自己署名証明書1では何を間違っているのかどうしてもうまく行かないことがあり、プライベートCAを構築して、暗号化が必要な場合は活用していくことにしました。

easy-rsaというOpenVPNが開発しているプロジェクトがあり、これを使うとOpenSSLを直接叩くよりも楽ができそうということと、Ubuntuにeasyrsaパッケージがあるのでセットアップも簡単で使うことに決めました。環境はUbuntu 20.10です。

OpenVPN/easy-rsa

easy-rsa - Simple shell based CA utility. Contribute to OpenVPN/easy-rsa development by creating an account on GitHub.

今回のプライベートCAは基本的にDigital Oceanのサイトにあったものの手順をなぞっています。

Ubuntu 20.04で認証局(CA)をセットアップおよび設定する方法 | DigitalOcean

Ubuntu 20.04で実行するプライベート認証局は、クライアントとサーバー間の暗号化された接続を必要とするプログラムを設定、テスト、および実行できます。プライベートCAを使用して、インフラストラクチャ内のユーザー、サーバー、または個々のプログラムとサービスの証明書を発行できます。プライベートCAは、すべての開発、ステージング、および本番環境で同様のアーキテクチャと設定を使用できるようにするのに役立ちます。

作るもの・用途

名称用途サービス側
に配備
端末側
に配備
備考
CA(認証局)色んな人から受け取ったCSR(証明書署名要求)に対して、
内容に問題がなければ署名する
端末にはCRTをルート証明書として
インポートしてもらう
CAを信頼する端末がある限り運用すべき
CAの秘密鍵(.key)CSRに署名(、署名取り消し)する際に必要どこにも公開しない
CAのCRT
(公開鍵・
ルート証明書)
CAを信頼する端末にインポートする端末に配布する
秘密鍵(.key)端末が生成
CAに署名してもらったCRTとともにサービス提供時に必要
個人的には拡張子を.privkeyにしている
サービス公開側に配備する
CSR(証明書署名要求)端末が秘密鍵から生成
CAに提出して署名してもらう
CRT(公開鍵)CAがCSRに署名してもらった結果。
秘密鍵とともに公開するサービスで使用する
サービス公開側に配備する

CA(認証局)の構築

easy-rsaのインストール

# apt-get install easy-rsa

CA管理用のユーザーを作成

プライベートCAの操作を実行するための(rootではない)ユーザーを作成します。また、sudoを実行できるようにします。

# adduser ca
ユーザー `ca' を追加しています...
新しいグループ `ca' (1002) を追加しています...
新しいユーザー `ca' (1002) をグループ `ca' に追加しています...
ホームディレクトリ `/home/ca' を作成しています...
`/etc/skel' からファイルをコピーしています...
新しいパスワード: 
新しいパスワードを再入力してください: 
passwd: パスワードは正しく更新されました
ca のユーザ情報を変更中
新しい値を入力してください。標準設定値を使うならリターンを押してください
	フルネーム []: 
	部屋番号 []: 
	職場電話番号 []: 
	自宅電話番号 []: 
	その他 []: 
以上で正しいですか? [Y/n] 
# gpasswd -a ca sudo
ユーザ ca をグループ sudo に追加

以降のCA関連のコマンドは作成したcaユーザーで行います。

# su - ca
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
$ id
uid=1002(ca) gid=1002(ca) groups=1002(ca),27(sudo)

PKI(公開鍵インフラストラクチャ)ディレクトリの準備

easy-rsaを操作する準備をします。

$ mkdir ~/easy-rsa
$ ln -s /usr/share/easy-rsa/* ~/easy-rsa/
$ chmod 700 ~/easy-rsa
$ cd ~/easy-rsa
$ ./easyrsa init-pki

init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /home/ca/easy-rsa/pki

CAの作成

デフォルト値を記述した~/easy-rsa/varsというファイルを作成します。

set_var EASYRSA_REQ_COUNTRY    "JP"
set_var EASYRSA_REQ_PROVINCE   "Tokyo"
set_var EASYRSA_REQ_CITY       "Tokyo"
set_var EASYRSA_REQ_ORG        "PrivateCA"
set_var EASYRSA_REQ_EMAIL      "rpi4@internal"
set_var EASYRSA_REQ_OU         "Community"
set_var EASYRSA_ALGO           "ec"
set_var EASYRSA_DIGEST         "sha512"

CAの構築コマンドを実行します。

$ cd ~/easy-rsa
$ ./easyrsa build-ca

コマンド実行時、パスフレーズCN(Common Name)を入力します。パスフレーズはCAを使っている間は絶対に漏洩してはならないものなのでオフラインのどこかにメモして置くのがオススメ。2Common Nameはインターネット上で使う予定があれば自分の所有しているドメインにするのが良いでしょう。今回はLAN内のDNSサーバで管理しているinternalにします。

Note: using Easy-RSA configuration from: ./vars

Using SSL: openssl OpenSSL 1.1.1f  31 Mar 2020

Enter New CA Key Passphrase: 
Re-Enter New CA Key Passphrase: 
(snip)
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:internal

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/home/ca/easy-rsa/pki/ca.crt

これで2つのファイルが生成されます。

  • ~/easy-rsa/pki/ca.crt CAの公開鍵
  • ~/easy-rsa/pki/private/ca.key CAの秘密鍵

公開鍵は必要に応じて配布することで、暗号化されたコンテンツにアクセスできるようにします。

秘密鍵は絶対に誰にも見られてはいけないもので、CSRに署名する際に必要なものです。

以上でCAの作成は完了です。

使用する各端末にCAの公開鍵(ca.crt)をルート証明書としてインポート

システムにインポート

ここでは手元のUbuntu20.04に公開鍵をインポートします。

update-ca-certificates

ca.crtをscpでコピーするか中身のbase64の文字列をコピーして~/ca.crtというファイルに保存したら、/usr/local/share/ca-certificates/ディレクトリにコピーします。

$ sudo mv ca.crt /usr/local/share/ca-certificates/

端末上にCAの公開鍵を反映します。

# update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...

Adding debian:ca.pem
done.
Updating Mono key store
Mono Certificate Store Sync - version 6.12.0.107
Populate Mono certificate store from a concatenated list of certificates.
Copyright 2002, 2003 Motus Technologies. Copyright 2004-2008 Novell. BSD licensed.

Importing into legacy system store:
I already trust 138, your new list has 139
Import process completed.

Importing into BTLS system store:
I already trust 138, your new list has 139
Import process completed.
Done
done.

これで、この端末はCAが署名した証明書を信頼します。

$  ls -l /etc/ssl/certs/ | grep ca.pem
lrwxrwxrwx 1 root root      6  1月 20 22:57 2352eff5.0 -> ca.pem
lrwxrwxrwx 1 root root     39  1月 20 22:57 ca.pem -> /usr/local/share/ca-certificates/ca.crt

但し、ブラウザのキャッシュが関係しているのかわかりませんがこの状態で接続できるようになったのは手元ではEpiphanyのみでした。(後で確認したRocket.Chatの画面)

certutilコマンドで$HOME/.pki/nssdbにインポート

update-ca-certificatesコマンドで反映した後もChromium Edge(Dev)では状態が変わらなかった(かつ、後述するChromiumと違って証明書のインポート設定を開けなかった)ので、他にシステム的に反映させられそうなものを検索した結果、$HOME/.pki/nssdbがユーザー単位で操作できそうだったので、ここにインポートします。

libnss3-toolsをインストールします。

# apt-get install libnss3-tools 

現在格納されている証明書を見てみます。

$ certutil -d sql:$HOME/.pki/nssdb -L

Certificate Nickname                                         Trust Attributes
                                                             SSL,S/MIME,JAR/XPI

1つもありませんでした。/home/hogehoge/privateca.crtというファイル名の証明書をインポートします。-tで指定する属性はよくわかりませんでしたがとりあえずこれで動くことを確認しています。

$ certutil -d sql:$HOME/.pki/nssdb -A -t "CT,c," -n privateca -i /home/hogehoge/privateca.crt 

$ certutil -d sql:$HOME/.pki/nssdb -L

Certificate Nickname                                         Trust Attributes
                                                             SSL,S/MIME,JAR/XPI

privateca                                                    CT,c,

この後、Chromium Edge(Dev)を起動し直すかリロードすることで信頼されるようになることを確認できました。

Chromiumにインポート

Chromiumの場合どこからルート証明書を読み取っているのかわからなかったので設定画面から直接インストールします。Chromiumの場合の手順です。

システムではインポートしたはずですが、相変わらず信頼されていない状態が続いています。

Chromiumの設定から「セキュリティ」を選択します。

「詳細設定」にある「証明書の管理」を選びます。

「認証局」タブの「インポート」ボタンを押します。

CAの公開鍵ファイルを開きます。

信頼する種別を選択する画面になるので、今回はとりあえずウェブサイトの識別で信頼します。

インポートが終わると、internalというCAがインポートされたことがわかります。

信頼されていなかったタブに戻ってリロードすると、証明書が有効扱いに変更されたことがわかります。

Androidにインポート

CA公開鍵(privateca.crt)をAndroidにコピーして、「設定」→「セキュリティ」→「暗号化と認証情報」→「ストレージからのインストール」を選択します。

CAの公開鍵ファイルを選択して認証をすると、証明書の名前を指定する画面になります。

名前を指定してOKをタップすると、信頼できる認証情報に追加されます。

CSR(証明書署名要求)の作成とCAによる署名と取り消し

サービス提供側は秘密鍵とCRTを使ってサービスを提供します。CRTは秘密鍵の生成後、秘密鍵から作成されたCSRにCAが署名することで作成されます。

一般に使用するLinuxでは既にインストールされていることが多いですが、インストールされていない場合はopensslパッケージをインストールします。

# apt-get install openssl

秘密鍵の作成

適当に秘密鍵を作成します。今回はファイルのコピーが面倒なので、CAの端末内に一時的に作成するディレクトリで秘密鍵(example-server.key)を作成します。

$ mkdir ~/practice-csr
$ cd ~/practice-csr
$ openssl genrsa -out example-server.key
Generating RSA private key, 2048 bit long modulus (2 primes)
(snip)
e is 65537 (0x010001)

秘密鍵の内容を確認します。modulusが公開鍵そのもので、prime1prime2が秘密鍵です。

$ openssl rsa -text < example-server.key 
RSA Private-Key: (2048 bit, 2 primes)
modulus:
    00:c1:2c:6d:ab:c7:03:e5:96:f4:db:e9:57:bd:68:
(snip)
publicExponent: 65537 (0x10001)
privateExponent:
    22:84:cc:e8:38:3c:f3:06:e1:a4:76:73:0e:4e:a8:
(snip)
prime1:
    00:eb:5f:1b:79:a7:be:8c:e7:36:b0:82:b5:28:40:
(snip)
prime2:
    00:d2:1a:8c:d2:1c:4b:21:fe:05:b6:73:2f:0f:72:
(snip)
exponent1:
    00:af:a2:a0:d5:ff:1e:69:f6:7f:10:e7:f8:56:b9:
(snip)
exponent2:
    08:2c:cc:49:e8:9b:eb:c8:ac:84:3a:db:1b:e8:bf:
(snip)
coefficient:
    00:be:68:c5:bd:37:43:4e:8f:04:c1:7d:76:4b:88:
writing RSA key
-----BEGIN RSA PRIVATE KEY-----
(snip)
-----END RSA PRIVATE KEY-----

CSRの作成

作成した秘密鍵を元に、CSR(example-server.req)を作成します。この内容をCAが見てCSRに署名するため、基本的にはちゃんと作ります(今回はサンプルなので適当に入力しますが)

$ openssl req -new -key example-server.key -out example-server.req
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.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Tokyo
Organization Name (eg, company) [Internet Widgits Pty Ltd]:arekore
Organizational Unit Name (eg, section) []:Community
Common Name (e.g. server FQDN or YOUR name) []:internal
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

上記のように対話式で実行せずとも以下の様にopensslコマンドに引数で渡すことができます。

$ openssl req -new -key sammy-server.key -out server.req -subj /C=JP/ST=Tokyo/L=Tokyo/O=arekore/OU=Community/CN=internal

CSRの内容を確認します。

$ openssl req -in example-server.req -noout -subjectsubject=C = JP, ST = Tokyo, L = Tokyo, O = arekore, OU = Community, CN = internal

CSRを署名してもらうためにCAにコピーします。

$ sudo cp ~hogehoge/practice-csr/example-server.req /tmp/example-server.req

CAによる署名

CSRをインポートします。インポートが終了するとeasy-rsaコマンドのsign-reqオプションで署名できるようになります。

$ cd ~/easy-rsa/
$ ./easyrsa import-req /tmp/example-server.req example-server

Note: using Easy-RSA configuration from: ./vars

Using SSL: openssl OpenSSL 1.1.1f  31 Mar 2020

The request has been successfully imported with a short name of: example-server
You may now use this name to perform signing operations on this request.

今回はリクエストタイプをサーバー(server)にして署名します。申請内容を確認して問題がなければyesと入力し、CAのパスフレーズを入力します。

$ ./easyrsa sign-req server example-server

Note: using Easy-RSA configuration from: ./vars

Using SSL: openssl OpenSSL 1.1.1f  31 Mar 2020


You are about to sign the following certificate.
Please check over the details shown below for accuracy. Note that this request
has not been cryptographically verified. Please be sure it came from a trusted
source or that you have verified the request checksum with the sender.

Request subject, to be signed as a server certificate for 1080 days:

subject=
    countryName               = JP
    stateOrProvinceName       = Tokyo
    localityName              = Tokyo
    organizationName          = arekore
    organizationalUnitName    = Community
    commonName                = internal


Type the word 'yes' to continue, or any other input to abort.
  Confirm request details: yes
Using configuration from /home/ca/easy-rsa/pki/safessl-easyrsa.cnf
Enter pass phrase for /home/ca/easy-rsa/pki/private/ca.key:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'JP'
stateOrProvinceName   :ASN.1 12:'Tokyo'
localityName          :ASN.1 12:'Tokyo'
organizationName      :ASN.1 12:'arekore'
organizationalUnitName:ASN.1 12:'Community'
commonName            :ASN.1 12:'internal'
Certificate is to be certified until Jan  5 21:32:08 2024 GMT (1080 days)

Write out database with 1 new entries
Data Base Updated

Certificate created at: /home/ca/easy-rsa/pki/issued/example-server.crt

出来上がった/home/ca/easy-rsa/pki/issued/example-server.crtに公開暗号化キーとCAからの署名が含まれます。

$ openssl x509 -text -noout -in pki/issued/example-server.crt 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
(snip)
        Signature Algorithm: ecdsa-with-SHA512
        Issuer: CN = internal
        Validity
(snip)
        Subject: C = JP, ST = Tokyo, L = Tokyo, O = arekore, OU = Community, CN = internal
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus: 00:c1:2c:6d:ab:c7:03:e5:96:f4:db:e9:57:bd:68:
(snip)
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            X509v3 Subject Key Identifier: 
(snip)
            X509v3 Authority Key Identifier: 
(snip)
                DirName:/CN=internal serial:35:58:33:C9:EA:39:B2:01:49:5D
(snip)
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            X509v3 Key Usage: 
                Digital Signature, Key Encipherment
            X509v3 Subject Alternative Name: 
                DNS:internal
    Signature Algorithm: ecdsa-with-SHA512 30:64:02:30:33:77:3e:e6:28:f4:14:4a:36:00:07:c4:00:c8:
(snip)

サービス提供側は秘密鍵とこの署名されたCRTファイルを組み合わせることで、CAを信頼している各端末で通信させることができます。

署名の取り消し

CAが署名をしたものの、後からCRL(証明書失効リスト)を発行することで署名を取り消すことができます。

ここでは上の手順でインポートしたexample-serverを無効化します。

署名した時と同様、内容を確認してyesと打ち込んだ後、CAのパスフレーズを入力します。

$ cd ~/easy-rsa/
$ ./easyrsa revoke example-server

Note: using Easy-RSA configuration from: ./vars

Using SSL: openssl OpenSSL 1.1.1f  31 Mar 2020


Please confirm you wish to revoke the certificate with the following subject:

subject=
    countryName               = JP
    stateOrProvinceName       = Tokyo
    localityName              = Tokyo
    organizationName          = arekore
    organizationalUnitName    = Community
    commonName                = internal


Type the word 'yes' to continue, or any other input to abort.
  Continue with revocation: yes
Using configuration from /home/ca/easy-rsa/pki/safessl-easyrsa.cnf
Enter pass phrase for /home/ca/easy-rsa/pki/private/ca.key:
Revoking Certificate 3CB552D4968EED59.
Data Base Updated

IMPORTANT!!!

Revocation was successful. You must run gen-crl and upload a CRL to your
infrastructure in order to prevent the revoked cert from being accepted.

CRLを生成します。CAのパスフレーズが必要です。

$ ./easyrsa gen-crl

Note: using Easy-RSA configuration from: ./vars

Using SSL: openssl OpenSSL 1.1.1f  31 Mar 2020
Using configuration from /home/ca/easy-rsa/pki/safessl-easyrsa.cnf
Enter pass phrase for /home/ca/easy-rsa/pki/private/ca.key:

An updated CRL has been created.
CRL file: /home/ca/easy-rsa/pki/crl.pem
$ openssl crl -in pki/crl.pem -text
Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: ecdsa-with-SHA512
        Issuer: CN = internal
(snip)
        CRL extensions:
            X509v3 Authority Key Identifier:    (snip)
                DirName:/CN=internal
(snip)
Revoked Certificates:
    Serial Number: 3CB552D4
        Revocation Date: Jan 20 18:59:54 2021 GMT
    Signature Algorithm: ecdsa-with-SHA512 30:66:02:31:00:83:48:df:9d:63:6e:4c:77:b9:9c:95:6a:31:
(snip)
-----BEGIN X509 CRL-----
(snip)
-----END X509 CRL-----

生成されたcrl.pemファイルをCAをインポートした端末にコピーし、CRLのインポートを行うことで該当端末での失効を行うことができます。

ということになっていますが、CRLファイルがOSでハンドリングできる場合はともかく、コマンドからインポートする方法がよくわかりませんでした。

スポンサーリンク


  1. 「オレが保証するから安心だよ!」という証明書。IT界隈に於いて圧倒的なネーミングセンスだと思っています [return]
  2. 設定しないこともできます [return]

comments powered by Disqus