つばくろぐ @takamii228

知は力なり

iOSアプリ開発を自習したい人が見るとよさそうなコンテンツをまとめてみた

以前Androidの学習コンテンツについてまとめましたが、今回はそのiOS版について少し見通しがたったのでまとめてみることにします。

takamii.hatenablog.com

macOS端末とiOS端末を用意する

iOSアプリ開発Xcodeという開発キットを利用します。このXcodemacOSでしか動作しないため、iOSアプリ開発ではmacOS端末が必須になります。WindowsLinuxではXcodeは利用できません。実際に実機端末で動作させたい場合はiOS端末も用意しましょう。

Apple Developer Programへの加入を検討する

手元のiOS端末でデバッグをするだけでなく、TestFlightを利用したり実際にAppStoreでアプリをリリースしたい場合はApple Devleloper Programに加入しましょう。個人向けライセンスであれば税込み12980円で1年単位での更新が必要です。

なお1台のみの実機デバッグだったりApp Storeで公開されているXcodeの利用のみであればApple Developer Programに加入しなくてもよいです。

Apple Developer Program - Apple Developer

メンバーシップの選択 - サポート - Apple Developer

公式のチュートリアルをやる

Androidと同様にiOSも基本的には公式の情報をキャッチアップするのがよいでしょう。

チュートリアルについては以下のリンクにSwiftUI版とUIKit版が公開されています。

https://developer.apple.com/tutorials/app-dev-training

一番最初のXcodeの操作はここを見るとよいでしょう。

https://developer.apple.com/documentation/xcode/creating_an_xcode_project_for_an_app

昨年のWWDCでSwiftUIがリリースされたときに公開されたSwiftUIのチュートリアルもあるので、SwiftUIを使う場合は余裕があれば見ておくとよいでしょう。

https://developer.apple.com/tutorials/swiftui

WWDCの動画を見る

Appleは毎年の開発者向けイベントであるWWDCの動画をアーカイブとして公開しています。新しい技術やアップデート情報はこちらからキャッチアップするとよいでしょう。

Design and Development Videos - Apple Developer

動画内で触れているサンプルコードについても公開されています。

Sample Code - WWDC20 - Apple Developer

WWDC 2020ではSwiftUIのサンプルコードとしてFrutaが公開されていました。

https://developer.apple.com/documentation/swiftui/fruta_building_a_feature-rich_app_with_swiftui

役立つ公式のリンク集

Appleの公式ドキュメントはなかなかたどり着くのが手間なのでせめて入り口だけでもブックマークしておくとよいでしょう。

iBooksで公開されている情報

Apple EducationとしてiBooksで学習用のコンテンツも公開されています。書籍なので情報は少し古めです。

books.apple.com

非公式の参考となるコンテンツ

スタンフォードの講義やUdemyの動画で学習するのもよいでしょう。mixiのコンテンツが長らく有名でしたがメンテナンスされてないようです。最近ゆめみさんがコンテツを公開したのでそちらも見てみるとよさそうです。

Xcodeのバージョンと各OSの対応表はここを見るとひと目で分かるのでよく使っています。

xcodereleases.com

mixi groupの2021年新卒研修の資料が公開(2021/5/14追記)

mixiのSwiftUIの新卒向け研修のコンテンツが公開されていました。 SwiftUIの説明からCombineやXCTest、MVVMアーキテクチャについても触れられていてよいと思いました。

medium.com

Peaksや技術書展の本

Peaksで公開されている設計本やテストの本も参考になるので、興味がある方は手元においておくとよいでしょう。紹介リンク付きのツイートを載せておきます。

技術書展でもiOS関連の書籍を執筆している人がいるので、チュートリアルが物足りない方は合わせて見てみるとよさそうです。

techbookfest.org

RSS登録すべき情報

Appleは定期的に開発者向けにニュースで周知をしているので最新情報をキャッチアップするために以下をRSS登録しておくとよいでしょう。

developer.apple.com

OSやツールのアップデート情報は以下でRSS取得ができます。

developer.apple.com

Apple全体のニュースはこちらのようです。

www.apple.com

まとめ

以上がiOS関連の技術をキャッチアップするときに見るべき情報一覧になります。

GWにiOSアプリをやっていくぞ、という方の参考になればと思います。

私はまずはチュートリアルを完走しようと思います。

APNsのルート証明書更新の影響について調査する方法を調べてみた

先月Apple Developer Newsの中でAPNs(Apple Push Notification service)のサーバー証明書更新のお知らせが来ていました。

developer.apple.com

どうやらPush通知依頼のリクエストを受けるAPNsのエンドポイントのルート証明書を2021/3/29にGeoTrust Global CAからAAACertificateServicesに変更するようです。ルート証明書が切り替わったときにPush通知依頼が失敗しないように、2021/3/29までにPush通知サーバーのTrust Storeに両方の証明書が入ってるか確認しておいてね、とのことです。

なお記事の最後にある通り、Appleが我々デベロッパー向けに発行しているAPNsの証明書についての影響はないので混乱しないように注意が必要です。

Note that Apple Push Notification service SSL provider certificates issued to you by Apple do not need be to updated at this time.

FirebaseやサードパーティのPush通知サービスを使ってる場合、今回の対応は利用しているサービスに任せればよいですが、自前でAPNsへのリクエストを組んでいる場合は念の為確認しておく必要がありそうです。

そこでAPNsへのリクエストを行うアプリケーションが動作する環境でAAACertificateServicesの証明書がTrust Storeに入っていることを確認する方法を調べたので以下にまとめました。

なおAPNsへPush通知のリクエストを投げる方法は以前まとめたので参考にしてみてください。

takamii.hatenablog.com

ルート証明書とは何か

ルート証明書https通信を行うときに用いられる証明書で、OSやブラウザにデフォルトで保存されているものです。ここでは詳細については触れないため、ルート証明書についてよくわからないという方はさくらインターネットさんのコラムを見るとよいでしょう。

ssl.sakura.ad.jp

ssl.sakura.ad.jp

ssl.sakura.ad.jp

2021/3/7時点のAPNsへ通信で使われるルート証明書

opensslコマンドを使ってsandboxと商用のAPNsへのhttps通信で使われるルート証明書を見てみましょう。

$ openssl s_client -connect api.development.push.apple.com:443 > /dev/null
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify return:1
depth=1 CN = Apple IST CA 2 - G1, OU = Certification Authority, O = Apple Inc., C = US
verify return:1
depth=0 CN = api.development.push.apple.com, OU = management:idms.group.533599, O = Apple Inc., ST = California, C = US
verify return:1

$ openssl s_client -connect api.push.apple.com:443 > /dev/null
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify return:1
depth=1 CN = Apple IST CA 2 - G1, OU = Certification Authority, O = Apple Inc., C = US
verify return:1
depth=0 CN = api.push.apple.com, OU = management:idms.group.533599, O = Apple Inc., ST = California, C = US
verify return:1

今はどちらもGeoTrust Global CAになっていることがわかります。

AAACertificateServicesの証明書の中身の確認

AAACertificateServicesのルート証明書のcrtファイルは上記のAppleのニュースの本文の中にあるリンクからダウンロードできます。

support.sectigo.com

こちらもopensslコマンドで中身を見てみましょう。

$ openssl x509 -noout -text -in ~/Desktop/AAACertificateServices.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1 (0x1)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=GB, ST=Greater Manchester, L=Salford, O=Comodo CA Limited, CN=AAA Certificate Services
        Validity
            Not Before: Jan  1 00:00:00 2004 GMT
            Not After : Dec 31 23:59:59 2028 GMT
        Subject: C=GB, ST=Greater Manchester, L=Salford, O=Comodo CA Limited, CN=AAA Certificate Services
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:be:40:9d:f4:6e:e1:ea:76:87:1c:4d:45:44:8e:
                    be:46:c8:83:06:9d:c1:2a:fe:18:1f:8e:e4:02:fa:
                    f3:ab:5d:50:8a:16:31:0b:9a:06:d0:c5:70:22:cd:
                    49:2d:54:63:cc:b6:6e:68:46:0b:53:ea:cb:4c:24:
                    c0:bc:72:4e:ea:f1:15:ae:f4:54:9a:12:0a:c3:7a:
                    b2:33:60:e2:da:89:55:f3:22:58:f3:de:dc:cf:ef:
                    83:86:a2:8c:94:4f:9f:68:f2:98:90:46:84:27:c7:
                    76:bf:e3:cc:35:2c:8b:5e:07:64:65:82:c0:48:b0:
                    a8:91:f9:61:9f:76:20:50:a8:91:c7:66:b5:eb:78:
                    62:03:56:f0:8a:1a:13:ea:31:a3:1e:a0:99:fd:38:
                    f6:f6:27:32:58:6f:07:f5:6b:b8:fb:14:2b:af:b7:
                    aa:cc:d6:63:5f:73:8c:da:05:99:a8:38:a8:cb:17:
                    78:36:51:ac:e9:9e:f4:78:3a:8d:cf:0f:d9:42:e2:
                    98:0c:ab:2f:9f:0e:01:de:ef:9f:99:49:f1:2d:df:
                    ac:74:4d:1b:98:b5:47:c5:e5:29:d1:f9:90:18:c7:
                    62:9c:be:83:c7:26:7b:3e:8a:25:c7:c0:dd:9d:e6:
                    35:68:10:20:9d:8f:d8:de:d2:c3:84:9c:0d:5e:e8:
                    2f:c9
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                A0:11:0A:23:3E:96:F1:07:EC:E2:AF:29:EF:82:A5:7F:D0:30:A4:B4
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 CRL Distribution Points: 

                Full Name:
                  URI:http://crl.comodoca.com/AAACertificateServices.crl

                Full Name:
                  URI:http://crl.comodo.net/AAACertificateServices.crl

    Signature Algorithm: sha1WithRSAEncryption
         08:56:fc:02:f0:9b:e8:ff:a4:fa:d6:7b:c6:44:80:ce:4f:c4:
         c5:f6:00:58:cc:a6:b6:bc:14:49:68:04:76:e8:e6:ee:5d:ec:
         02:0f:60:d6:8d:50:18:4f:26:4e:01:e3:e6:b0:a5:ee:bf:bc:
         74:54:41:bf:fd:fc:12:b8:c7:4f:5a:f4:89:60:05:7f:60:b7:
         05:4a:f3:f6:f1:c2:bf:c4:b9:74:86:b6:2d:7d:6b:cc:d2:f3:
         46:dd:2f:c6:e0:6a:c3:c3:34:03:2c:7d:96:dd:5a:c2:0e:a7:
         0a:99:c1:05:8b:ab:0c:2f:f3:5c:3a:cf:6c:37:55:09:87:de:
         53:40:6c:58:ef:fc:b6:ab:65:6e:04:f6:1b:dc:3c:e0:5a:15:
         c6:9e:d9:f1:59:48:30:21:65:03:6c:ec:e9:21:73:ec:9b:03:
         a1:e0:37:ad:a0:15:18:8f:fa:ba:02:ce:a7:2c:a9:10:13:2c:
         d4:e5:08:26:ab:22:97:60:f8:90:5e:74:d4:a2:9a:53:bd:f2:
         a9:68:e0:a2:6e:c2:d7:6c:b1:a3:0f:9e:bf:eb:68:e7:56:f2:
         ae:f2:e3:2b:38:3a:09:81:b5:6b:85:d7:be:2d:ed:3f:1a:b7:
         b2:63:e2:f5:62:2c:82:d4:6a:00:41:50:f1:39:83:9f:95:e9:
         36:96:98:6e

2004年に発行されたもののようです。確認用にfingerprintを確認しておきましょう。

$ openssl x509 -sha256 -fingerprint -noout -in ~/Desktop/AAACertificateServices.crt 
SHA256 Fingerprint=D7:A7:A0:FB:5D:7E:27:31:D7:71:E9:48:4E:BC:DE:F7:1D:5F:0C:3E:0A:29:48:78:2B:C8:3E:E0:EA:69:9E:F4

次にAPNsへのPush通知のリクエストを投げる発射台に上記の証明書が含まれることを確認していきます。今回はJavaPHP、Nodeについて調べてみました。

Javaの場合

Javaアプリケーションの場合はJDK / JREのKeystore情報を確認すればよいです。keytoolコマンドつかってkeystoreの中身を確認します。

$ java -version
openjdk version "11.0.10" 2021-01-19
OpenJDK Runtime Environment (build 11.0.10+9)
OpenJDK 64-Bit Server VM (build 11.0.10+9, mixed mode)

$ keytool -list -storepass changeit -keystore $JAVA_HOME/lib/security/cacerts | \
  grep "55:92:60:84:EC:96:3A:64:B9:6E:2A:BE:01:CE:0B:A8:6A:64:FB:FE:BC:C7:AA:B5:AF:C1:55:B3:7F:D7:60:66"
Warning: use -cacerts option to access cacerts keystore
Certificate fingerprint (SHA-256): 55:92:60:84:EC:96:3A:64:B9:6E:2A:BE:01:CE:0B:A8:6A:64:FB:FE:BC:C7:AA:B5:AF:C1:55:B3:7F:D7:60:66

AAACertificateServicesのfingerprintがヒットしました。lessで詳しく見てみると、comodoaaacaというAliase nameで登録されていました。

...
Alias name: comodoaaaca [jdk]
Creation date: Jan 1, 2004
Entry type: trustedCertEntry

Owner: CN=AAA Certificate Services, O=Comodo CA Limited, L=Salford, ST=Greater Manchester, C=GB
Issuer: CN=AAA Certificate Services, O=Comodo CA Limited, L=Salford, ST=Greater Manchester, C=GB
Serial number: 1
Valid from: Thu Jan 01 09:00:00 JST 2004 until: Mon Jan 01 08:59:59 JST 2029
Certificate fingerprints:
         SHA1: D1:EB:23:A4:6D:17:D6:8F:D9:25:64:C2:F1:F1:60:17:64:D8:E3:49
         SHA256: D7:A7:A0:FB:5D:7E:27:31:D7:71:E9:48:4E:BC:DE:F7:1D:5F:0C:3E:0A:29:48:78:2B:C8:3E:E0:EA:69:9E:F4
...

OpenJDK11の場合は特に何もしなくてもよさそうですね。

PHPの場合

PHPアプリケーションの場合は以下の関数を実行することでPHPが参照しているルート証明書リストの情報が確認できます。

www.php.net

確認方法だけなのでここではmacOS上で実行してしまいます。

ちなみにTerminalでPHPを実行したら警告がでました。将来のmacOSにはPHPはバンドルされないんですね...。

$ php -v
WARNING: PHP is not recommended
PHP is included in macOS for compatibility with legacy software.
Future versions of macOS will not include PHP.
PHP 7.3.24-(to be removed in future macOS) (cli) (built: Dec 21 2020 21:33:25) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.24, Copyright (c) 1998-2018 Zend Technologies

$ php -a
Interactive shell

php > var_dump(openssl_get_cert_locations());
array(8) {
  ["default_cert_file"]=>
  string(25) "/private/etc/ssl/cert.pem"
  ["default_cert_file_env"]=>
  string(13) "SSL_CERT_FILE"
  ["default_cert_dir"]=>
  string(22) "/private/etc/ssl/certs"
  ["default_cert_dir_env"]=>
  string(12) "SSL_CERT_DIR"
  ["default_private_dir"]=>
  string(24) "/private/etc/ssl/private"
  ["default_default_cert_area"]=>
  string(16) "/private/etc/ssl"
  ["ini_cafile"]=>
  string(0) ""
  ["ini_capath"]=>
  string(0) ""
}
php > quit

/private/etc/ssl/cert.pem にある証明書リストを参照するようです。中身を見てみます。

$ awk -v cmd='openssl x509 -noout -sha256 -fingerprint' '
    /BEGIN/{close(cmd)};{print | cmd}' < /private/etc/ssl/cert.pem | grep "D7:A7:A0:FB:5D:7E:27:31:D7:71:E9:48:4E:BC:DE:F7:1D:5F:0C:3E:0A:29:48:78:2B:C8:3E:E0:EA:69:9E:F4"
unable to load certificate
4442443436:error:09FFF06C:PEM routines:CRYPTO_internal:no start line:/AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-56.60.2/libressl-2.8/crypto/pem/pem_lib.c:684:Expecting: TRUSTED CERTIFICATE
SHA256 Fingerprint=D7:A7:A0:FB:5D:7E:27:31:D7:71:E9:48:4E:BC:DE:F7:1D:5F:0C:3E:0A:29:48:78:2B:C8:3E:E0:EA:69:9E:F4

AAACertificateServicesのfingerprintがヒットしました。lessで中身を見ても確認できました。

$ awk -v cmd='openssl x509 -noout -text ' '
    /BEGIN/{close(cmd)};{print | cmd}' < /private/etc/ssl/cert.pem | less
...
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1 (0x1)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=GB, ST=Greater Manchester, L=Salford, O=Comodo CA Limited, CN=AAA Certificate Services
        Validity
            Not Before: Jan  1 00:00:00 2004 GMT
            Not After : Dec 31 23:59:59 2028 GMT
        Subject: C=GB, ST=Greater Manchester, L=Salford, O=Comodo CA Limited, CN=AAA Certificate Services]
...

Node.jsの場合

Node.jsの場合はtslモジュールが参照しているroot証明書を確認すればよいです。

ドキュメントを読むと、If the ca option is not given, then Node.js will default to using Mozilla's publicly trusted list of CAs. とあるのでMozilaの証明書リストを使うようになっているようです。

nodejs.org

https://hg.mozilla.org/mozilla-central/raw-file/tip/security/nss/lib/ckfw/builtins/certdata.txt

以下のコードを実際に実行して参照しているルート証明書のリストが確認できます。

const tls = require('tls');
const net = require('net');

var list = tls.rootCertificates;

for(key in list){
  const secureContext = tls.createSecureContext({
    cert: list[key]
  });
  const secureSocket = new tls.TLSSocket(new net.Socket(), { secureContext });
  const cert = secureSocket.getCertificate();
  console.log(cert);
}
$ node -v           
v12.20.1

$ node rootCAList.js | grep "D7:A7:A0:FB:5D:7E:27:31:D7:71:E9:48:4E:BC:DE:F7:1D:5F:0C:3E:0A:29:48:78:2B:C8:3E:E0:EA:69:9E:F4"
  fingerprint256: 'D7:A7:A0:FB:5D:7E:27:31:D7:71:E9:48:4E:BC:DE:F7:1D:5F:0C:3E:0A:29:48:78:2B:C8:3E:E0:EA:69:9E:F4',

AAACertificateServicesのfingerprintがヒットしました。lessで詳細を見ても合致してますね。

$ node rootCAList.js | less
...
{
  subject: [Object: null prototype] {
    C: 'GB',
    ST: 'Greater Manchester',
    L: 'Salford',
    O: 'Comodo CA Limited',
    CN: 'AAA Certificate Services'
  },
  issuer: [Object: null prototype] {
    C: 'GB',
    ST: 'Greater Manchester',
    L: 'Salford',
    O: 'Comodo CA Limited',
    CN: 'AAA Certificate Services'
  },
...

3/29までに何をすればよい?

Appleの通知の本文にある通り、APNsへリクエストを投げるアプリケーションがAAACertificateServicesのルート証明書を信頼するようになっていればよいです。ルート証明書を信頼するかどうかは上記の証明書リストにAAACertificateServicesのルート証明書が登録されていればよいです。

より厳密にやるのであれば、Push通知のリクエストをAAACertificateServices経由で行えばよいのですがこれは3/29を待たなければできません。sandboxだけでも先に変わってくれるとよいのですが、おそらく一括で変更が入るのでしょう。

どうしても心配な方は、AAACertificateServicesの証明書を経由するhttpsリクエストが正しく実行できることを確認してみるといいかなと思いました。 例えばsupport.sectigo.comへのリクエストはAAACertificateServicesのルート証明書を経由するようです。

$ openssl s_client -connect support.sectigo.com:443 > /dev/null   
depth=3 C = GB, ST = Greater Manchester, L = Salford, O = Comodo CA Limited, CN = AAA Certificate Services
verify return:1
depth=2 C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = Sectigo Limited, CN = Sectigo RSA Extended Validation Secure Server CA
verify return:1
depth=0 serialNumber = 04058690, jurisdictionCountryName = GB, businessCategory = Private Organization, C = GB, postalCode = M5 3EQ, ST = Manchester, L = Salford, street = Trafford Road, street = Office Village Exchange Quay, street = 3rd Floor Building 26, O = Sectigo Limited, OU = IT, CN = support.sectigo.com
verify return:1

まとめ

APNsのルート証明書変更に起因して、JavaPHP・Node.jsのアプリケーションが参照しているルート証明書情報を確認する手順をまとめました。検証を通してhttps通信の仕組みについて改めて理解を深めることができました。証明書の更新に関しては影響がある方は3/29以降問題が発生しないように事前に確認しておくとよいでしょう。

2021/4/2追記

切り替えあとに証明書チェインの情報を確認してみたら無事AAA Certificate Servicesに変わってました。

$ openssl s_client -connect api.development.push.apple.com:443 > /dev/null
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = Comodo CA Limited, CN = AAA Certificate Services
verify return:1
depth=1 CN = Apple Public Server RSA CA 12 - G1, O = Apple Inc., ST = California, C = US
verify return:1
depth=0 CN = api.development.push.apple.com, OU = management:idms.group.533599, O = Apple Inc., ST = California, C = US
verify return:1

$ openssl s_client -connect api.push.apple.com:443 > /dev/null
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = Comodo CA Limited, CN = AAA Certificate Services
verify return:1
depth=1 CN = Apple Public Server RSA CA 12 - G1, O = Apple Inc., ST = California, C = US
verify return:1
depth=0 CN = api.push.apple.com, OU = management:idms.group.887777, O = Apple Inc., ST = California, C = US
verify return:1

Jenkinsのビルドエージェントとしてmac miniを設定する手順をまとめた

ここ最近はGitLab CIにどっぷりだったのだけど、久しぶりにJenkinsでiOSのCI/CDを組む必要が出てきたのでmac miniをビルドエージェントに登録する手順をQIita記事にまとめました。

qiita.com

基本的な流れはGitLab CIと同じですね。JNLPという仕組みを使ってるところが大きな違いでしょうか。 GitLabと比較してJNLPを使う分ポートを一つ多く開けないと行けない点がポイントですね。

またGitLab CIの場合はgitlab-runner register / installコマンドでlaunchd登録してくれて再起動時に自動起動してくれるようですがJenkinsの場合はそのような仕組みはないので自前で準備しました。launchdのplistファイルの書き方も勉強になりました。

リポジトリとジョブ設定の連携でいうとGitLab CIの場合はGitLabと密になっているのでトリガー設定はshedule実行以外はほぼ不要です。その一方でJenkinsはリポジトリ連携を細かくやらないと行けない点が面倒ですね。

Jenkinsの唯一いい点でいうとParameterized Buildでオンデマンドのジョブ定義がやりやすい点でしょうか。もしかしたら最近のGitLab CIでもできたような気がしますがうろ覚えですのでどなたか教えてください。