つばくろぐ @takamii228

知は力なり

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でもできたような気がしますがうろ覚えですのでどなたか教えてください。

「私の情報の集め方、知識の学び方」を書き直した

以前自分の情報収集のやり方について以下のブログにまとめていました。

takamii.hatenablog.com

ここではGitPitchというサービスを使ってgithubリポジトリにおいたmarkdownからスライドを生成していたのですが、このGitPitchが2021/3/1にサービスが終了するそうです。

Service on gitpitch.com is shutting down on March 1, 2021.

github.com

そこで内容を一部修正、加筆しつつスライド化してspeakerdeckに再アップロードしました。

speakerdeck.com

コロナでリモートワークがメインになって変わってたところもあるなーと修正していて感じました。

まとめていた内容はライフステージの変化や自身の置かれた状況によって変わりそうなので、定期的に見直すと良さそうだなと思いました。