究極のノマドを追求したらカギっ子管理者になってたオレオレ証明書の巻！！


#課題

管理画面などはセキュリティ上の理由からBasic認証にしてみたり、IPで制限かけたりMacアドレスで制限かけたりすることが多い。ただ、この方法だけ見てみると、どっちもイマイチでBasic認証だと弱いしIP制限やMacアドレスだと休日何かあった時に毎回会社に来るとか...。休日の時に見なければならないということはおおよそ緊急なので、移動してる間に問題が拡大している可能性もある。

#なんかイイ方法ないの？

鍵を持つ。

サーバ側では、鍵を持っている人しかアクセスさせないようにすれば場所に縛られることなくセキュアにアクセス出来る。コレを実現するのにクライアント証明書の設定をする。

![self_certs.png](https://qiita-image-store.s3.amazonaws.com/0/8726/0128db2e-4182-716b-f8e7-b4b9915537f2.png "self_certs.png")

#0.まずはopensslのバージョンを確認する

[Openssl公式サイト](http://www.openssl.org/)で確認してopenssl 1.0.1g以上にすること

#1.設定ファイルの編集してみる

鍵長等は[コチラ](https://jp.globalsign.com/service/ssl/knowledge/use/1024bit.html)を参考にさせてもらい設定の方針を決めてみる

* 頻繁に更新が必要なのも大変なので有効期限を10年にする (default_days)
* ハッシュ方法は限られた数を想定しているため固めにsha512にしてみる (default_md)
* 鍵長を2048bitsにする (default_bits)
* ルートCA用の設定は[ v3_ca ]で変更
* クライアント認証の設定は行末に[ clientAuth ]を追加
* サーバ認証用の設定は行末に[ serverAuth ]を追加

```bash:cmd
cp -p /etc/pki/tls/{openssl.cnf,openssl.cnf.`date +"%Y%m%d"`}
vi /etc/pki/tls/openssl.cnf
```

```diff:openssl.cnf
diff -u openssl.cnf.20140510 openssl.cnf
--- openssl.cnf.20140510	2014-04-07 20:26:41.000000000 +0900
+++ openssl.cnf	2014-05-10 05:50:21.198111607 +0900
@@ -70,9 +70,9 @@
 # crlnumber must also be commented out to leave a V1 CRL.
 # crl_extensions	= crl_ext

-default_days	= 365			# how long to certify for
+default_days	= 3650			# how long to certify for
 default_crl_days= 30			# how long before next CRL
-default_md	= default		# use public key default MD
+default_md	= sha512	        # use public key sha256
 preserve	= no			# keep passed DN ordering

 # A few difference way of specifying how similar the request should look
@@ -104,7 +104,7 @@
 ####################################################################
 [ req ]
 default_bits		= 2048
-default_md		= sha1
+default_md		= sha512
 default_keyfile 	= privkey.pem
 distinguished_name	= req_distinguished_name
 attributes		= req_attributes
@@ -237,14 +237,14 @@

 # This is what PKIX recommends but some broken software chokes on critical
 # extensions.
-#basicConstraints = critical,CA:true
+basicConstraints = critical,CA:true
 # So we do this instead.
-basicConstraints = CA:true
+#basicConstraints = CA:true

 # Key usage: this is typical for a CA certificate. However since it will
 # prevent it being used as an test self-signed certificate it is best
 # left out by default.
-# keyUsage = cRLSign, keyCertSign
+keyUsage = cRLSign, keyCertSign, digitalSignature, keyAgreement, keyCertSign, keyEncipherment

 # Some might want this also
 # nsCertType = sslCA, emailCA
@@ -260,6 +260,8 @@
 # You can even override a supported extension:
 # basicConstraints= critical, DER:30:03:01:01:FF

+extendedKeyUsage = serverAuth, clientAuth
+
 [ crl_ext ]

 # CRL extensions.
@@ -350,3 +352,17 @@
 				# (optional, default: no)
 ess_cert_id_chain	= no	# Must the ESS cert id chain be included?
 				# (optional, default: no)
+
+[ clientAuth ]
+extendedKeyUsage = clientAuth
+basicConstraints=CA:FALSE
+keyUsage = digitalSignature, keyEncipherment
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+[ serverAuth ]
+extendedKeyUsage = serverAuth , clientAuth
+basicConstraints=CA:FALSE
+keyUsage = digitalSignature, keyEncipherment
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
```

鍵の用途(keyUsage)部分に指定できるパラメータ。更に詳しい情報は[コチラ](http://www.nii.ac.jp/hrd/ja/joho-karuizawa/h20/txt4-1.pdf)

|parameter|usage|
|---|---|
|digitalSignature|デジタル署名|
|nonRepudiation|否認防止|
|keyEncipherment|鍵配布|
|dataEncipherment|データ暗号化|
|keyAgreement|鍵の検証|
|keyCertSign|鍵署名|
|cRLSign|証明書失効リスト(CRL)の検証|
|encipherOnly|暗号化のみ|
|decipherOnly|復号化のみ|

#2.自己ルートCAの開設してみる

|生成されるもの|ファイル|備考|
|---|---|---|
|CA公開鍵|/etc/pki/CA/cacert.pem||
|CA秘密鍵|/etc/pki/CA/private/cakey.pem|非公開ファイル、厳重に管理すること|

```bash:/etc/pki/tls/misc/CA
$ sudo /etc/pki/tls/misc/CA -newca

CA certificate filename (or enter to create)
'[enterを押す]'
Making CA certificate ...
Generating a 2048 bit RSA private key
.............................................................................+++
.........................+++
writing new private key to `/etc/pki/CA/private/./cakey.pem`
Enter PEM pass phrase: '[passwordを入れる]'
Verifying - Enter PEM pass phrase: '[再度passwordを入れる]'
-----
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) [XX]:JP '[国名を２文字で記入]'
State or Province Name (full name) []:Tokyo '[都道府県を記入]'
Locality Name (eg, city) [Default City]:Shibuya-ku '[市区町村を記入]'
Organization Name (eg, company) [Default Company Ltd]:hoge Corp '[会社名などを記入]'
Organizational Unit Name (eg, section) []:hoge '[部署名などを入れる]'
Common Name (eg, your name or your server`s hostname) []:hoge.jp '[ドメインを記入]'
Email Address []:  '[(任意)入れなくてもよい]'

Please enter the following `extra` attributes
to be sent with your certificate request
A challenge password []: '[(任意)入れなくてもよい]'
An optional company name []: '[(任意)入れなくてもよい]'
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/CA/private/./cakey.pem: '[上記で記入したpasswordを再度入力]'
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 13847243035304331584 (0xc02b4a2cbdd10940)
        Validity
            Not Before: May  9 21:07:11 2014 GMT
            Not After : May  8 21:07:11 2017 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Tokyo
            organizationName          = hoge Corp
            organizationalUnitName    = hoge
            commonName                = hoge.jp
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                2F:A4:34:1B:CE:B5:58:54:E3:14:C7:B3:B2:BE:CB:04:A7:80:0D:BA
            X509v3 Authority Key Identifier:
                keyid:2F:A4:34:1B:CE:B5:58:54:E3:14:C7:B3:B2:BE:CB:04:A7:80:0D:BA

            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Key Usage:
                Digital Signature, Key Encipherment, Key Agreement, Certificate Sign, CRL Sign
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
Certificate is to be certified until May  8 21:07:11 2017 GMT (1095 days)

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

大事なファイルなのでパーミッションを換えておく

```
chmod 400 /etc/pki/CA/private/cakey.pem
```

困ったときは

|topic|command|
|---|---|
|ルートCAを作り直したいとき|`rm -rf /etc/pki/CA/*`|
|公開鍵の内容を確認したいとき|`openssl x509 -noout -in [公開鍵] -text`|
|秘密鍵の内容を確認したいとき|`openssl rsa  -noout -in [秘密鍵] -text`|


#3.サーバ証明書をツクル

nginxがインストールされている事を前提として、SSL証明書を更新する事を想定してyyyymmddをつけたディレクトリを作成

####3-1.サーバ秘密鍵とCSRの作成して署名をする

|生成されるもの|ファイル|備考|
|---|---|---|
|サーバ秘密鍵|hoge.key|非公開ファイル、厳重に管理すること|
|証明書署名要求(CSR)|hoge.csr||
|サーバ証明書|hoge.crt||

####3-2.サーバ秘密鍵とCSRを作成する

```bash:create_csr
mkdir /etc/nginx/ssl_`date +"%Y%m%d"` && cd !$
openssl req -new -nodes -newkey rsa:2048 -sha256 -keyout hoge.key -out hoge.csr
```
####3-3.[任意]サーバ秘密鍵のパスワードを解除

```bash:no_key
cd /etc/nginx/ssl_`data +"%Y%m%d"`
openssl rsa -in hoge.key -out hoge_no.key
```

####3-4.自己ルートCAでCSRへの署名

```bash:sign
openssl ca -out /etc/nginx/ssl_`date +"%Y%m%d"`/hoge.crt -infiles /etc/nginx/ssl_`date +"%Y%m%d"`/hoge.csr
```

####[tips]署名がうまくいかない時

```error
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
..(snip)..
Certificate is to be certified until May  8 10:58:48 2024 GMT (3650 days)
Sign the certificate? [y/n]:y
failed to update database
TXT_DB error number 2
```

```bash:revoke
ll /etc/pki/CA/newcerts/
  -rw-r--r-- 1 root root 4625  5月 10 06:07 2014 C02B4A2CBDD10940.pem
openssl ca -revoke /etc/pki/CA/newcerts/C02B4A2CBDD10940.pem
```

#4.nginxでクライアント証明書のチェックをするようにしてみる

```bash:prepare
cp -p /etc/nginx/conf.d/{default.conf,default.conf.`date +"%Y%m%d"`}
vi /etc/nginx/conf.d/default.conf
```

|主要パラメータ|内容|備考|
|---|---|---|
|[ssl_verify_client](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_verify_client)|クライアント認証の検証有無を設定||
|[ssl_verify_depth](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_verify_depth)|サーバがクライアントの身元を確認するために<br>証明書のチェーンを何階層までたどるかを設定||
|ssl_ciphers|[こちらで確認して下さい](http://qiita.com/kuni-nakaji/items/5118b23bf2ea44fed96e)||

```nginx:nginx_config_sample
server {
    listen 443;
    server_name hoge.jp;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    ssl on;
    ssl_certificate /etc/nginx/ssl_20140511/hoge.crt;
    ssl_certificate_key /etc/nginx/ssl_20140511/hoge_no.key;
    ssl_client_certificate /etc/pki/CA/cacert.pem;
    ssl_session_timeout 5m;

    ssl_verify_client on;
    ssl_verify_depth 3;

    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE+RSAGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!aNULL!eNull:!EXPORT:!DES:!3DES:!MD5:!DSS;
    ssl_prefer_server_ciphers on;
}
```
```
service nginx configtest
service nginx restart
```

#5.クライアント証明書もツクル

基本的にはサーバ証明書と同様に作成

####5-1.クライアント秘密鍵とCSRの作成して署名をする

|生成されるもの|ファイル|備考|
|---|---|---|
|クライアント秘密鍵|client_hoge.key|非公開ファイル、厳重に管理すること|
|証明書署名要求(CSR)|client_hoge.csr||
|クライアント証明書|clinet_hoge.crt||

```bash:create_csr
cd /tmp
openssl req -new -nodes -newkey rsa:2048 -sha256 -keyout client_hoge.key -out client_hoge.csr
```

```bash:sign
openssl ca -out /tmp/client_hoge.crt -infiles /tmp/client_hoge.csr
```

####5-2.pkcs12形式にまとめる

クライアント証明書を配布出来るようにpkcs12形式でファイルを作成

|生成されるもの|ファイル|備考|
|---|---|---|
|pkcs12ファイル|client_hoge.p12|特定の人に配布するためのファイル|

秘密鍵とクライアント証明書をpkcs12形式でまとめます。

```bash:pkcs12
openssl pkcs12 -export -in client_hoge.crt -inkey client_hoge.key -certfile /etc/pki/CA/cacert.pem -out client_hoge.p12
```

#6.ようやくブラウザからアクセス!

####クライアント証明書が無い場合

証明書がないためBad Requestで画面が表示できない。

![400_The_SSL_certificate_error.png](https://qiita-image-store.s3.amazonaws.com/0/8726/09d28404-d6e2-8bb9-64e5-3d44164c7611.png "400_The_SSL_certificate_error.png")

####クライアント証明書を入れてみる[Macの場合]

先ほど作成したpkcs12形式のファイルをダブルクリック

![client_cert.png](https://qiita-image-store.s3.amazonaws.com/0/8726/52ee0294-d256-feef-86d3-30b791481582.png "client_cert.png")

キーチェーンに追加

![add_certs.png](https://qiita-image-store.s3.amazonaws.com/0/8726/118383e7-7ba5-e5a3-5c96-3d3c64259a50.png "add_certs.png")

リストに追加されるがルート証明書が✕になっているの何とかする

![before_check.png](https://qiita-image-store.s3.amazonaws.com/0/8726/4da38e09-240a-2c66-11e5-b7dbfb7ef73c.png "before_check.png")

####自分自身を信じてみる

身元が判明しているのでルート証明書をSSLだけ信頼してみる

![root_certs.png](https://qiita-image-store.s3.amazonaws.com/0/8726/6961ba9c-467c-650a-938a-859a46a1f385.png "root_certs.png")

リストを確認すると青い＋になった

![after_check.png](https://qiita-image-store.s3.amazonaws.com/0/8726/ef550318-bcad-9feb-a72a-ebd5e051342d.png "after_check.png")

ブラウザで確認してみる。

![ssl_certificate_ok.png](https://qiita-image-store.s3.amazonaws.com/0/8726/e35a2eef-212d-b559-028f-b8f37fb4a38a.png "ssl_certificate_ok.png")

####[tips]確認している時にハマったこと

- ブラウザにキャッシュされてしまうためかいつまでもクライアント証明書が有効にならない

 ```
 設定がほんとに間違えている可能性もあるが、まずはシークレットモードでも試してみる
 ```

####[tips]他にも色々使える

- スマホにもクライアント証明書を使用できる
- Mailサーバ証明書 (SMTPS, IMAPS, POP3S)
- バージョン管理 (git, Mercurial, etc..)
- DB接続 ([mysql](http://inokara.hateblo.jp/entry/2013/04/29/023037), etc..)

#まとめ

やってみると、最低限おんなじようなことを３回くらいやらなければならないので面倒に見えるが慣れれば１０分位でできるので是非お試しあれ！ただ、所詮オレオレ証明書なのでBasic認証なども組み合わせてセキュリティには十分注意してください！