Windows環境のTomcatとOpenSSLでクライアント認証するまでの流れ

# 目的とゴール

* Tomcatを利用したクライアント認証環境を構築する。
* Windows環境のTomcat(8系)を使い、クライアント認証を行う。
 * 認証後のアクセス制御までは行わない

## 必要な作業

- 入手＆インストールするもの
 - JDK8 ( keytoolsを利用 )
 - Tomcat8 ( 8.0.20 )
 - OpenSSLのWindowsバイナリ ( 1.0.2a )
- 構築作業
 - OpenSSLの設定を適用する
 - OpenSSLにて検証用の認証局(ルートCA)をたてる
 - クライアント証明書の一式生成
 - サーバ証明書の一式作成（任意）
 - Tomcatに対してキーストアを有効にする
- 検証環境
 - pleiades All in one Eclipse 4.4.2 SR1 64bit JREバンドルあり
 - Tomcatの設定や実行は Eclipse WTPから実施。

## 本ドキュメントで登場する単語

|用語(和名)|用語(英語)|対象技術|概要|
|:----|:--|:--|:--|
|キーストア|keystore|Java Keytool|暗号で扱うファイルを格納。今回は公開鍵暗号方式の一式を扱い、Tomcat8にこれらを読ませる。|
|認証局|CA|PKI|公開鍵暗号で扱う鍵の妥当性を検証する。認証局は複数の階層を持つことが可能で、階層の頂点をルートCAと呼ぶ。|
|証明書発行要求|CSR|PKI|証明書を利用する側が、認証局に証明書の発行を要求するために必要な内容を記したファイル|
|公開鍵|PublicKey|PKI|秘密鍵によって暗号化されたものを復号するための鍵で、認証局から発行された証明書の構成要素の1つ。|
|秘密鍵|PrivateKey|PKI|暗号化するための鍵。公開鍵とは異なり秘密鍵は絶対に公開してはいけない。|
|証明書|certification|PKI|CAの署名がついた公開鍵。|
|署名|signature|PKI|証明書の妥当性検証に利用。署名にはSHA-256などのハッシュ関数を用いる。電子署名の略で、CAが証明書に対して署名を付与する。|
|証明書失効リスト|CRL|PKI|CAにより無効化した証明書のリスト。CRLも電子証明書と同様にCAの署名が付与されている。失効リストに記載された電子証明書は無効と判断する。失効リストがなくてもクライアント認証は動作するが、失効リストには何らかの理由で無効化した証明書が記載されているので正式運用時には不可欠である。|
|鍵ペア|keypair|PKI|公開鍵暗号方式における秘密鍵と公開鍵のペアのこと。|

## OpenSSLの設定

OpenSSLの設定ファイルが必須となります。デフォルト設定でも動作しますが、今回の設定ファイルは https://gist.github.com/A-pZ/4b79b9a47829da076bbb にて公開しています。内容はほぼデフォルトと同じですが、作業ディレクトリをカレントにしていることと、証明書のポリシーを少しゆるくしています（必須条件は、発行者が同じであること）

さらにopensslの展開ディレクトリにて、以下の追加作業が必要です。

* index.txt ファイルを作成し、テキストの中は00のみ記入する
* crlnumber ファイルを作成し、テキストの中は00のみ記入する
* serial(拡張子なし)ファイルを作成し、中身は空とする
* newcertsディレクトリを作成する
* crlディレクトリを作成する

ファイルを作成する際には **必ずBOMなしUTF-8で** 作成してください。

以上の作業が完了したら、準備完了です。

## CAの構築

管理者権限でコマンドプロンプトを開き、OpenSSLのディレクトリへ移動してopensslを実行します。

```cmd.exe
C:\Windows\system32>cd C:\openssl-1.0.2a-x64_86-win64

C:\openssl-1.0.2a-x64_86-win64>openssl
OpenSSL>
```

### CAの秘密鍵を生成する

```openssl.exe
OpenSSL> genrsa -out ca.key 2048 -config openssl.cfg
Loading 'screen' into random state - done
Generating RSA private key, 2048 bit long modulus
..........................................................................................................................................+++
..+++
e is 65537 (0x10001)
```

これでCAの秘密鍵が ca.key の名前で生成されます。後ろの数字は鍵長で、ビット数を記載します。

> 鍵長は長いほど堅牢ですが、長さに比例して暗号/復号の時間がかかります。今回は2015年時点で一般的に使われる2048にしています。

### CAの証明書を作成する。

証明書の作成手順は、以下の順で行います

1. 証明書発行要求(CSR)を作成する
2. CAは、受け取ったCSRから証明書を作成する(=CAの署名をつける)
3. 証明書を要求者に返す

CA自身の証明書はCAが自ら署名します。これを **自己署名証明書** と呼びます。

ではCSRを作成します。

```openssl.exe
OpenSSL> req -new -key ca.key -out ca.csr -config openssl.cfg
Loading 'screen' into random state - done
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) []:Shinagawa
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Capybara-Local
Organizational Unit Name (eg, section) []:Section1
Common Name (e.g. server FQDN or YOUR name) []:a-pz.jp
Email Address []:ca@a-pz.jp

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

これで、CAの証明書発行要求 ca.csrができました。次に証明書を作ります。

```openssl.exe
OpenSSL> x509 -req -in ca.csr -signkey ca.key -out ca.crt
Loading 'screen' into random state - done
Signature ok
subject=/C=JP/ST=Tokyo/L=Shinagawa/O=Capybara-Local/OU=Section1/CN=a-pz.jp/emailAddress=ca@a-pz.jp
Getting Private key
```

これで、ca.crtの名前で、自己署名のCA証明書が完成しました。ブラウザで見ることができますので見てみましょう。

![rootca.png](https://qiita-image-store.s3.amazonaws.com/0/13268/e27f5e5b-3acc-4056-0b0c-a77ee8eaf8fd.png)

証明書が見れる＝証明書の発行は完了しました、ということです。しかしこのCAの証明書は発行しただけで、「このCA証明書は信頼できませんよ、知らない認証局の証明書ですよ」と警告してくれます。

CAに関するファイルの生成はここまでです。
CAの公開鍵である ca.crt はあとでkeystoreにインポートします。

## クライアント証明書の発行

クライアント証明書の発行手順は、CAの証明書の発行手順と同じ流れです。

1. 秘密鍵作成
2. CSR作成
3. CAが署名して証明書を発行する
4. インストールできるP12ファイルを作成する

### 秘密鍵の作成

```openssl.exe
OpenSSL> genrsa -out client.key 2048 -config openssl.cfg
Loading 'screen' into random state - done
Generating RSA private key, 2048 bit long modulus
.......................................+++
........................................................+++
e is 65537 (0x10001)
OpenSSL>
```

client.keyが生成されました。

### CSRの作成

証明書発行要求を作ります。

OpenSSLから以下を入力します。

```openssl.exe
OpenSSL> req -new -key client.key -out client.csr -config openssl.cfg
Loading 'screen' into random state - done
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) []:Shinagawa
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Capybara-Private
Organizational Unit Name (eg, section) []:Private
Common Name (e.g. server FQDN or YOUR name) []:A-pZ
Email Address []:a-pz@a-pz.jp

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

### クライアント証明書の発行

CSRができたら、クライアント証明書をCAから発行します。

```openssl.exe
OpenSSL> ca -config openssl.cfg -out client.crt -infiles client.csr
Using configuration from openssl.cfg
Loading 'screen' into random state - done
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Jun 15 08:09:51 2015 GMT
            Not After : Jun 14 08:09:51 2016 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Tokyo
            organizationName          = Capybara-Private
            organizationalUnitName    = Private
            commonName                = A-pZ
            emailAddress              = a-pz@a-pz.jp
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                CF:13:D1:41:DC:21:D1:05:26:4E:94:C9:9A:51:8A:93:AB:10:65:0E
            X509v3 Authority Key Identifier:
                DirName:/C=JP/ST=Tokyo/L=Shinagawa/O=Capybara-Local/OU=Section1/CN=a-pz.jp/emailAddress=ca@a-pz.jp
                serial:AC:38:88:67:35:14:17:2F

Certificate is to be certified until Jun 14 08:09:51 2016 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
```

これで、クライアント証明書 client.crtが生成され、鍵のペアができました！

### 余談：WindowsからCA証明書をインストールできるP12ファイルを作る

P12ファイルは電子証明書の鍵ペアがセットになってインストールできる形式のファイルです。Windowsでインストールする場合は、このp12形式のファイルをダブルクリックするだけでインストーラが起動します。

ここではP12ファイルの作成までを目標とします。

> 正式は PKCS#12 形式ですが、拡張子を3文字にする慣例から p12ファイルと呼ぶ方たちもいます。

OpenSSLを使い、クライアント証明書と秘密鍵からP12ファイルを生成します。

```openssl.exe
OpenSSL> pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name tomcat -CAfile ca.crt -caname root -chain
Loading 'screen' into random state - done
Enter Export Password:
Verifying - Enter Export Password:

```

成功すると、client.p12ファイルが生成されます。これはWindowsにCAの証明書としてインストールできます。

## keystore(キーストア)にサーバ証明書を作成する

必要な作業は以下のとおりです。

* サーバ証明書の鍵ペア作成
* キーストアの作成

### サーバの鍵ペアの作成 と キーストアの作成

Tomcat用の鍵ペアを作成します。作成はKeytoolから行います。また、鍵を保管する場所（ファイル）をキーストアと呼びますが、Tomcatに読み込ませるキーストアも同時に作成します。

> keytoolはファイルとして存在しないキーストアの名前を指定すると、キーストアも新規作成してくれます。

秘密鍵は keytool から生成し、公開鍵はCAの証明書を作成したOpenSSLから生成します。

#### 秘密鍵の生成

keytoolを使います。OpenSSLのときとは異なり、証明書の情報を入力する順番は真逆です。

**姓名の部分は、サーバのドメインもしくはIPアドレスにしてください**

```keytool.exe
C:\openssl-1.0.2a-x64_86-win64>keytool -genkey -alias tomcat -keyalg RSA -keystore tomcat.jks
キーストアのパスワードを入力してください:
  姓名は何ですか。
    [Unknown]:  <サーバのドメインないしはIPアドレス>
  組織単位名は何ですか。
    [Unknown]:  Capybara-Server
  組織名は何ですか。
    [Unknown]:  Capybara
  都市名または地域名は何ですか。
    [Unknown]:  Shinagawa
  都道府県名または州名は何ですか。
    [Unknown]:  Tokyo
  この単位に該当する2文字の国コードは何ですか。
    [Unknown]:  JP
CN=A-pZ, OU=Capybara-Server, O=Capybara, L=Shinagawa, ST=Tokyo, C=JPでよろしいですか。
  [いいえ]:  y

<tomcat>の鍵パスワードを入力してください
        (キーストアのパスワードと同じ場合はRETURNを押してください):[ここではRETURNとする]
```

これでキーストアに秘密鍵が生成されました。キーストアに格納している証明書や鍵の一覧を見てみましょう。
キーストアの中身一覧を見るには、同じくkeytoolを使います。

```keytool.exe
C:\openssl-1.0.2a-x64_86-win64>keytool -list -keystore tomcat.jks
キーストアのパスワードを入力してください:

キーストアのタイプ: JKS
キーストア・プロバイダ: SUN

キーストアには1エントリが含まれます

tomcat,2015/06/16, PrivateKeyEntry,
証明書のフィンガプリント(SHA1): 30:21:E2:FA:0D:23:8A:DF:18:7A:5D:57:D3:11:D0:B0:23:3C:3B:82
```

tomcatの名前で、PrivateKeyEntryがありますね。

#### 証明書発行要求(CSR)を出す

keytoolから行います。先ほどの秘密鍵生成のときに入力した情報をもとにCSRを発行します。

```keytool.exe
C:\openssl-1.0.2a-x64_86-win64>keytool -certreq -alias tomcat -file tomcat.csr -keystore tomcat.jks 
キーストアのパスワードを入力してください: [パスワード]
```

これで証明書発行要求(CSR)である tomcat.csrが生成されます。

#### CAがCSRを受けて署名し、サーバ証明書を生成する

CAによる署名はOpenSSLが行います。

```openssl.exe
OpenSSL> ca -config openssl.cfg -out tomcat.crt -infiles tomcat.csr
Using configuration from openssl.cfg
Loading 'screen' into random state - done
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: Jun 16 01:50:00 2015 GMT
            Not After : Jun 15 01:50:00 2016 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Tokyo
            organizationName          = Capybara
            organizationalUnitName    = Capybara-Server
            commonName                = <サーバのドメインないしはIPアドレス>
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                76:81:19:AB:F2:D4:75:CF:1F:86:6D:75:BD:42:A5:49:EA:21:57:70
            X509v3 Authority Key Identifier:
                DirName:/C=JP/ST=Tokyo/L=Shinagawa/O=Capybara-Local/OU=Section1/CN=a-pz.jp/emailAddress=ca@a-pz.jp
                serial:AC:38:88:67:35:14:17:2F

Certificate is to be certified until Jun 15 01:50:00 2016 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
```

これでサーバ証明書が tomcat.crtの名前で 作成できました。

#### キーストアにCAの証明書とサーバ証明書をインポートする

できあがったサーバ証明書と、最初に作成したCAの証明書を、キーストアへインポートします。
サーバ証明書はCAの署名がついており、キーストアはキーストア自信が信頼したとマークしたCAの署名がないとインポートできません。

ですので、まずはCAの証明書をインポートして、CAの署名をキーストアに認識させます。

```keytool.exe
C:\openssl-1.0.2a-x64_86-win64>keytool -import -alias ca -trustcacerts -file ca.crt -keystore tomcat.jks
キーストアのパスワードを入力してください:
所有者: EMAILADDRESS=ca@a-pz.jp, CN=a-pz.jp, OU=Capy1, O=Capybara, L=Shinagawa, ST=Tokyo, C=JP
発行者: EMAILADDRESS=ca@a-pz.jp, CN=a-pz.jp, OU=Capy1, O=Capybara, L=Shinagawa, ST=Tokyo, C=JP
シリアル番号: d41c5364fe68b148
有効期間の開始日: Tue Jul 25 12:18:30 JST 2017終了日: Thu Aug 24 12:18:30 JST 2017
証明書のフィンガプリント:
         MD5:  D4:F1:5D:76:82:A3:E3:9E:A0:B0:AA:2E:9B:1E:09:98
         SHA1: 63:7A:53:3A:A8:EE:49:11:9B:39:17:0E:DC:BE:22:38:BB:03:B0:DD
         SHA256: 65:24:52:99:50:EC:57:59:C4:DD:67:22:99:B7:B6:B5:B1:7A:E7:5C:7C:30:19:AF:41:20:C8:1D:BC:8A:24:2D
         署名アルゴリズム名: SHA256withRSA
         バージョン: 1
この証明書を信頼しますか。 [いいえ]:  y
証明書がキーストアに追加されました
```
これで、自己署名のCAから発行した証明書を受け入れる準備が整いましたので、続いてはサーバ証明書をキーストアへインポートします。

```keytool.exe
C:\openssl-1.0.2a-x64_86-win64>keytool -import -alias tomcat -trustcacerts -file tomcat.crt -keystore tomcat.jks
キーストアのパスワードを入力してください:
証明書応答がキーストアにインストールされました
```

鍵ペアなので、-aliasは 秘密鍵と同じ tomcat に必ずします。
無事入ったようですね！

以上でサーバ証明書の作成作業は完了です。

## Tomcatに対してキーストアを有効にする

Tomcat用の鍵ペアが格納してあるキーストア tomcat.jks を、Tomcatに読み込ませます。

設定はTomcatのインストールディレクトリ/conf/server.xmlにて行います。インストール直後の設定では、SSLの設定はコメントになっています。server.xmlの中から Connector要素を探し、その次の行に以下の内容を追加します。

```xml:server.xml(抜粋)
<Connector 	maxThreads="150" port="8443"
			protocol="org.apache.coyote.http11.Http11NioProtocol"
			scheme="https" secure="true" sslProtocol="TLS"
			SSLEnabled="true" disableUploadTimeout="true"
			acceptCount="100" debug="0"
			keyAlias="tomcat" clientAuth="true"
			keystorePass="password" keystoreFile="./conf/tomcat.jks" keystoreType="JKS"
			truststorePass="password" truststoreFile="./conf/tomcat.jks" truststoreType="JKS"
/>
```

クライアント認証用に追加した属性を以下に列挙します。

|属性名|説明|
|:--|:--|
|clientAuth|クライアント認証の有無|
|truststorePass|証明書チェーンを含めたキーストアのパスワード|
|truststoreFile|証明書チェーンを含めたキーストアのファイル名|
|truststoreType|証明書チェーンを含めたキーストアのファイル形式|

> 設定の詳細については https://tomcat.apache.org/tomcat-8.0-doc/ssl-howto.html#Configuration を参照に。

以上でTomcatでHTTPSが有効になり、クライアント認証も有効になります（暗号化プロトコルはTLS1.2が選ばれます)

### Webアプリケーションでクライアント認証を有効にする

サーバ側はクライアント認証を有効にしましたが、搭載するWebアプリケーションにも設定が必要です。クライアント認証（証明書の提示）のみ行うのが今回の目的ですので、設定は非常に簡素です。

web.xmlに次の一式を追加します。たいていの場合は最下部で良いです。

```xml:web.xml
<login-config>
	<auth-method>CLIENT-CERT</auth-method>
	<realm-name>[任意の英数で名前をつける]</realm-name>
</login-config>
```

以上の設定を行い、Tomcatを起動して動作確認をします。

http://[サーバのドメインないしはIPアドレス]:8443/ 以下にアクセスすると次の画面にあるように証明書選択のポップアップが表示されます。

有効な証明書がインストールされていればこの画面のように証明書を選択します。

![selectcert.png](https://qiita-image-store.s3.amazonaws.com/0/13268/e807ef49-0fb6-0bef-866f-5950ffd65740.png)

選択後、OKを押してリクエストしたURLが表示されれば完了です。

お疲れ様でした！