PKI の基本と SSL証明書と OpenSSL コマンド

# PKI の基本と SSL証明書と OpenSSL コマンド

## 目的

  - 自分の備忘録
  - しらべごとのまとめ

## 対象

  - SSL証明書、なんとなく使ってるけど実はよく分かってない
   - ググった先のコマンドコピペで自己証明書作ったりはできる、けど
   - 手順が書いてあればファイルを配置してコンフィグ編集してSSL化はできる、けど

## 内容

- 1. PKI とはなにか?
- 2. SSL証明書を準備しよう
  - 自己証明書
  - Route53 + Let's Encrypt
- 3. テストしてみよう
  - openssl コマンドを使ってみよう(s_server, s_client)
  - SSL ハンドシェイク
  - TLS バージョンと Cipher(サイファー)強度 と 脆弱性

## References

- IPA: https://www.ipa.go.jp/security/pki/index.html
- SSLハンドシェイク:  http://itpro.nikkeibp.co.jp/article/COLUMN/20071012/284445/

### 1. PKI とはなにか?

PKI とは 公開鍵認証基盤 (Public Key Infrastructure) のこと

まず、以下のドキュメントを読もう。(ありがとう新野さん)

- 5分で絶対に分かるPKI
  - http://www.atmarkit.co.jp/ait/articles/0102/17/news003.html

話のキーになるのは4ページ目

つまり、

> 1. 情報の送信元(サーバー)は 証明書(Cert File) と 公開鍵(Public Key) を相手(クライアント)に渡す
2. クライアントは、受け取った 証明書(Cert File) を見て信頼できると確認できたら、もらった 公開鍵(Public Key) で暗号化して送信元へ情報を渡す
3. 送信元は受け取ったデータと、もともと持っていた 秘密鍵(Private Key) を使って暗号化を解いて情報を参照する

> (共通鍵とSSLハンドシェイクについては後述する)

自己証明書を作ったことがある人はわかると思うが、いろいろと中間ファイル含めて作成するが大事なのは、

> サーバの持ち物: 秘密鍵 (Private Key)
クライアントの持ち物: 証明書 (Cert File), 公開鍵 (Public Key)

だ。そこで、

>「"公開"とは書いてあるが"鍵"をそんな簡単にクライアントに渡していいの?」
「秘密鍵はサーバー側で完全に管理しなければならないのに、公開鍵はいいの?」

という疑問を抱くかもしれないが、なんの問題もない。

> 公開鍵を使って暗号化した情報は、秘密鍵 のみで復号化できるからだ。

### 2. SSL証明書を準備しよう

- 自己証明書
- 正規のSSL証明書(無料)

#### 2-1. 自己証明書の作成

ref. http://qiita.com/akito1986/items/8eb41f5a43bb9421ae79

**自己証明書の利用には注意**
**可能なら無料もあるので正規の証明書を使いたいところ**

- -subj オプションを使うことで非対話にすることが可能です。

```shell-session:client_side
openssl genrsa -out ca-privatekey.pem 2048

openssl req -new -key ca-privatekey.pem -out ca-csr.pem \
-subj "/C=JP/ST=Tokyo/O=Test Company/OU=IT Department/CN=ssl.example.com"

openssl req -x509 -key ca-privatekey.pem -in ca-csr.pem -out ca-crt.pem -days 3560
```

```shell-session:server_side
openssl genrsa -out server-privatekey.pem

openssl req -new -key server-privatekey.pem -out server-csr.pem \
-subj "/C=JP/ST=Tokyo/O=Test Company/OU=IT Department/CN=ssl.example.com"

openssl x509 -req -CA ca-crt.pem -CAkey ca-privatekey.pem -CAcreateserial -in server-csr.pem -out server-crt.pem -days 3650
```

- Serverに置くファイル
  - server-crt.pem
  - server-privatekey.pem
- クライアントに置くファイル
  - ca-crt.pem

#### 2-2. 正規のSSL証明書(無料)

2017 現在、無料でSSL証明書を取得できます。有名なのな以下の2つでしょうか。

- AWS の ACM(AWS Certificate Manager)

  - http://docs.aws.amazon.com/ja_jp/acm/latest/userguide/acm-overview.html
- Let's Encrypt
  - https://letsencrypt.org/

今回は AWS 以外での利用も可能なように Let's Encrypt を利用します

##### 1) まずは ドメイン名 を取得します。.net などであれば年額1000円程度(US$11.0)で取得可能です。

- 今回は AWS の Route53 で取得し管理します。
  - https://console.aws.amazon.com/route53/home

- 今回は test-domain.net を取得したとします。

##### 2) 恒久的なグローバルIPをもったサーバーもしくは仮想マシンを準備します。

- 今回は GCP 上に CentOS7 VM を用意しました。
  - Static な External IP アドレスを取得しVMにアサインします。
      - https://console.cloud.google.com/networking/addresses/list

- ホスト名は FQDN で www.test-domain.net とします。

##### 3) Let's Encrypt で証明書を作成します。

- https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-centos-
- https://www.linode.com/docs/security/ssl/install-lets-encrypt-to-create-ssl-certificates

```shell-session
sudo yum update -y

sudo yum install epel-release
sudo yum install certbot git

sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt

cd /opt/letsencrypt
sudo -H ./letsencrypt-auto certonly --standalone -d www.test-domain.net

sudo ls -al /etc/letsencrypt/live/www.test-domain.net/fullchain.pem
```
```shell-session:output
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/www.test-domain.net/fullchain.pem. Your cert
   will expire on 2017-04-07. To obtain a new or tweaked version of
   this certificate in the future, simply run letsencrypt-auto again.
   To non-interactively renew *all* of your certificates, run
   "letsencrypt-auto renew"
 - If you lose your account credentials, you can recover through
   e-mails sent to *****@gmail.com.
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:
   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
```

```shell-session
sudo ls /etc/letsencrypt/live
www.test-domain.net

sudo ls /etc/letsencrypt/live/www.test-domain.net
cert.pem  chain.pem  fullchain.pem  privkey.pem
```

### 3. 証明書をテストする

#### 3-1. openssl コマンドを使ってみよう(s_server, s_client)

- ref.
    - man s_client
    - man s_server

```shell-session:server_session
sudo openssl s_server -key \
> /etc/letsencrypt/live/www.test-domain.net/privkey.pem \
> -cert /etc/letsencrypt/live/www.test-domain.net/cert.pem \
> -accept 443 -www -state -debug
```

```shell-session:client_session
echo "Q" | openssl s_client -connect localhost:443 -state -debug
```

- ブラウザからアクセスしてみましょう。
    - URL https://www.test-domain.net/

- 以下のように緑の鍵アイコンが表示されていれば有効な証明書でHTTPS通信できています。
- テストが終わったら openssl コマンドで実行している s_server セッションを Ctrl + c で終了させておきます。

![Screenshot 2017-01-08 02.26.10.png](https://qiita-image-store.s3.amazonaws.com/0/67892/e571fc24-87e4-662d-e021-688affc01006.png)

#### 3-2. SSL ハンドシェイク

- IPA: https://www.ipa.go.jp/security/pki/index.html
- SSLハンドシェイク:  http://itpro.nikkeibp.co.jp/article/COLUMN/20071012/284445/
- https://tools.ietf.org/html/rfc5246#page-33

クライアント側の動きは上で実行した openssl s_client コマンドから確認できます。

```shell-session:実行例
$ sudo echo "Q" | openssl s_client -connect localhost:443 -state 2>&1 | grep SSL_
SSL_connect:before/connect initialization
SSL_connect:SSLv2/v3 write client hello A
SSL_connect:SSLv3 read server hello A
SSL_connect:SSLv3 read server certificate A
SSL_connect:SSLv3 read server key exchange A
SSL_connect:SSLv3 read server done A
SSL_connect:SSLv3 write client key exchange A
SSL_connect:SSLv3 write change cipher spec A
SSL_connect:SSLv3 write finished A
SSL_connect:SSLv3 flush data
SSL_connect:SSLv3 read server session ticket A
SSL_connect:SSLv3 read finished A
```

- サーバ側も同様

```shell-session
$ sudo openssl s_server -key \
> /etc/letsencrypt/live/www.test-domain.net/privkey.pem \
> -cert /etc/letsencrypt/live/www.test-domain.net/cert.pem \
> -accept 443 -www -state 2>&1 | grep SSL_

SSL_accept:before/accept initialization
SSL_accept:SSLv3 read client hello A
SSL_accept:SSLv3 write server hello A
SSL_accept:SSLv3 write certificate A
SSL_accept:SSLv3 write key exchange A
SSL_accept:SSLv3 write server done A
SSL_accept:SSLv3 flush data
SSL_accept:SSLv3 read client key exchange A
SSL_accept:SSLv3 read finished A
SSL_accept:SSLv3 write session ticket A
SSL_accept:SSLv3 write change cipher spec A
SSL_accept:SSLv3 write finished A
SSL_accept:SSLv3 flush data
```

#### 3-3. TLS バージョンと Cipher(サイファー)強度 と 脆弱性

- TL;DR 端的に言うと、そのときに利用可能な最新の openssl バージョンを利用することで強度の高い Cipher Suite(暗号アルゴリズムの組み合わせ)を利用することができ、 すくなくとも利用開始時点でより安全な SSL/TLS 環境を構築することができる、ということ。

- openssl のバージョン確認
- リリース
  - https://www.openssl.org/news/newslog.html

- サーバ上

```shell-session
$ openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

$ rpm -q openssl
openssl-1.0.1e-60.el7.x86_64
```

- 利用可能な Cipher Suite

ref. man ciphers

```shell-session:実行例
$ openssl ciphers -v ECDH+aRSA+HIGH
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA384
ECDHE-RSA-AES256-SHA    SSLv3 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA1
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(128) Mac=AEAD
ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(128)  Mac=SHA256
ECDHE-RSA-AES128-SHA    SSLv3 Kx=ECDH     Au=RSA  Enc=AES(128)  Mac=SHA1
ECDHE-RSA-DES-CBC3-SHA  SSLv3 Kx=ECDH     Au=RSA  Enc=3DES(168) Mac=SHA1
```

- どの Cipher Suite を使うべきかは IPA のガイドラインを参照して参考にしても良いと思います。
- 例えばクライアント側の環境が古いなどでサーバ側の暗号化強度を高い方で制限しにくい場合などもあり、弱めから強いのまで幅広く対応せざるを得ないこともあります。
  - ref. https://www.ipa.go.jp/security/vuln/ssl_crypt_config.html

### おまけ

- RFC 5077
   - Transport Layer Security (TLS) Session Resumption without Server-Side State
   - https://tools.ietf.org/html/rfc5077

- https://techblog.yahoo.co.jp/infrastructure/ssl-session-resumption/

所感: 端的に言うとサーバーサイドのキャッシュ保持が不要になるため、分散環境などで有効かもしれません。あと、キャッシュサイズの増加の心配もなくなりそうなので適切な利用が進むと良いのではないかと思います。