iOS13からのメールサーバへの接続にかんするメモ

# 使用環境がiOS13になったら、自前メールサーバにPOP over SSLで接続できなくなった

## 症状

「サーバの識別情報を検証できません。」「"...."の識別情報をメールで確認できません。」

## SSLの証明書に対するポリシーが変わっていたのが原因。

詳しくは[公式サイト](https://support.apple.com/ja-jp/HT210176)参照. 自己証明書でなくて、ちゃんとした証明書を取れば問題ないのだろうが、しばらくの間でも自己証明書の更新でしのぐ。ポイントは、CA証明書のSAN(subjectAltName)という項目にIP/DNSとextendedKeyUsageという項目を適切につけて、SHA2を使え、ということらしい。

## SSL証明書の更新

SSLの鍵作成／証明書作成関係のコマンドは、数年に1回調べて叩いて、そのたび忘れる。また、忘れると思う。
Google検索するといくつかのサイトが見つかが、今回主に参考にしたのは下記。
 - [OpenSSLコマンドの備忘録](https://qiita.com/takech9203/items/5206f8e2572e95209bbc)
 - [OpenSSLでX.509 v3の証明書を作成する] (https://qiita.com/SRsawaguchi/items/f4dc743198e1246fff64)
 - [証明書に x509v3 拡張属性を追加する] (https://access.redhat.com/ja/solutions/2586691)
 - [オレだよオレオレ認証局で証明書つくる](https://qiita.com/ll_kuma_ll/items/13c962a6a74874af39c6)
 - [iPhone/iPad等iOSに自己署名オレオレCA証明書からサーバー/クライアント証明書を署名しインストールする](https://qiita.com/ll_kuma_ll/items/13c962a6a74874af39c6)

## OpenSSLの確認／インストール。

十分信頼できるバージョンのOpenssl実装がインストールされていればこの作業はスキップできるだろう。ちょっと自前サーバーの環境の事情により、opensslのバージョンアップから始める。事情により、[Libressl](https://www.libressl.org/)を使うことにする。

```tcsh:インストール例
% wget https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.9.2.tar.gz
% tar xvf libressl-2.9.2.tar.gz
% cd libressl-2.9.2
%  ./configure --prefix=/usr/local/depot/libressl-2.9.2 --with-openssldir=/usr/local/depot/libressl-2.9.2/openssl LDFLAGS=-Wl,-rpath,/usr/local/depot/libressl-2.9.2/lib
% make
% make check
% su -c "make install"
```
インストール後、

```tcsh:ランタイムライブラリの確認
(linux)% ldd /usr/local/depot/libressl-2.9.2/bin/openssl
(macOS)% otool -L /usr/local/depot/libressl-2.9.2/bin/openssl
```
などの方法で、読み込まれるライブラリ(`libssl.{so,dylib}`, `libcrypto.{so,dylib}`)のパスが所望のものか確認する。

## 設定ファイルをアップデート

### `/usr/local/depot/libressl-2.9.2/openssl/openssl.cnf`
`openssl`コマンドを叩くごとに、地域名や組織名などをn回入力するのは手間なので、それらのデフォルト値を設定する。
  - `countryName_default`
  - `stateOrProvinceName_default`
  - `localityName_default`
  - `0.organizationName_default`
  - `organizationalUnitName_default`
  - `commonName_default,emailAddress_default`
 これらのパラメータをそれぞれの`_default`がつかないパラメータ定義にあるプロンプト文を参考に設定しておく。また、（実際のIPアドレス、FQDNに合わせて適宜修正のうえ）下記を追記する。下記例の`san_attr_ca`というセクション名は、あとの`openssl`の実行時オプションと整合性がとれていればなんでもよい。

```.ini:opessl.cnfに追記
[ san_attr_ca ]
basicConstraints=critical,CA:true,pathlen:1
keyUsage=digitalSignature,keyCertSign,cRLSign
extendedKeyUsage=serverAuth,clientAuth,codeSigning,emailProtection
subjectAltName=IP:000.000.000.000,DNS:samplehost.sampledomain.sampleTLD
```

以下は`root`で、`/usr/local/depot/libressl-2.9.2/openssl/certs`以下で作業した。

### 'x509v3-mod.cnf'の作成。

`/usr/local/depot/libressl-2.9.2/openssl/x509v3.cnf`を参考に。

```.ini:x509v3-mod.cnf
[ x509v3_san_attr ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage=serverAuth,clientAuth,emailProtection
subjectAltName=IP:000.000.000.000,DNS:samplehost.sampledomain.sampleTLD
```

## 鍵／証明書群を作成

今後、作業手順を毎回思い出す手間を省くため,今回作業手順を`Makefile`で記述。今後は`% make`と叩けば、あとはプロンプトに従って答えるのみ。自己認証局名(下記例だと`samplehostname`)や、作成される署名済みキーのbasename(下記例では`pop3s`)は適宜修正して使用する。作成される暗号化されていない秘密鍵に関しては取り扱い注意。

```Makefile:Makefile
OPENSSL=/usr/local/depot/libressl-2.9.2/bin/openssl
KEYBITS=4096
SERIALNO=0
CA_HOSTNAME=samplehostname
CA_EXTSEC=san_attr_ca
EXTCNF=x509v3-mod.cnf
EXTCNFSEC=x509v3_san_attr
SERVICE_NAME=pop3s
EXPIREDAYS=824

#.PHONY: usage
#.SUFFIXES: .key .csr .crt .pem
#.PRECIOUS: %.key %.csr %.crt %.pem

all: $(SERVICE_NAME).pem $(CA_HOSTNAME)_ca_certs.cer

$(CA_HOSTNAME)_ca.key:
	umask 077 ; $(OPENSSL) genrsa -des3 -out $@ $(KEYBITS) 
$(CA_HOSTNAME)_ca.csr: $(CA_HOSTNAME)_ca.key
	umask 077 ; $(OPENSSL) req -utf8 -new -key $(@:.csr=.key) -sha256 -out $@
$(CA_HOSTNAME)_ca.crt: $(CA_HOSTNAME)_ca.key $(CA_HOSTNAME)_ca.csr
	umask 077 ; $(OPENSSL) req -utf8 -x509 -new -key $(@:.crt=.key) -in $(@:.crt=.csr) -out $@ -set_serial $(SERIALNO) -days $(EXPIREDAYS) -sha256 -extensions $(CA_EXTSEC) -nodes
	openssl x509 -text -in $@
$(CA_HOSTNAME)_ca_certs.pem: $(CA_HOSTNAME)_ca.key $(CA_HOSTNAME)_ca.crt
	umask 077 ; cat $^ > $@
$(CA_HOSTNAME)_ca_certs.cer:
	umask 077 ; $(OPENSSL) x509 -outform der -in $(@:.cer=.pem) -out $@
	openssl x509 -text -in $@

$(SERVICE_NAME).key:
	umask 077 ; $(OPENSSL) genrsa -des3 -out $(@:.key=.enc.key) $(KEYBITS) ; $(OPENSSL) rsa -in $(@:.key=.enc.key) -out $@
$(SERVICE_NAME).csr: $(SERVICE_NAME).key
	umask 077 ; $(OPENSSL) req -new -key $(@:.csr=.key) -sha256 -out $@
$(SERVICE_NAME).crt: $(SERVICE_NAME).csr $(CA_HOSTNAME)_ca_certs.pem $(CA_HOSTNAME)_ca.key $(EXTCNF)
	umask 077 ; $(OPENSSL) x509 -req -CA $(CA_HOSTNAME)_ca_certs.pem -CAkey $(CA_HOSTNAME)_ca.key -CAcreateserial -CAserial $(CA_HOSTNAME)_ca.srl -in $(@:.crt=.csr) -out $@ -days $(EXPIREDAYS) -sha256 -extfile $(EXTCNF) -extensions $(EXTCNFSEC)
	openssl x509 -text -in $@ -inform der
$(SERVICE_NAME).pem: $(SERVICE_NAME).key $(SERVICE_NAME).crt
	umask 077 ; cat $^ > $@
```

いくつか作成されるファイルのうち、以下で使用するのは、
  - 自己認証局の証明書(上記例だと samplehost_ca_certs.cer)
  - サービス用の署名済み鍵(上記例だと、pop3s.pem)
 
## サーバー側の作業

上記で作成したサービス用の署名済みの鍵(上記例だと、pop3s.pem)を適切なところに置く。これはpop3s/imapsサーバーの仕様／設定によるだろう。

## iOS端末側の設定

  1. なんらかの安全な手段で、iOSの端末に自己認証局の証明書(上記例だと samplehost_ca_certs.cer)を端末に送る。(メール添付／クラウド経由/webサーバ経由...)をダウンロードする。
  2. 「設定」アプリ → 「プロファイルがダウンロードされました」→ 「インストール」...　と進み、**種々の警告メッセージを読んで、リスクを理解して**インストールする。
  3. 「設定」アプリ → 「一般」→「情報」→ 「証明書信頼設定」→ 「ルート証明書を全面的に信頼する」の欄で該当するものを**種々の警告メッセージを読んで、リスクを理解したうえで**有効にする。 

### 最後に

サーバー管理者以外設定できないですね。ユーザーとしては、まっとうなサーバーを使う以外にないですね。