iOS 13でHTTPS通信を行う場合について

iOS 13とmacOS 10.15から、信頼済み証明書の要件が変更されました。そのため、これまで使用していた証明書が新しい要件に対応していない場合、外部サーバとの通信ができなくなります。

詳しくは、[iOS 13 および macOS 10.15 における信頼済み証明書の要件](https://support.apple.com/ja-jp/HT210176) を参照してください。

今回は、[OpenSSL](https://www.openssl.org)を使用して、iOS 13とmacOS 10.15に対応したWebサーバ用の秘密鍵や証明書、クライアント用のCA証明書を作成してみたいと思います。

作成環境は、macOS CatalinaにOpenSSLをインストールして行っていますが、OpenSSLのコマンドは、他の環境でも利用できると思いますので、適宜、環境に合わせて読み替えてください。

今回は、macOS Catalinaに標準でインストールされているローカル環境のWebサーバ（Apache）とiOS 13端末がHTTPS通信を行うことを想定しています。


# OpenSSLをインストールする

macOS Catalinaでは、OpenSSLがインストールされていますが、ターミナルで `openssl version` コマンドを実行すると `LibreSSL 2.8.3` のうように表示されます。

そのため、HomebrewからOpenSSLをインストールして、こちらを使用します。
OpenSSLのインストールコマンドは、下記になります。

```
brew install openssl
```

Homebrewのインストール方法は、[Homebrew](https://brew.sh/index_ja)を参照してください。
HomebrewからOpenSSLをインストールしても、まだLibreSSLが優先されているため、`.zshrc` に設定を追加します。

```
echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.zshrc
```

コマンド実行後、一度、ターミナルを閉じて、再度開き直してください。
これで、OpenSSLが利用可能になります。


# openssl.cnfの設定

今回は、テスト用のためテスト専用のopenssl.cnfを作成して使用します。

  - /etc/ssl
  - /usr/local/etc/openssl＠1.1

にあるopenssl.cnfは、最小構成の設定だったため、今回は、「/usr/local/etc/openssl＠1.1」の「openssl.cnf.dist」を「openssl.cnf」にリネームして使用しました。

下記のように、openssl.cnfの設定を修正、追加します。

```
[ CA_default ]
dir		= ./			        # Where everything is kept
default_days	= 820			# how long to certify for

[ req_distinguished_name ]
countryName				= JP
countryName_default		= JP
countryName_min			= 2
countryName_max			= 2

stateOrProvinceName			= 都道府県名
stateOrProvinceName_default	= 都道府県名

localityName				= 市区町村名
localityName_default		= 市区町村名

0.organizationName			= 組織名
0.organizationName_default	= 組織名

organizationalUnitName			= 部門名
organizationalUnitName_default	= 部門名

commonName				= localhost
commonName_default		= localhost
commonName_max			= 64

emailAddress			= メールアドレス
emailAddress_default	= メールアドレス
emailAddress_max		= 64

[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true
keyUsage = critical, cRLSign, keyCertSign

[ SAN ]
basicConstraints = CA:false
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
authorityKeyIdentifier=keyid,issuer

subjectAltName=@alt_names
basicConstraints=CA:FALSE

[ alt_names ]
DNS.1=${req_distinguished_name::commonName}
IP.1=192.168.x.x # 環境に合わせてサーバのIPに変更します。
IP.2=127.0.0.1
```


## [ CA_default ]

  - dir: カレントディレクトリに変更します。
  - default_days: 新しい要件では、TLSサーバ証明書の有効期間は `825日以下` にする必要があるため、今回は820日に変更しています。

## [ req_distinguished_name ]

`req_distinguished_name` に、

  - countryName: 国名
  - stateOrProvinceName: 都道府県名
  - localityName: 市区町村名
  - organizationName: 組織名
  - organizationalUnitName: 部門名
  - commonName: コモンネーム
  - emailAddress: メールアドレス

を設定することで、証明書署名要求（CSR）作成時に入力を楽にすることができます。今回は、検証用なので、req_distinguished_name を設定し、`[ alt_names ]` の `DNS.1` の値に `commonName` を設定しています。環境に合わせて設定してください。

## [ v3_ca ]

  - keyUsage: コメントを外します。拡張領域を含める必要があるため、`critical` を追加して拡張領域の重要度を追加しています。


## [ SAN ]

`Digital Signature` や `TLS Web Server Authentication`、`DNS情報` 等の要件に必要な設定を行うために、`Subject Alternative Name（SAN）` を追加しています。


## [ alt_names ]

[[ SAN ]](#-san-)で使用する `subjectAltName` を指定するために追加しています。


# 作業フォルダーの準備

秘密鍵や証明書を作成するための作業フォルダーを作成し、そこにopenssl.cnfを保存します。次に、作業フォルダーにインデックスやシリアル用の、

  - index.txt
  - index.txt.attr
  - serial
  
を作成します。

ターミナルを起動し、作業フォルダーに移動後、下記のコマンドを実行します。

```
touch index.txt
touch index.txt.attr
echo 00 > serial
```


# CA証明書の作成

iOS端末にインストールするCA証明書を作成していきます。


## CA秘密鍵の作成

CA秘密鍵を作成するには、下記のコマンドを実行します。

```
openssl genrsa -aes256 -out cakey.pem 2048
```

コマンド実行時に、パスワードを設定します。このパスワードは、以降の処理で使用します。


## CA証明書署名要求（CSR）の作成

CA証明書署名要求（CSR）を作成するには、下記のコマンドを実行します。

```
openssl req -new -key cakey.pem -config openssl.cnf -out cacert.csr
```

パスワードを求められた場合は、[CA秘密鍵の作成](#ca秘密鍵の作成) で入力したパスワードを入力します。


## CA証明書の作成

CA証明書を作成するには、下記のコマンドを実行します。

```
openssl ca -in cacert.csr -selfsign -keyfile cakey.pem -notext -config openssl.cnf -outdir . -extensions v3_ca -out cacert.pem
```
パスワードを求められた場合は、[CA秘密鍵の作成](#ca秘密鍵の作成) で入力したパスワードを入力します。


## CA証明書の確認

[[ v3_ca ]](#-v3_ca-) で設定した内容が反映されているか確認します。

```
openssl x509 -in cacert.pem -text

X509v3 extensions:
    X509v3 Subject Key Identifier: 
        AF:C0:77:5D:2E:77:8A:40:15:1F:2A:03:70:57:FE:85:56:B3:DB:01
    X509v3 Authority Key Identifier: 
        keyid:AF:C0:77:5D:2E:77:8A:40:15:1F:2A:03:70:57:FE:85:56:B3:DB:01

    X509v3 Basic Constraints: critical
        CA:TRUE
    X509v3 Key Usage: critical
        Certificate Sign, CRL Sign
```


# サーバ秘密鍵と証明書の作成

Webサーバ（Apache）に設定するサーバ秘密鍵と証明書を作成していきます。

## サーバ秘密鍵の作成

サーバ秘密鍵を作成するには、下記のコマンドを実行します。

```
openssl genrsa -aes256 -out server.key 2048
```

コマンド実行時に、パスワードを設定します。このパスワードは、以降の処理で使用します。


## サーバ証明書署名要求（CSR）の作成

サーバ証明書署名要求（CSR）を作成するには、下記のコマンドを実行します。

```
openssl req -new -key server.key -config openssl.cnf -out server.csr
```

パスワードを求められた場合、[サーバ秘密鍵](#サーバ秘密鍵の作成) の作成で入力したパスワードを入力します。


## サーバ証明書の作成

サーバ証明書を作成するには、下記のコマンドを実行します。

```
openssl ca -in server.csr -config openssl.cnf -keyfile cakey.pem -outdir . -extensions SAN -out local-server.crt
```

パスワードを求められた場合、サーバ秘密鍵の作成で入力したパスワードを入力します。上記のコマンド実行後、エラーが出る場合は、

   - index.txt
   - index.txt.attr

を削除して、再度、作成し直した後にコマンドを実行すると処理が成功します。


## サーバ証明書の確認

[[ SAN ]](#-san-) や [[ alt_names ]](#-alt_names-) 、`X509v3 Authority Key Identifier` の値が、CA証明書と同じ値になっているか確認します。

```
openssl x509 -in local-server.crt -text

X509v3 extensions:
    X509v3 Key Usage: critical
        Digital Signature, Key Encipherment
    X509v3 Extended Key Usage: 
        TLS Web Server Authentication
    X509v3 Authority Key Identifier: 
        keyid:AF:C0:77:5D:2E:77:8A:40:15:1F:2A:03:70:57:FE:85:56:B3:DB:01

    X509v3 Subject Alternative Name: 
        DNS:localhost, IP Address:192.168.x.x, IP Address:127.0.0.1
    X509v3 Basic Constraints: 
        CA:FALSE
```


## サーバ秘密鍵のパスワードの削除

パスワードが設定されている場合、Webサーバ起動時にパスワードを聞かれたり、起動に失敗する場合があるため、パスワードを削除します。

```
openssl rsa -in server.key -out local-server.key
```


## キーチェーン登録用のp12ファイルの作成

p12ファイルを作成するには、下記のコマンドを実行します。

```
openssl pkcs12 -export -inkey local-server.key -in local-server.crt -out local-server.p12
```

作成された p12ファイル をキーチェーンに登録し、

情報を見る > 信頼 > この証明書を使用するとき

の設定を `常に信頼` に変更します。

「常に信頼」に設定することで、Webサーバにアクセスした際に、正常にアクセスできるようになります。


# 作成したファイルについて

これまでの作業で、以下のファイルが作成されました。

  - CA証明書：cacert.pem（iOS端末にインストールするルート証明書）
  - サーバ秘密鍵：local-server.key（Webサーバに設定する秘密鍵）
  - サーバ証明書：local-server.crt（Webサーバに設定する証明書）
  - 鍵付きサーバ証明書：local-server.p12（キーチェーンに登録する鍵付きWebサーバ証明書）

iOS端末にインストールしたCA証明書は、インストールしただけでは有効にはなりません。
iOS端末にインストール後、設定画面の

一般 > 情報 > 証明書信頼設定 > ルート証明書を全面的に信頼する

から、インストールした証明書を `有効` にします。


# おわりに

作業工程は長いですが、作成されたファイルを使用することで、ローカル上のWebサーバに対してiOS 13端末からIPアドレスでHTTPS通信ができるようになります。

Monacaで開発するアプリからHTTPS通信のテストを行いたい場合、ローカル上でテストできた方が開発効率も上がります。iOS 13とローカル上のWebサーバでHTTPSテストを行いたい場合は、一度、チャレンジしてみてください。