セキュアなIoTデバイス通信をやってみた(MbedTLS移植編-httpsテスト-サーバー構築)

#はじめに#
2019年5月に[IoTLT](https://iotlt.connpass.com/)にてLTをさせていただきました。
その内容のフォローを記入しながら自分なりに整理していきたいと思います。

発表内容：
[スタートアップIoTデバイスのセキュリティを考える](https://www.slideshare.net/KentaroMitsuyasu1/iot-144602008)

#前回までのあらすじ#
[セキュアなIoTデバイス通信をやってみた(MbedTLS移植編-Provision)](https://qiita.com/kmitsu76/items/0b90aef3effe8ba4a0cd)でATECC608Aから抜き出した公開鍵を使って、自己認証局から署名を行いデバイス証明書を作成しました。
その後、証明書をATECC608Aへ保存するところまで行いました。

今回の記事に関連するソースコードは下記にて公開中。
[kmwebnet/ECC608-httpsconnection](https://github.com/kmwebnet/ECC608-httpsconnection)

#https接続環境整備#

httpsサーバーを構築し、接続先として設定する。
下記を参考に、最新のApacheとOpenSSL1.1.1で環境を構築する。
[Apache2.4.37 + OpenSSL1.1.1aをソースからインストール](https://ji0vwl.net/index.php/2019/01/04/1603/)

■サーバー証明書作成
デバイス側から、サーバーを認証するためのサーバー証明書を
前回[セキュアなIoTデバイス通信をやってみた(MbedTLS移植編-Provision)](https://qiita.com/kmitsu76/items/0b90aef3effe8ba4a0cd)で使用した
root-ca.crt
root-ca.key
signer-ca.crt
signer-ca.key
を用意する。

Openssl1.1.1のインストール先/usr/local/openssl-1.1.1の配下へCAディレクトリを作成、上記4つをその中へコピー、その中で証明書作成を行う。
以下のようにディレクトリを整える

```
cd /usr/local/openssl-1.1.1
mkdir certs
mkdir private
mkdir crl
mkdir newcerts
mkdir revoke

chmod 700 private
echo "01" > serial

touch index.txt

cp /usr/local/openssl-1.1.1/ssl/openssl.cnf .
```

openssl.cnfの設定内容。変更点のみ。

```
####################################################################
[ CA_default ]

dir             = /usr/local/openssl-1.1.1/CA           # 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.
#unique_subject = no                    # Set to 'no' to allow creation of
                                        # several ctificates with same subject.
new_certs_dir   = $dir/newcerts         # default place for new certs.

certificate     = $dir/signer-ca.pem    # The CA certificate
serial          = $dir/serial           # The current serial number
crlnumber       = $dir/crlnumber        # the current crl number
                                        # must be commented out to leave a V1 CRL
crl             = $dir/crl.pem          # The current CRL
private_key     = $dir/private/signer-ca.key # The private key
RANDFILE        = $dir/private/.rand    # private random number file


default_days    = 3650                  # how long to certify for
default_crl_days= 90                    # how long before next CRL
default_md      = sha256                # use SHA-256 by default
preserve        = no                    # keep passed DN ordering

# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy          = policy_match

# For the CA policy
[ policy_match ]
countryName             = optional
stateOrProvinceName     = optional
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ svr_cert ]

# These extensions are added when 'ca' signs a request.

# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.

basicConstraints=CA:FALSE

# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.

# This is OK for an SSL server.
nsCertType                      = server

# For an object signing certificate this would be used.
# nsCertType = objsign

# For normal client use this is typical
# nsCertType = client, email

# and for everything including object signing:
# nsCertType = client, email, objsign

# This is typical in keyUsage for a client certificate.
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

# This will be displayed in Netscape's comment listbox.
nsComment                       = "OpenSSL Generated Certificate"

# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer

# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
subjectAltName= @alt_names

[ alt_names ]

DNS.1 = iot.testcorp.com //変更
DNS.2 = *.testcorp.com　//変更
```

■サーバー証明書の秘密鍵とCSRを作成

```
openssl req -new -config openssl.cnf -newkey ec:<(openssl ecparam -name prime256v1) -keyout server.key -subj "/O=testcorp/OU=iot/CN=iot.testcorp.com" -out server.csr
```

■サーバー証明書を中間CAで署名

```
openssl ca -config openssl.cnf -extensions svr_cert -keyfile signer-ca.key -cert signer-ca.crt -in server.csr -out server.crt -days 3650
```
■完成したサーバー証明書のチェーンを作成する
apache2.4.8以降では、サーバー証明書の設定の仕方が変わったとのこと。
[apache 2.4におけるSSL証明書の設定](https://qiita.com/bageljp@github/items/6b9876b7571852284ead)
証明書チェーン作成に対応するため、スクリプトを使う。
下記をbundle.shで保存、chmod +xで実行権限をつける。

```
#!/bin/sh
#
# Convert PEM Certificate to ca-bundle.crt format
#

test ! $1 && printf "Usage: `basename $0` certificate" && exit 1

# Friendly Name and Underline Friendly Name with equal signs
openssl x509 -in $1 -text -noout | sed -e 's/^  *Subject:.*CN=\([^,]*\).*/\1/p;t  c' -e 'd;:c' -e 's/./=/g'
# Output Fingerprint and swap = for :
openssl x509 -in $1 -noout -fingerprint | sed -e 's/=/: /'
# Output PEM Data:
echo 'PEM Data:'
# Output Certificate
openssl x509 -in $1
# Output Certificate text swapping Certificate with Certificate Ingredients
openssl x509 -in $1 -text -noout | sed -e 's/^Certificate:/Certificate Ingredients:/'
```

■bundle.shを使った証明書の連結
以下のように、サーバー証明書→中間証明書→ルートCA証明書の順番に連結させる。

```
[root@ CA]# ./bundle.sh server.crt > server.chain
[root@ CA]# ./bundle.sh signer-ca.pem >> server.chain
[root@ CA]# ./bundle.sh root-ca.pem >> server.chain

```



■接続htmlの準備

以下のようにテストページを用意する。

```
[root@ ~]# echo "<html><body><h1>It works</h1></body></html>" > /usr/local/httpd2/htdocs/index.html
```

■apache　SSL設定
以下のように設定。変更点のみ記載。サーバー側からクライアント証明書を要求し、ATECC608Aからデバイス証明書を発行させる設定とする。

```
[root@ ~]# cat /usr/local/httpd2/conf/extra/httpd-ssl.conf

#
# When we also provide SSL we have to listen to the
# standard HTTP port (see above) and to the HTTPS port
#
Listen 4689

#   SSL Cipher Suite:
#   List the ciphers that the client is permitted to negotiate,
#   and that httpd will negotiate as the client of a proxied server.
#   See the OpenSSL documentation for a complete list of ciphers, and
#   ensure these follow appropriate best practices for this deployment.
#   httpd 2.2.30, 2.4.13 and later force-disable aNULL, eNULL and EXP ciphers,
#   while OpenSSL disabled these by default in 0.9.8zf/1.0.0r/1.0.1m/1.0.2a.
#SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES
#SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES

SSLCipherSuite "ECDHE-ECDSA-AES128-GCM-SHA256 \
ECDHE-ECDSA-AES256-GCM-SHA384 \
ECDHE-ECDSA-AES128-SHA \
ECDHE-ECDSA-AES256-SHA \
ECDHE-ECDSA-AES128-SHA256 \
ECDHE-ECDSA-AES256-SHA384 \
ECDHE-RSA-AES128-GCM-SHA256 \
ECDHE-RSA-AES256-GCM-SHA384 \
ECDHE-RSA-AES128-SHA \
ECDHE-RSA-AES256-SHA \
ECDHE-RSA-AES128-SHA256 \
ECDHE-RSA-AES256-SHA384 \
DHE-RSA-AES128-GCM-SHA256 \
DHE-RSA-AES256-GCM-SHA384 \
DHE-RSA-AES128-SHA \
DHE-RSA-AES256-SHA \
DHE-RSA-AES128-SHA256 \
DHE-RSA-AES256-SHA256"

#   User agents such as web browsers are not configured for the user's
#   own preference of either security or performance, therefore this
#   must be the prerogative of the web server administrator who manages
#   cpu load versus confidentiality, so enforce the server's cipher order.
SSLHonorCipherOrder off

#   SSL Protocol support:
#   List the protocol versions which clients are allowed to connect with.
#   Disable SSLv3 by default (cf. RFC 7525 3.1.1).  TLSv1 (1.0) should be
#   disabled as quickly as practical.  By the end of 2016, only the TLSv1.2
#   protocol or later should remain in use.
SSLProtocol all -SSLv3
SSLProxyProtocol all -SSLv3


##
## SSL Virtual Host Context
##

SSLStrictSNIVHostCheck off

<VirtualHost *:4689>

#   General setup for the virtual host
DocumentRoot "/usr/local/httpd2/htdocs"
ServerName iot.testcorp.com:4689
ServerAdmin you@example.com
LogLevel debug
ErrorLog "/usr/local/httpd2/logs/error_log"
TransferLog "/usr/local/httpd2/logs/access_log"

    <Directory "/usr/local/httpd2/htdocs">
        Options FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>


#   SSL Engine Switch:
#   Enable/Disable SSL for this virtual host.
SSLEngine on

#   Server Certificate:
#   Point SSLCertificateFile at a PEM encoded certificate.  If
#   the certificate is encrypted, then you will be prompted for a
#   pass phrase.  Note that a kill -HUP will prompt again.  Keep
#   in mind that if you have both an RSA and a DSA certificate you
#   can configure both in parallel (to also allow the use of DSA
#   ciphers, etc.)
#   Some ECC cipher suites (http://www.ietf.org/rfc/rfc4492.txt)
#   require an ECC certificate which can also be configured in
#   parallel.
SSLCertificateFile "/usr/local/openssl-1.1.1/CA/server.chain"
#SSLCertificateFile "/usr/local/httpd2/conf/server-dsa.crt"
#SSLCertificateFile "/usr/local/httpd2/conf/server-ecc.crt"

#   Server Private Key:
#   If the key is not combined with the certificate, use this
#   directive to point at the key file.  Keep in mind that if
#   you've both a RSA and a DSA private key you can configure
#   both in parallel (to also allow the use of DSA ciphers, etc.)
#   ECC keys, when in use, can also be configured in parallel
SSLCertificateKeyFile "/usr/local/openssl-1.1.1/CA/server.key"
#SSLCertificateKeyFile "/usr/local/httpd2/conf/server-dsa.key"
#SSLCertificateKeyFile "/usr/local/httpd2/conf/server-ecc.key"

#   Certificate Authority (CA):
#   Set the CA certificate verification path where to find CA
#   certificates for client authentication or alternatively one
#   huge file containing all of them (file must be PEM encoded)
#   Note: Inside SSLCACertificatePath you need hash symlinks
#         to point to the certificate files. Use the provided
#         Makefile to update the hash symlinks after changes.
#SSLCACertificatePath ""
SSLCACertificateFile "/usr/local/openssl-1.1.1/CA/root-ca.pem"

#   Client Authentication (Type):
#   Client certificate verification type and depth.  Types are
#   none, optional, require and optional_no_ca.  Depth is a
#   number which specifies how deeply to verify the certificate
#   issuer chain before deciding the certificate is not valid.
SSLVerifyClient require
SSLVerifyDepth  10

</VirtualHost>
```

ここまでで接続先サーバーの準備が完了した。