iPhone(iOS 10以降)でUIWebViewを使って https 通信をするときに自己署名証明書を使っている場合、どうやっても接続ができなかった。
現状
色々調べたのだが、
ServicePointManager.ServerCertificateValidationCallback を使って、SSL証明書のエラーを回避する → そもそもここが呼ばれない。
Info.plist に NSExceptionDomainsとNSExceptionAllowsInsecureHTTPLoadsを追加して例外扱いにする → NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813) のエラーになる。
非推奨の allowsAnyHTTPSCertificateForHost をオーバーライドして、ture を返す → そもそも、ここが呼ばれない。
自己証明書を使用しているサーバにHttpClientで接続 言語: C#
https://code.msdn.microsoft.com/windowsdesktop/HttpClient-fc321142
xcodeにてApp Transport Security(ATS)除外のドメインを複数設定する – Qiita
http://qiita.com/BurialMound/items/87b8c33e2c7ad767c9dc
SwiftでHTTPS通信時に自己認証証明書の警告によるエラーを無視させる – Steel Dragon 14106
http://raimon49.github.io/2015/05/27/ignore-verify-self-signed-certificate.html
ちなみに、WebView じゃなくて HttpClient とか NSURLSesstion の場合はうまく動いたりするので、WebView 固有の問題かもしれない。
最終的には、Xamarin.Forms を使うわけだが、その前段階として Xamarin.iOS と Swift で調査をしている。
「動いた」というページもあるのだが、
- 「動いた」という記事自体が少し古い
- 実機で動いても iOS シミュレータはだめっぽい、という記事がある。
- Info.plist に IP を書いた場合は動かない。ドメイン名が必要。
とあるので、ここは最初に戻って、自己署名証明書じゃない正式な証明書を使うことを検討する。
ちなみに自己署名証明書は、↓を参考にすればok。
自己署名証明書の作成方法 – Qiita
http://qiita.com/akito1986/items/8eb41f5a43bb9421ae79
検証環境自体が、
- HTTPS のサーバーは、外から見えない(検証機なので)
- HTTPS のサーバーはドメインを持っていない(内部IPだけなので)
- iOSシミュレーターでも確認したい。
という所謂、検証環境を想定しいている。
証明書を作る
証明書を作るのには、Let’s Encrypt が無償で使える。
Let’s Encrypt 総合ポータル
https://letsencrypt.jp/
適当なサーバーやクライアントの環境(Linuxかmacに限る)があれば、certbot コマンドで正式な証明書を作ることができる。この証明書は、そのまま外部のWebサーバーに載せることもできる、今回のような検証機に載せることもできる。
Let’s Encryptを使うときの制限は、
- 証明書を発行するときに、ドメインを持ち外部公開されているサーバーが必要
- 証明書の期限は3か月に制限されている
となる。外部に公開されているサーバーに証明書をおき、certbot を定期的に動作させて3か月の証明書期間を更新する仕組みなっている。
ただし、手動(manual)で証明書を取得する仕組みも用意されているので、必ずしも自動更新しなければいけない訳ではない。
問題は、「外部公開されているドメイン」が必要なところで、今回のように社内に検証機があって外部から見えないようになっている、グローバルIPを持っていない、ような場合には使えるのか?という疑問がある。なので、いきおい自己署名書になるわけだが、大丈夫、少しトリックを使うと使えるようになる。
実は、
- Let’s Encrypt から見えるサーバー
- 実際に証明書を使うサーバー
が同一である必要はない。Let’s Encrypt は、証明書を発行するときにレスポンスファイルを外部サーバーに置くことになるが、これは実際に証明書を使うサーバーとは異なっていても良いのだ。つまり証明書の servername だけ合わせておけばよいということになる。
証明書を作成する手順
Let’s EncryptでManual発行してみました | ぽちゃ猫.com
https://www.pochaneko.com/ietsencrypt-man/
を参考にしながら、certbot をインストールする。今回は mac 上でやっている。
1.仮アクセスできる外部サーバーを用意する。捨てドメイン名になるので ssl.moonmile.net のようにサブドメインを使うのが吉
2.sudo certbot certonly –manual で起動する
3.証明書に書き込むドメイン ssl.moonmile.net など を入れる
4.アクセスチェックのファイルが表示されるので、このファイルを先の外部サーバー ssl.moonmile.net に置く。
5.無事、証明書ができると /etc/letsencrypt/live に置かれる。
1 2 3 4 5 | -rw-r--r-- 1 root wheel 543 7 23 18:38 README lrwxr-xr-x 1 root wheel 36 7 23 18:38 cert.pem -> ../../archive/moonmile.net/cert1.pem lrwxr-xr-x 1 root wheel 37 7 23 18:38 chain.pem -> ../../archive/moonmile.net/chain1.pem lrwxr-xr-x 1 root wheel 41 7 23 18:38 fullchain.pem -> ../../archive/moonmile.net/fullchain1.pem lrwxr-xr-x 1 root wheel 39 7 23 18:38 privkey.pem -> ../../archive/moonmile.net/privkey1.pem |
6.シンボリックリンクされているので、本体をコピーする。
privkey.pem が秘密キー
cert.pem が証明書
7.検証機の WEB サーバー(今回は xamp)の C:/xampp/apache/conf/ に証明書を配置する
ssl.key に privkey.pem
ssl.crt に cert.pem
8.config/extra/httpd-ssl.conf に以下を追加する。ssl を有効にすることを忘れずに。
1 2 3 4 5 6 7 | < VirtualHost *:443> DocumentRoot "C:/xampp/htdocs" ServerName ssl.moonmile.net SSLEngine on SSLCertificateFile "conf/ssl.crt/cert.pem" SSLCertificateKeyFile "conf/ssl.key/privkey.pem" </ VirtualHost > |
これで HTTPS サーバーの準備が完了
9.iOSシミュレーターを動かす mac の /etc/hosts に ssl.moonmile.net を追加する。
172.16.0.12 ssl.moonmile.net
な風に追加する。「172.16.0.12」は検証サーバー機のIPアドレス。
iOSシミュレーターは、ホストの mac の /etc/hosts を共有しているようで、これで度命名「ssl.moonmile.net」でアクセスできます。
実行してみる
xcode で UIWebView を使って表示させてみると、
1 2 3 4 5 | @IBAction func clickConnet(_ sender: Any) { let url = URL( string : "https://ssl.moonmile.net/" ) let req = URLRequest(url: url!) web.loadRequest(req) } |
こんな風に、https 接続して xamp トップページが表示される。当然、「nscurl –ats-diagnostics 」もさっくりと PASS する。
これは、info.plist の変更なしでいける…のがちょっと不思議なんだけど、何も設定しないから、そのままで審査が通るってことでしょう。
おまけ
ちなみに、UIWebView 限定なのであれこれやったが、SFSafariViewController を使うと自己署名証明書でもスルーしてくれる。通常のブラウザと同じように、自己署名だからという警告が1回だけ出るが、その後はキャッシュされるらしく警告がでなくなる。
ATS対策 外部ページの表示にはSFSafariViewController – Qiita
http://qiita.com/bird_tomita/items/2139d2b95c35a4e7d20f
なんか、適当な https を表示するだけだったら、こっちのほうが手軽でよいかも、ということで。