プライベート認証局(CA)にてクライアント証明書の発行

# ■はじめに
## ▽目的
### クライアント証明書でアクセス制限！

概要
・サーバやクライアント認証の証明書を発行するため、プライベートCAを構築
・作成したクライアント証明書(プライベートCAの署名付き)を利用してwebコンテンツへのアクセスを制限

補足
・本項、実際に私が実装した際の手順を記載したものになります。
・公式Document や 各コマンドのマニュアル等を読み込み、そこから手順を起こした。とういわけではなくインターネット上の情報を私なりに整理したという程度の内容になりますため、同じ環境の方の参考になれば幸いです。

## ▽今回の実装環境
CentOS `6.5`
openssl `1.0.1e`
mod_ssl `2.2.15`
apache  `2.2.15`

# ■準備
## ▽必要なパッケージ
httpd
openssl
mod_ssl

・導入していなければインストール

```bash
# yum install mod_ssl openssl httpd
```

## ▽CSR情報を事前に確認

```text
例: プライベート用のため何でもよい
■Country									JP
■State or Province Name (full name) []:	Tokyo
■Locality Name (eg, city) [Default City]:	Minato-ku
■Organization			company, inc.
■Organizational Unit	Technical dept.
■Common Name			secure.example.com
```

## ▽各パスフレーズを用意(整理のため)

```text
プライベート認証局:    passphrase_ca
サーバ証明書:          passphrase_server
クライアント証明書:    passphrase_client
PCL12ファイル展開用:   passphrase_pcl12
```

# ■1. プライベートCA構築

■作成予定のもの
1) /etc/pki/CA/private/cakey.pem : CA秘密鍵
2) /etc/pki/CA/cacert.pem :        CA公開鍵 & CA証明書


## ▽プライベートCAを構築する準備1：CAの編集
※```/etc/pki/tls/misc/CA``` → 認証局作成および各証明書への署名に利用するスクリプト

```bash
# cp -p /etc/pki/tls/misc/CA /etc/pki/tls/misc/CA.org
# vi /etc/pki/tls/misc/CA
----------
# 有効期限を伸ばす。以下の値を3650(10年)へ
DAYS="-days 365"    # 1 year
CADAYS="-days 1095" # 3 years
↓
DAYS="-days 3650"    # 10 years
CADAYS="-days 3650" # 10 years
```

※変更にsedを使っても可

```bash
sed -i "s/365/3650/g" /etc/pki/tls/openssl.cnf　※後述
sed -i "s/365/3650/g" /etc/pki/tls/misc/CA
sed -i "s/1095/3650/g" /etc/pki/tls/misc/CA
```

## ▽プライベートCAを構築する準備2：openssl.cnfの編集

※この設定ファイルはCAの構築以外にも、OpenSSL全般の動作に利用されます。

```bash
# cp -p /etc/pki/tls/openssl.cnf /etc/pki/tls/openssl.cnf.org
# vi /etc/pki/tls/openssl.cnf
--------------------
[ CA_default ]
default_days    = 3650                  # how long to certify for　←上記で対応した内容

[ req_distinguished_name ]　# CSRのデフォルト情報を設定しておく(上述のCSR情報)
countryName_default             = JP
stateOrProvinceName_default    = Tokyo
localityName_default    = Minato-ku
0.organizationName_default      = company, inc.
organizationalUnitName_default = Technical dept.

※commonName は、証明書毎に変えるためデフォルト値を設定していません
```

## ▽プライベートCAの構築

■(1) 認証局(CA)用として opensslの設定ファイルを用意

```bash
# cp -p /etc/pki/tls/openssl.cnf /etc/pki/tls/openssl-ca.cnf
# vi /etc/pki/tls/openssl-ca.cnf
-----
[ usr_cert ]
basicConstraints=CA:TRUE  # デフォルトでCA証明書として使用

[ v3_ca ]
nsCertType = sslCA, emailCA  # デフォルトでSSL認証局、電子メール認証局とする
```
　
■(2)プライベート認証局のCA証明書を作成

```bash
# cd /etc/pki/tls/
# CADAYS="-days 3650" SSLEAY_CONFIG="-config /etc/pki/tls/openssl-ca.cnf" /etc/pki/tls/misc/CA -newca
# ※一応コマンドのオプションに有効期限3650日を渡しています。
........+++
writing new private key to '/etc/pki/CA/private/./cakey.pem'
Enter PEM pass phrase:                    # プライベートCA用のパスフレーズ 
Verifying - Enter PEM pass phrase:        # 再入力
-----
# CSR情報入力。上記で設定したデフォルト([]内表示)で良ければ、そのままRETURN
Common Name (eg, your name or your server's hostname) []: Mitzi Private CA  # CAの名前(任意)
-----
Enter pass phrase for /etc/pki/CA/private/./cakey.pem: # 先に入力したパスフレーズ（パスワード）を再入力

#証明書の概要が表示

Certificate is to be certified until Nov 26 14:23:47 2024 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated
```

　
・作成されたファイルの確認

```bash
# ls -1 /etc/pki/CA/*
/etc/pki/CA/cacert.pem  # CA証明書(CAのcakey.pem で署名した公開鍵)
/etc/pki/CA/careq.pem   # CAのCSR (CAの自己署名する前の公開鍵)
/etc/pki/CA/index.txt   # CA管理ファイル（証明書管理情報）
/etc/pki/CA/index.txt.attr  # CA管理ファイル（属性）
/etc/pki/CA/index.txt.old   # CA管理ファイル（一つ前）
/etc/pki/CA/serial

/etc/pki/CA/certs:   # 証明書ディレクトリ

/etc/pki/CA/client:　# クライアント証明書用に作成したディレクトリ(後述)
certs
private

/etc/pki/CA/crl:

/etc/pki/CA/newcerts:
973B5D09FC491CC0.pem

/etc/pki/CA/private:  # 秘密鍵ディレクトリ
cakey.pem             # CA秘密鍵
```

# ■2.サーバー証明書
## ▽(1) サーバ用にOpenSSLの設定ファイルを用意
```bash
# cp -p /etc/pki/tls/openssl.cnf /etc/pki/tls/openssl-server.cnf
# vi /etc/pki/tls/openssl-server.cnf
-------
[ usr_cert ]
...
basicConstraints=CA:FALSE # デフォルトでCA証明書として使用しません
nsCertType = server # デフォルトでサーバー用証明書を作成します
```
　
## ▽(2) サーバ用の証明書を作成
```bash
# cd /etc/pki/tls/
# DAYS="-days 3650" SSLEAY_CONFIG="-config /etc/pki/tls/openssl-server.cnf" /etc/pki/tls/misc/CA -newreq
..........+++
writing new private key to 'newkey.pem'
Enter PEM pass phrase:return              # サーバー用のパスフレーズ（パスワード）を入力
Verifying - Enter PEM pass phrase:
-----

#CSR情報入力
Common Name (eg, your name or your server's hostname) []: secure.example.com
-----
Request is in newreq.pem, private key is in newkey.pem
```

```bash
# ls -l
newkey.pem  # サイト用秘密鍵
newreq.pem  # サイト用公開鍵-署名なし（CSR）
```
　
## ▽(3) 認証局の署名を入れたサーバー証明書を作成
```bash 
# cd /etc/pki/tls/
# SSLEAY_CONFIG="-config /etc/pki/tls/openssl-server.cnf" /etc/pki/tls/misc/CA -sign
------
Enter pass phrase for /etc/pki/CA/private/cakey.pem: # CA証明書を作成する際に設定したパスフレーズ

#CSR や 作成する証明書の形式が表示

Sign the certificate? [y/n]: y
1 out of 1 certificate requests certified, commit? [y/n] y

#証明書の内容が表示

Signed certificate is in newcert.pem
```
　
・確認(作成された証明書の)

```bash 
# ls -l
newcert.pem # サーバー証明書（プライベート認証局の署名入り）
```
　
## ▽(4) 作成したサーバ用証明書を移動
```bash 
# mv /etc/pki/tls/newcert.pem /etc/pki/CA/certs/secure.example.com.crt
```
　
## ▽(5) サーバ用秘密鍵にパスフレーズ埋め込み
※作成したクライアント用証明書、秘密鍵をわかりやすいように認証局(CA)管理下のディレクトリへ移動しておきます。

```bash 
# openssl rsa -in /etc/pki/tls/newkey.pem -out /etc/pki/CA/private/secure.example.com.key
Enter pass phrase for /etc/pki/tls/newkey.pem:　# サーバー証明書、秘密鍵を作成したときのパスフレーズ)
writing RSA key
```

# ■3. クライアント証明書
## ▽(1) クライアント用にOpenSSLの設定ファイルを用意
```bash 
# cp -p /etc/pki/tls/openssl.cnf /etc/pki/tls/openssl-client.cnf
# vi /etc/pki/tls/openssl-client.cnf
----------
[ usr_cert ]
...
basicConstraints=CA:FALSE # デフォルトでCA証明書として使用しません
nsCertType = client, email, objsign # デフォルトでクライアント用証明書を作成します
...
```
※nsCertType の 設定内容
  client : 文字通りクライアントの意味
  email : 文字通りメールの意味
  objsign : ソフトウェアなどの署名に用いる

　
## ▽(2) クライアント用の証明書作成
```bash 
# cd /etc/pki/tls/
# DAYS="-days 3650" SSLEAY_CONFIG="-config /etc/pki/tls/openssl-client.cnf" /etc/pki/tls/misc/CA -newreq
-----
writing new private key to 'newkey.pem'
Enter PEM pass phrase: #クライアント用のパスフレーズ (パスワード)
-----
# CSR情報を入力

Common Name (eg, your name or your server's hostname) []:mitzi2funk   # クライアント名(任意)
-----
Request is in newreq.pem, private key is in newkey.pem
```
　
・確認(作成物の)

```bash
# ls -l
newkey.pem  # クライアント用秘密鍵
newreq.pem  # クライアント用公開鍵-署名なし（CSR）
```
　
## ▽(3) 認証局の署名を入れたクライアント証明書を作成
```bash
# cd /etc/pki/tls/
# SSLEAY_CONFIG="-config /etc/pki/tls/openssl-client.cnf" /etc/pki/tls/misc/CA -sign
------
Enter pass phrase for /etc/pki/CA/private/cakey.pem: #CA証明書を作成する際に設定したパスフレーズ

# CSR や 作成する証明書の形式が表示

Sign the certificate? [y/n]: y
1 out of 1 certificate requests certified, commit? [y/n] y

# 証明書の内容が表示

Signed certificate is in newcert.pem
```
　
・確認(作成された証明書の)

```bash
# ls -1
newcert.pem # クライアント証明書（プライベート認証局の署名入り）
```
　
## ▽(4) 作成したクライアント用証明書、秘密鍵をPKCS#12フォーマットに変換
・証明書と秘密鍵を１つのファイルにまとめたPKCS#12フォーマットのファイルを作成

```bash
# cd /etc/pki/tls/
# openssl pkcs12 -export -in newcert.pem -inkey newkey.pem -out mitzi2funk.secure.example.com.pfx -name "secure.example.com"
------
Enter pass phrase for newkey.pem: # クライアント証明書作成時のパスフレーズ
Enter Export Password: # 作成しているPCL12ファイルを展開（読み込む）するためのパスフレーズ
Verifying - Enter Export Password:
------
※-name "<friendlyname>" は 任意の名前でOK。識別出来る名前が良い。
```
　
## ▽(5) クライアント証明書、秘密鍵等を整理
※作成したクライアント用証明書、秘密鍵をわかりやすいように認証局(CA)管理下のディレクトリへ移動しておきます。

```bash
# mkdir -p /etc/pki/CA/client/certs/
# mkdir -p /etc/pki/CA/client/private/

# mv /etc/pki/tls/newcert.pem /etc/pki/CA/client/certs/mitzi2funk.secure.example.com.crt
# mv /etc/pki/tls/newreq.pem /etc/pki/CA/client/private/mitzi2funk.secure.example.com.csr
# mv /etc/pki/tls/newkey.pem /etc/pki/CA/client/private/mitzi2funk.secure.example.com.key
# mv /etc/pki/tls/mitzi2funk.secure.example.com.pfx /etc/pki/CA/client/private/
```
　
# ■4. Apache の 設定
・作成したクライアント証明書、サーバ証明書を利用してwebサーバへのアクセス制限を設ける

## ▽テスト用コンテンツディレクトリ作成

```bash
# mkdir /var/www/html/mitzi
# chown -R apache.apache cloudpack
```

▽ssl.confの設定

```bash
# cp -p /etc/httpd/conf.d/ssl.conf /etc/httpd/conf.d/ssl.conf.org
# vi /etc/httpd/conf.d/ssl.conf
--------------------
DocumentRoot "/var/www/html/mitzi"
ServerName secure.example.com:443

SSLCertificateFile /etc/pki/CA/certs/secure.example.com.crt　サーバ証明書
SSLCertificateKeyFile /etc/pki/CA/private/secure.example.com.key　サーバ秘密鍵(ノーパスフレーズ)

SSLCACertificateFile /etc/pki/CA/cacert.pem　CA証明書

SSLVerifyClient require　←クライアント証明書を要求するかの設定
SSLVerifyDepth  10　←証明書のチェーン数
```

▽apache再起動

```bash 
# service httpd configtest
# service httpd restart
```

# ■5. クライアント証明書(pkcs12)をクライアントにインポートする

## ▽(0) 確認用端末
今回、以下を利用して動作確認しております

・PC(Windows 7)のブラウザ(IE 11、Chrome ver.39、Firefox ver.34))
・iPad mini (iOS 7.0.4)

## ▽(1) 各端末にクライアント証明書(pkcs12)をダウンロードする
※私はDropboxを利用して各端末に証明書を配布しました。

## ▽(2) クライアント証明書を設定

▽PC ブラウザの場合
・各端末にDLした「***.pfx」をダブルクリックすると、インポートウィザードが起動する。
(IE、Chrome、Safari、iPad miniなど)

　1) インポートする証明書を選択し
　2) 証明書(pkcs12)作成時に設定したパスワードを入力
　3) 「証明書の種類に基づいて、自動的に証明書ストアを選択する」を選択

▽iPad miniの場合
「***.pfx」をダブルクリックするとインストール作業が始まる。
後は証明書(pkcs12)作成時に設定したパスワードを入力することでインポート完了

## ▽(3) インポート状況の確認
### a) chromeの場合
「chrome://settings/」 -> [HTTPS/SSL]の「証明書の管理」ボタンを押下。
  [個人]タブ及び[信頼されたルート証明機関]タブ に該当証明書が設定されていることを確認。

### b) ipad miniの場合
[設定] ＞ [一般] ＞ [プロファイル] より 証明書が登録されていることを確認。

## ▽(4) 接続確認
サーバ証明書および ssl.confで設定した以下ドメインに、
クライアント証明書を設定した端末からのみアクセス出来ることを確認
https://secure.example.com

　
以上で本項の目的達成になります。
******
おまけ
# ■6. ELB配下のEC2で クライアント認証する場合

## ▽ELB、EC2の設定
・ELB、EC2ともにSecurityGroupにて443からのInbound通信を許可する
・[Listeners]にて INもOUTも TCP(443)とする
・health checkを [Ping Target] TCP:443 とする
・該当サーバをアタッチ

## ▽ELB経由でEC2へアクセス出来ることを確認
※DNSレコードは以下のように設定してあること

```
secure.example.com TTL IN CNAME <ELBのDNS名>
```

https://secure.example.com


# 7.■クライアント証明書の失効
## ▽(0) はじめに
・上記で作成したクライアント証明書を失効させます。

・以下ファイルが無ければ手動で作成しておく

```bash 
# echo '00' > /etc/pki/CA/crlnumber
```

## ▽(1) クライアント証明書の失効
・クライアント証明書なので、クライアント証明書用の設定ファイルを指定。サーバ証明書の場合はそれに応じて対応

```bash 
# openssl ca -gencrl -revoke  /etc/pki/CA/client/certs/mitzi2funk.secure.example.com.crt -config /etc/pki/tls/openssl-client.cnf
...
Enter pass phrase for /etc/pki/CA/private/cakey.pem:  # CA証明書を作成する際に設定したパスフレーズ（パスワード）を入力する
...
Data Base Updated
```

## ▽(2) 失効証明書リスト(CRL)を更新

```bash 
・失効リスト情報を出力
# openssl ca -gencrl -out /etc/pki/CA/revoke.crl
...
Enter pass phrase for /etc/pki/CA/private/cakey.pem:  # CA証明書を作成する際に設定したパスフレーズ（パスワード）を入力する
```

## ▽(3) apache の SSL設定を行う
・失効証明書リスト(CRL)を有効にする必要があります。

```bash
# vi /etc/httpd/conf.d/ssl.conf
----------
SSLCACertificateFile /etc/pki/CA/cacert.pem 

SSLCARevocationFile /etc/pki/CA/revoke.crl # 失効証明書リストのパスを指定(CA証明書の下あたりに追記)
```

・反映のためapache再起動

```bash
# service httpd configtest
# service httpd restart
```

## ▽(4) 同証明書を設定した端末より、該当サイトにアクセス、接続出来ないことを確認

・Firefoxの場合以下エラーが表示されました。

```text
安全な接続ができませんでした
SSL peer rejected your certificate as revoked.
(エラーコード: ssl_error_revoked_cert_alert
```
　
　
以上になります。