クライアント証明書を要求するサイトの構築（Apache+OpenSSL）その1

参考にしたサイト
----

* [OpenSSL CSR生成スクリプト - dondari](https://www.dondari.com/OpenSSL_CSR%E7%94%9F%E6%88%90%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88)
* [null-i.net - Linux/OpenSSL](https://null-i.net/index.html?Linux/OpenSSL)
* [openssl](http://freebsd.sing.ne.jp/FreeBSD/10Web/10.03.html)
* [apache](http://www.nina.jp/server/slackware/httpd/ssl.conf.html)

[クライアント証明書を要求するサイトの構築（Apache+OpenSSL）その2 - Qiita [キータ]](http://qiita.com/tukiyo/items/9633b8b5f63e887f55b1)書きました。


## ランダムパスワード生成方法

* 多くのパスワードを作る必要があるので`pwgen`使うと便利。


## サーバ証明書の作成

### 1. 独自認証局の秘密鍵、ルート証明書(CA)の作成

* C: 国名 (JP)
* ST: 都道府県 (Tokyo)
* O: 会社名 (My Company Ltd)
* OU: 部署名 (sales)
* CN:サーバーのドメイン名 (`mycompany.co.jp:8000`等。証明書の表示名に使われます。)

```bash:
cd /opt/
mkdir -p demoCA/{private,newcerts}
cd demoCA
echo 01 > serial
```

```bash:
openssl req \
  -x509 \
  -days 3650 \
  -newkey rsa:2048 \
  -keyout private/cakey.pem \
  -out cacert.pem \
  -subj "/C=JP/ST=Tokyo/O=localhost/OU=test/CN=$(hostname)"

# cakey.pem のパスワードを入力
```

| file       | 備考             |
|:-----------|:----------------|
| private/cakey.pem | 秘密鍵    |
| cacert.pem        | 認証局(CA)証明書 |


### 2. 秘密鍵とサーバ署名要求(CSR)の作成

```bash:サーバ証明書
cd /opt/demoCA/

openssl req \
  -newkey rsa:2048 \
  -keyout newkey.pem \
  -out newreq.pem \
  -subj "/C=JP/ST=Tokyo/O=localhost/OU=test/CN=$(hostname)"

# newkey.pem のパスワードを入力
```

| file       | 備考                       |
|:-----------|:---------------------------|
| newkey.pem | ホストキー(パスワードあり) |
| newreq.pem | ホスト証明書               |


### 3.パスワードなしのホストキー作成

apacheに組みこむ用。

```bash:/opt/demoCA/
openssl rsa -in newkey.pem -out nokey.pem
# newkey.pem のパスワードを入力
```

### 4. CSRへの署名

```bash:/opt/demoCA/
touch index.txt
cd /opt

openssl ca \
  -in demoCA/newreq.pem \
  -days 3650 \
  -out demoCA/cert.pem \
  -notext
# /opt/demoCA/private/cakey.pem のパスワードを入力
```

### 5. openssl.cnfを編集

```bash:
cd /opt/demoCA
cp -a /etc/ssl/openssl.cnf .
```

openssl.cnfを書き換える

```diff:openssl.cnf.patch
/opt/demoCA/openssl.cnf--- /etc/ssl/openssl.cnf	2017-05-19 17:31:50.000000000 +0900
+++ openssl.cnf	2018-10-16 09:16:10.628048155 +0900
@@ -39,7 +39,7 @@
 ####################################################################
 [ CA_default ]
 
-dir		= ./demoCA		# Where everything is kept
+dir		= /opt/demoCA		# Where everything is kept
 certs		= $dir/certs		# Where the issued certs are kept
 crl_dir		= $dir/crl		# Where the issued crl are kept
 database	= $dir/index.txt	# database index file.
@@ -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	= sha256		# use public key default MD
 preserve	= no			# keep passed DN ordering
 
 # A few difference way of specifying how similar the request should look
@@ -85,8 +85,8 @@
 countryName		= match
 stateOrProvinceName	= match
 organizationName	= match
-organizationalUnitName	= optional
-commonName		= supplied
+organizationalUnitName	= supplied
+commonName		= optional
 emailAddress		= optional
 
 # For the 'anything' policy
@@ -97,8 +97,8 @@
 stateOrProvinceName	= optional
 localityName		= optional
 organizationName	= optional
-organizationalUnitName	= optional
-commonName		= supplied
+organizationalUnitName	= supplied
+commonName		= optional
 emailAddress		= optional
 
 ####################################################################
@@ -126,23 +126,23 @@
 
 [ req_distinguished_name ]
 countryName			= Country Name (2 letter code)
-countryName_default		= AU
+countryName_default		= JP
 countryName_min			= 2
 countryName_max			= 2
 
 stateOrProvinceName		= State or Province Name (full name)
-stateOrProvinceName_default	= Some-State
+stateOrProvinceName_default	= Tokyo
 
 localityName			= Locality Name (eg, city)
 
 0.organizationName		= Organization Name (eg, company)
-0.organizationName_default	= Internet Widgits Pty Ltd
+0.organizationName_default	= localhost
 
 # we can do this but it is not needed normally :-)
 #1.organizationName		= Second Organization Name (eg, company)
 #1.organizationName_default	= World Wide Web Pty Ltd
 
-organizationalUnitName		= Organizational Unit Name (eg, section)
+organizationalUnitName		= Organizational Unit Name (eg, username)
 #organizationalUnitName_default	=
 
 commonName			= Common Name (e.g. server FQDN or YOUR name)
```


### 6. 証明書失効リスト用

```bash:
echo "00" > crlnumber
```

## クライアント用証明書の作成

### p12キーの生成

各クライアントに配布する用にp12キーを生成。
クライアント名.p12 のパスワードも伝える必要がありますので、クライアント名.p12.txtにパスワードを書いときます。
このp12キーはクライアントの端末にインストールすることになります。

* [certificate - RSA鍵、証明書のファイルフォーマットについて - Qiita](http://qiita.com/kunichiko/items/12cbccaadcbf41c72735)
* [RSA - PKCS #12 個人情報交換ファイルフォーマットについて - Qiita](http://qiita.com/kunichiko/items/3e2ec27928a95630a73a)

```bash:/opt/demoCA/create_cert.sh
#!/bin/sh
set -eux

CLNT=$1
CA_ROOT=/opt/demoCA
REPOS=${CA_ROOT}/repository
mkdir -p $REPOS/${CLNT}

# key : 秘密鍵生成
openssl genrsa -rand /var/log/messages -out ${REPOS}/${CLNT}/${CLNT}-client.key -des3 2048

# key -> csr : 署名要求
openssl req -new -key ${REPOS}/${CLNT}/${CLNT}-client.key -out ${REPOS}/${CLNT}/${CLNT}-client.csr -config openssl.cnf 

# csr -> pem : 署名(Base64形式)
openssl ca -config openssl.cnf -keyfile ${CA_ROOT}/private/cakey.pem -cert ${CA_ROOT}/cacert.pem -in ${REPOS}/${CLNT}/${CLNT}-client.csr -out ${REPOS}/${CLNT}/${CLNT}-client.pem

# pem -> crt : 証明書
openssl x509 -in ${REPOS}/${CLNT}/${CLNT}-client.pem -out ${REPOS}/${CLNT}/${CLNT}-client.crt

# crl (失効リスト更新)
openssl ca -config openssl.cnf -gencrl -out ${CA_ROOT}/ca.crl -keyfile ${CA_ROOT}/private/cakey.pem -cert ${CA_ROOT}/cacert.pem -verbose -crldays 3650

# p12 (webブラウザ用。秘密鍵と公開鍵(証明書)を PKCS #12ファイルにまとめる)
openssl pkcs12 -export -in ${REPOS}/${CLNT}/${CLNT}-client.crt -inkey ${REPOS}/${CLNT}/${CLNT}-client.key -out ${REPOS}/${CLNT}/${CLNT}-client.p12 -certfile ${CA_ROOT}/cacert.pem -name 'demoCA Client Cert' -caname 'demoCA'

#p12.txt (p12のパスワードをtxtに保存)
echo -n "enter ${CLNT}.p12 pass : "
read P12PASS
echo ${P12PASS} > ${REPOS}/${CLNT}/${CLNT}-client.p12.txt
```

途中パスワード入力を間違えると手動でのリカバリーが大変なのでバージョン管理で戻せるようにする。

```bash:
chmod +x create_cert.sh

git init
git add -A && git commit -m "init"
```

```sh:使い方
./create_cert.sh kitamura
# repository/ユーザ名/ユーザ名.key のパスワード
# repository/ユーザ名/ユーザ名.p12 のパスワード
# private/cakey.pemのパスワード
```

```bash:
git add -A && git commit -m "add: クライアント名"
```

## Apacheの設定

```sh
sudo sh -c "echo ServerName $HOSTNAME" >> /etc/apache2/httpd.conf
```

```apache:/etc/apache2/sites-available/default-ssl.conf
SSLCertificateFile    /opt/demoCA/cert.pem
SSLCertificateKeyFile /opt/demoCA/nokey.pem
SSLCACertificateFile  /opt/demoCA/cacert.pem
<Location /owncloud/>
    SSLVerifyClient require
</Location>
```

* より詳しい書き方は[クライアント証明書を要求するサイトの構築（Apache+OpenSSL）その2 - Qiita [キータ]](http://qiita.com/tukiyo/items/9633b8b5f63e887f55b1)

```sh
a2enmod ssl
a2ensite default-ssl

sudo apachectl configtest
sudo /etc/init.d/apache2 restart
```

## (memo) pemファイルの変換

* [証明書ファイルの種類とopensslでの変換方法](http://www.digicert.ne.jp/howto/basis/file_types.html)

```bash:
openssl pkcs12 -export -in [input_file] -out [output_file]
```

```bash:
openssl x509 -in [input_file] -inform PEM -text -noout
```