woshidan's blog

そんなことよりコードにダイブ。

APNsを利用する際に必要な2種類の接続信頼について

APNsを利用する際に必要な2種類の接続信頼

APNsを利用する際には証明書やトークンが必要ですが、それは認証のためです。

ドキュメント*1には、暗号化されたデータが正しく復号できるか、途中で改ざんされていないかみたいな確認にあたる暗号化検証とあわせて 接続信頼 と呼ばれています。

この接続信頼には

  • プロバイダ - APNs間の接続信頼
    • APNSにリクエストを送ってきた人がプロバイダー(=プッシュ送りたい人、アプリ作った人)であることを証明する
    • これが確認できないと、プロバイダとAPNsの間でやりとりはできない
  • APNs - デバイス間の接続信頼
    • APNsから通知を受信するデバイスが、認証を受けたもののみであることを証明するためのもの

の2種類があります。

プロバイダーAPNs間の接続信頼

まず、プロバイダーAPNs間の接続信頼の話をします。

プロバイダーAPNs間の接続信頼には「トークンベースのプロバイダ接続信頼」「証明書ベースのプロバイダ接続信頼」があり、それぞれ接続信頼を確認するために必要な手順やプロバイダのサーバに配置するファイルが異なります。

トークンベースのプロバイダ接続信頼

  • WWDC2016で公表された方式。
  • こちらの記事の手順で管理画面で Apple Push Notification Authentication Key(Sandbox & Production) を発行し、その際にダウンロードする .p8 ファイルをサーバーに配置
    • Apple Push Notification Authentication Key有効期限は無期限 です(こちらの操作により無効にすることも可能)
  • こちらの記事 に書いてあるような手順で .p8 ファイルから秘密鍵を取り出し、時刻を利用して認証に使うトークンを生成します
    • 生成したトークンをリモート通知リクエストのたびにリクエストのHTTPヘッダに添えることで接続信頼の確認をします
    • このトークンの有効期限は1時間で有効期限が切れるたびに再発行する必要があります*2
  • バンドルIDが記載されているすべてのアプリケーションにおいて同じ Apple Push Notification Authentication Key を使うことが可能です
    • その代わり、リクエストごとに必ず apns-topic ヘッダでどのアプリケーションに対するリモート通知リクエストか示す必要があります
    • APNsとのやりとり関連でどのアプリケーションへのどの種類のリクエストか示すためにアプリのbundle IDなどが利用されるのですが、そのリクエスト先を指定するための項目を トピック といいます*3
  • 余談として、ドキュメントの セキュリティアーキテクチャ > プロバイダ-APNs間の接続信頼 > トークンベースのプロバイダ接続信頼鍵ペアを生成し、公開鍵をAppleに渡してください。秘密鍵はプロバイダ側で保持し と書いてあるものの、公開鍵をどうこうした覚えはなくて若干戸惑っていたり。。
    • これは推測ですが、秘密鍵がそのときDLする .p8 ファイルに入っているので、多分公開鍵は Apple Push Notification Authentication Key を発行した時にAPNs側に保管されているのでしょう。。

トークンベースの接続信頼を利用する場合に送るリクエストの例

https://developer.apple.com/jp/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW1 より

HEADERS
 - END_STREAM
 + END_HEADERS
 :method = POST
 :scheme = https
 :path = /3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0
 host = api.development.push.apple.com
 authorization = bearer eyAia2lkIjogIjhZTDNHM1JSWDciIH0.eyAiaXNzIjogIkM4Nk5WOUpYM0QiLCAiaWF0I jogIjE0NTkxNDM1ODA2NTAiIH0.MEYCIQDzqyahmH1rz1s-LFNkylXEa2lZ_aOCX4daxxTZkVEGzwIhALvkClnx5m5eAT6 Lxw7LZtEQcH6JENhJTMArwLf3sXwi
 apns-id = eabeae54-14a8-11e5-b60b-1697f925ec7b
 apns-expiration = 0
 apns-priority = 10
 apns-topic = <MyAppTopic>
DATA
 + END_STREAM
 { "aps" : { "alert" : "Hello" } }
  • ヘッダーに接続信頼用の authorization キーがあります。
  • Apple Push Notification Authentication Key は開発者アカウントと紐付いているすべてのアプリと紐付いているので、どのアプリ宛かを指定するための apns-topic の指定があります。
  • バイストークンの指定は、 :path で行われています。

証明書ベースのプロバイダ接続信頼

  • 証明書ベースのプロバイダ接続は、プロバイダ証明書(具体的には .p12 ファイルや .pem の中に含まれているデータ)で指定されている特定の1つのアプリケーションのためのものです
  • 接続信頼の確認に利用する証明書は事前に作成します
  • 証明書ベースの信頼を利用する場合、APNsが証明書失効リストを作成、管理しています*4
  • 証明書ベースでプロバイダ接続信頼を行う場合、証明書を使うのはTLS接続確立までで、個々のリモート通知リクエストの時は通知先のデバイストークンだけヘッダにつければよいです
  • トークンベースの接続信頼で使う Apple Push Notification Authentication Key と違い、証明書ベースの接続信頼で使うSSL証明書(.p12ファイル)は1年の有効期限があります

証明書ベースの接続信頼を利用する場合に送るリクエストの例

https://developer.apple.com/jp/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW1 より

HEADERS
 - END_STREAM
 + END_HEADERS
 :method = POST
 :scheme = https
 :path = /3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0
 host = api.development.push.apple.com
 apns-id = eabeae54-14a8-11e5-b60b-1697f925ec7b
 apns-expiration = 0
 apns-priority = 10
DATA
 + END_STREAM
 { "aps" : { "alert" : "Hello" } }
  • トークンベースの場合と違い、 authorization キーはありません。
  • 基本的に、証明書ベースの場合、証明書の中にどのアプリ用の証明書か含まれているため、証明書の中に記載されたトピックへのリクエストとして処理できるため、 apns-topic の指定がありません
    • 複数のtopic用の証明書の場合は apns-topic の指定が必要です。
  • バイストークンの指定は、 :path で行われています。

APNs-デバイス間の接続信頼

次にAPNs - デバイス間の接続信頼の話をします。

大枠を言うと

  • iOSなどのデバイスの利用開始設定(アクティベーション時)に接続信頼に利用される暗号化証明書と秘密鍵がOSから提供される
  • OSから提供された暗号化証明書と秘密鍵はキーチェーンに格納され、アプリケーションとは関係なしにAPNsとデバイスとの間の接続信頼に利用される
    • (= デバイスとAPNsとの接続信頼のために、開発者がなにかする必要はない)

となります。

そうすると自分(プロバイダ)として関心があることは、このAPNs - デバイスが接続された上で、デバイスへのリモート通知リクエストに必要なトークンを取得することですが、この部分については開発者側で実装や設定が必要です。

iOSおよびtvOSでのデバイストークンの取得などを参考にアプリケーションがリモート通知リクエストを送るためにAPNsへリクエストを送り、結果を受け取る実装をし、また、Enable push notificationsを参考にアプリケーションがプッシュ通知を利用する設定が必要です。

参考

*1:https://developer.apple.com/jp/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1

*2:かといって接続のたびに再発行してほしいわけではない

*3:アプリのリモート通知以外にもVoIP通知などの用途があり、その場合アプリのbundle IDに .voip がついた値をtopicに指定する https://developer.apple.com/jp/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW1

*4:これは、開発している時にはPush屋さんが失効時の挙動テストする時にAPNsのサンドボックスでの失効が遅い...(なんかSANDBOXの場合、半日くらい待ってる気がする) というくらいしか縁がないのではないか...