はじめに
2008年7月11日にiPhone 3Gが日本で発売されてから、18年が経ちました。時が流れるのは早いですね。
以下の写真は当日の朝の「めざましテレビ」の報道ですが、孫社長と握手しているのは、ソフトバンク表参道店の行列に並んでいた私です。
前日の22時くらいに並び始め、手続きが終わったのは13時頃だったので、実に15時間くらいかけて iPhone 3Gを手に入れました。ものすごく暑い日で熱中症になりかけた記憶があります。
当時は携帯電話用にソフトを自由に書くことができない時代でしたが、iPhoneの登場により「モバイルアプリを作る」という敷居も一気に下がり、今では個人でも当たり前のように開発できるようになりました。
そこからかなりの年月が経ちましたが、 Appleのプロビジョニングプロファイルやら証明書やらは関係が入り組んでいてわかりづらく、何がどう関係しているいのかわからなくなって、いまだに混乱します。
前からこの辺りはちゃんと理解して記事にしておきたいと思っていたので、自分の理解の整理も兼ねて、あらためて書いてみることにしました。
安全なコンピュータ = 不自由なコンピュータ
パソコン上で Hello Worldのソースコードをコンパイルして実行ファイルを作って実行するというのは、ごく当たり前に行われます。
> gcc helloworld.c
> ./a.out
Hello World!
もちろんコンパイルしなくても、どこからか実行ファイルを持ってくれば実行できてしまいます。これは便利ですが、危険でもあります。もしこのプログラムが「自分のホームディレクトリのファイルを全て消す」というプログラムだった場合、 あっという間に大事なファイルが消えてしまいます。
こういった 悪意のあるプログラム を実行させないためには、信頼できる人からお墨付きが与えられたプログラムだけが実行できるようになっている方が安全です。
iOSの場合は、
- 信頼できる人 = アップル
- お墨付きが与えられたプログラム = AppStoreに掲載されているアプリ
ということになります。
でも、「このプログラム(アプリ)を起動して良いかどうか」自体もプログラム(OSのプログラム)が判断しているので、そこを書き換えられてしまったらこの仕組みはうまく機能しません。となるとOS自体も信用できる必要があり、信用できないOSは起動できないようにする必要があります。じゃあそのOSを検証するのは誰かというと、これはファームウェア(BIOS、UEFIなど)の仕事になります。
つまり、安全なコンピュータというのは以下のような仕組みで動いています。
- 電源を入れるとファームウェアが起動する
- このファームウェアはメーカーの出荷時に書き込まれていて、簡単には書き換えられないようになっている(=信用できる)
- ファームウェアはストレージからブートローダーをロードし、信用できる人からのお墨付きが与えられていると確認できたら起動する
- ブートローダーは、ストレージからOSのカーネルをロードし、信用できる人からのお墨付きが与えられていると確認できたら起動する
- OSのカーネルは、機能拡張やプログラムをロードし、信用できる人からのお墨付きが与えられていると確認できたら起動する
このような信頼の連鎖を「Chain of Trust(信用の連鎖)」と呼び、Chain of Trustを使って安全に起動する仕組みは「Secure Boot」と呼ばれます。
この仕組みでガチガチに固められているiOSなどの場合、自分のプログラムを好きに動かすことはできないので、 エンジニア的には「不自由なコンピュータ」となります。 ですが、スマートフォンのように世界中の、技術に明るくないような人にも使われるコンピュータは、「なんでも動かせる」よりも「安全である」方が重要なため、このような仕組みになっています。
つまり、コンピュータの安全性と自由度はトレードオフの関係にあるのです。
余談1
iPhone 3Gが出たばかりの 2009年 2010年頃は、Androidは非常に自由なコンピュータでした。野良アプリも簡単にインストールでき、カーネルの機能、ハードウェアをかなり自由に触ることができたので、「iOSなんて不自由なOSはクソだ!おれはAndroidを使う!」というエンジニアも多くいました。ですが、Appleは 悪意のあるプログラムに汚染された Windowsの二の舞にはしたくないと考え、このような安全な仕組みを導入したのでしょう。実際、Andoroidはスパイアプリ、ストーカーアプリのようなものが大量に作られ、社会問題にもなりました。結局、Androidも OSのバージョンが上がるたびにだんだんと制約が厳しくなり、今ではほとんどiOSと同じくらいの厳しさ(=不自由さ)になっています。
結果的にはAppleの判断は正しかったということでしょう。
※もちろん、AppStoreを通じてしかアプリを配れないようにすることでプラットフォーマーとしての地位を確立し、そこから収益を産みたいというビジネス上の戦略もあったと思います。
Appleが iOSにかけている制約事項
前置きが長くなりましたが、iOSアプリのプロビジョニングプロファイル、証明書、署名などをちゃんと理解するためには、この Secure Bootの仕組みを抑え、 Appleが iOSで何を禁止しようとしているのか を理解するのが近道です。
具体的には、Appleは以下のようなことをしようとしています。
- ①アップルが作ったアプリだけが起動できるようにしたい
- ②ただし、アップルがお墨付きを与えた「サードパーティの開発者」であれば、試作品のアプリを限定的に起動することは許したい
iPhone上で動くソフトは Appleしか作れないようにしてしまえば①だけでいいのですが、AppStoreを解放し、世界中のデベロッパーにアプリを作ってもらうためには、②の例外が必要になります。ですが、②の例外が緩すぎると、AppStoreを経由せずにアプリを配って商売することができてしまうため、以下のような制約をかけています。
- サードパーティの開発者が作ったアプリは、事前に登録した端末(iPhone, iPadなど)でしか起動できない
- 登録できる端末台数にも上限がある(通常は100台)
- 開発者が作ったアプリは、ある期限内でしか起動できず、その日を過ぎると起動できなくなる
これを実現するために何をしているのかを解説していきます。
余談2
Nintendo Switchなどのゲーム機も、正規のゲーム以外は起動できないようにするために似たような仕組みを持っています。ですが、Switchの場合はソフトを開発するためには開発専用のハードが必要で、広く流通している一般のSwitchを使っての開発はできないようになっています。つまり上の①の部分だけができるようになっていて、②はできないということですね。
Appleもこれと同じように「開発専用のiPhoneを買わないと開発できないようにする」という選択肢もあったはずですが、もしそうなっていたらこんなにアプリ開発は広まっていなかったはずです。Androidという対抗馬の存在もありましたし、②の仕組みによって専用機材なしで開発できるようにし、アプリ開発の門戸を広げたいという考えがあったのだろうと想像します。
プロビジョニングプロファイルとは?
前述のとおり「①アップルが作ったアプリだけが起動できるようにしたい」だけであれば、インストールされたアプリにAppleのデジタル署名を施し「信用できる」か「信用できない」かの判断だけができれば良いです。
ですが、「②ただし、アップルがお墨付きを与えた「サードパーティの開発者」であれば、試作品のアプリを限定的に起動することは許したい」を実現するために、「UDIDが1111の端末では起動して良いが、2222の端末では起動してはいけない」などの細かな制御をしようと思うと、単なる署名の検証だけではうまくいきません。
そこで登場するのが 「プロビジョニングプロファイル」 です。
正直私が最初にこの単語に出会った時の感想は 「何をするものなのか全くわからん」 というものでした。「プロファイル」はSNSの「プロフィール」というカタカナ語で使われたりしますが、「プロビジョニング」の方は日本人的にはあまり馴染みのない単語です。
とりあえず直訳すると、
- provisioning = 資源の割り当て、許可
- profile = 定義書
で「資源許可定義書」とかになりますが、少し意訳して「アプリ実行許可定義書」という感じのほうがいいでしょうか。
つまり、プロビジョニングプロファイルとは、"どのID(BundleID)のアプリが、どの端末、環境で実行可能なのかを書いた定義書"ということです。これは非常に重要な書類なので、改ざんされないように電子署名が施され、改ざんができないように保護されています。
ただ、これだけ聞いてもよくわからないと思いますので、もう少し掘り下げてみたいと思います。
プロビジョニングプロファイルは Appleのサイトでしか作れない
プロビジョニングプロファイルは Appleの Developerサイトの Profiles というページ で管理されており、ここで作ったり消したり編集したりすることが可能です。
+ボタンを押すと、以下のようにいろいろなプロビジョニングプロファイルが作れます。
非常に重要なポイントとして、プロビジョニングプロファイルはAppleのWebサイト上でしか作ることができないという制約があります。プロビジョニングプロファイルは単なるXMLファイルなのですが、Appleの電子署名が施されている必要があり、Apple以外の第三者が作れない(勝手に作ったり書き替えると検証エラーになる)ようになっています。(ファイル構造の詳細は後述します)
プロビジョニングプロファイルの種類とその違い
前項の通りプロビジョニングプロファイルの+ボタンを押すといろいろな選択肢が出てきますが、よく見ると「iOS用」「macOS用」などが分かれているので多く見えるだけで、実際のiOSアプリ開発で使用するのは以下の3種類になります。
- iOS App Development
- アプリを実機でデバッグ実行するためのプロファイル
- Ad Hoc Distribution
- アプリを開発チームの関係者に配布するためのプロファイル
- App Store Distribution
- アップルにアプリを提出するためのプロファイル
- App Store、Test Flightでの配信はこちら
- アップルにアプリを提出するためのプロファイル
特徴の違いを表にすると以下のようになります。
| 項目 | iOS App Development | Ad Hoc Distribution | App Store Distribution |
|---|---|---|---|
| 主目的 | 実機デバッグ | 限定配布 | 一般公開 (Appleへの提出) |
| 実機インストール | ⭕ | ⭕ | ❌(提出のみ) |
| UDID 制限 | ⭕(登録端末のみ) | ⭕(登録端末のみ) | ❌ |
| 有効期限 | 1年未満 | 1年未満 | 1年未満(提出後はApple再署名) |
| デバッガ接続 (get-task-allow) | ⭕ | ❌ | ❌ |
| コード署名者(証明書、鍵) | 開発者個人のDevelopment証明書(鍵) | チームのDistribution証明書(鍵) | チームのDistribution証明書(鍵)で署名してAppleに提出後、Appleが再署名 |
いきなり表を見ても訳がわからないと思うので、順番に解説していきます。
なお、AppStoreから正式に配布されているアプリ(TestFlight含む)は上記プロビジョニングプロファイルとは異なる仕組みで起動許可/不許可が管理されており、私たち一般開発者の手が届かないところにあります。そのため、上記表には含めていません。私たちが関与できるのは AppStore Connectへの提出のところまで(上記表のApp Store Distributionまで)です。
どの端末(UDID)なら起動できるのか?
プロビジョニングプロファイルには「起動して良い端末の一覧」が書かれています。インストールしようとした端末のUDIDが1111であれば、その1111というIDがプロファイル内の端末一覧に書かれていなければ弾かれてしまいます。
これができるのはDevelopmentとAd Hocだけで、App Store Distributionではできません(「端末一覧」がない)。そのため、App Store Distributionの署名がされたアプリを直接インストールすることはできません。
いつまで起動できるのか?
一度作ったアプリがいつまでも起動できてしまうと、何か問題があった時に回収するのが難しくなります。Appleとしても、AppStoreを迂回する穴になりかねませんから、有効期限があります。
AppleのDeveloperサイトでプロビジョニングプロファイルの画面を開くと、以下のように Expires というところに有効期限が書かれています。これを過ぎるとアプリが起動できないように制限されています。
デバッガ接続を許すか
iOS端末上で動いているアプリをデバッガで接続すると、アプリが扱える範囲のメモリやストレージが全て丸見えになります。これは開発時には非常に強力な仕組みである一方、セキュリティ的には非常に危険な穴になります。
デバッガ接続をしても良いかどうかは、プロビジョニングプロファイの中の Entitements という項目に get-task-allow = true という形で書かれています。 先ほどプロビジョニングプロファイルは Appleしか作ることができないというご説明をしましたが、get-task-allow = true はDevelopmentプロファイルにしか入れてくれないので、Ad Hoc署名では開発ができません。
だれがコード署名を施すのか?
ソースコードをコンパイルして作成した実行ファイル(実行バイナリ)には、コード署名を施し、改ざんを防止します。これがないといくらプロビジョニングプロファイルで制限をかけても、アプリの中身を悪意のある第三者に書き換えられてしまっては意味がないからです。
コード署名は秘密鍵によって行うのですが、その候補となる秘密鍵は以下の3つになります。
- 開発者個人の秘密鍵
- チームの秘密鍵
- Appleの秘密鍵
署名用の秘密鍵というのは非常に取り扱いに注意すべきものです。SSL/TLSサーバー証明書などでもおなじみですが、万が一第三者に秘密鍵が漏れてしまうと証明書が無意味になります。あとから無効化することもできなくはないですが、回収するのはとても大変です。
なので、秘密鍵の流通範囲を限定する必要があります。当然ながら Appleの秘密鍵が最も重要な(貴重な)鍵で、おそらく非常に厳重に管理されていることでしょう(アップルのビジネスの根幹でもありますし)。もちろんプロビジョニングプロファイルの署名のようにAppleのサーバー上で署名してもらうようにすれば、秘密鍵自体を持っていなくても署名を施すことは可能です。ですが、アプリをビルドするたびにAppleに署名してもらうのは効率が悪いですし、常にオンラインでないといけないなど、取り回しが悪いです。
そこで、アプリをローカルのMac上でビルドする際は、開発者個人(のApple ID)に紐づいた秘密鍵を使用します。普段アプリ開発をしている方であれば、Mac上でキーチェーンを開き、「自分の証明書」というところを開くと以下のような、Apple Development という証明書が入っているのがわかります。
この「自分の証明書」というのは、"秘密鍵を持っている証明書"という意味です。
- 証明書
- 第三者の署名が施された 公開鍵 のこと
- 単体の証明書(.cerファイルなど)に含まれているのは公開鍵なので、バンバン配ってOK
- 自分の証明書
- 証明書に含まれる公開鍵と対になる秘密鍵も持っている証明書のこと
- 秘密鍵があるので、電子署名をすることが可能(署名された対象はこの証明書で照明できる)
- 「証明書と秘密鍵」のセット(Keychain上で”自分の証明書"になっているもの)は PCKS#12というファイル形式(.p12)でエクスポートして外部に持ち出すこともできます(取扱注意!)
この辺りをもう少し詳しく知りたい場合は、私の書いた以下の記事もご覧ください。
さて、この自分の Development証明書(+鍵)は Xcodeが自動的に作ってくれるので、自分で作ったという意識はあまりないかもしれません。開発に使っている Macのローカルキーチェーンに保存されているので、Macを買い替えたりOSを入れなおしたりした場合、新たに作り直すことになります。
もちろん .p12ファイルとしてエクスポートして新しいMacに持っていくこともできますが、Development証明書(+鍵)は作成上限なども特にないので、気にせず複数作ってしまっても問題はありません。
以下のように、Appleの Certificate を見ると同じ人の証明書がたくさん登録されてしまうのはちょっとみっともないのですが……。まあ気にしないことにしています。
むしろ、秘密鍵は不用意に持ち出さない方が安全なので、都度作り直してしまう方がいいでしょう。
少し前置きが長くなりましたが、 普段の「ビルド→実行」の際にはこの 「自分のDevelopment証明書(+鍵)」を使用します(プロビジョニングプロファイルはDevelopmentを使用)。 ライフサイクルが短いので、鍵を失っても実害は少ないです。ただ、この秘密鍵を第三者に渡してしまうと、あなたのフリをしてアプリを作れてしまうので、基本的にはあなたの Macの外には出さないようにしてください。
Developmentプロファイルの中には、「どの開発者の署名を信じて良いか」という開発者の一覧も含まれています。Developmentプロファイルを作成/編集する際に、以下のようにCertificatesの一覧にチエックを入れていると思います。
これがこのプロファイルが認めた開発者の一覧です。ここに存在しない開発者の証明書(+鍵)で署名しても、アプリは起動できないということになります。
さて、このDevelopment署名を施したアプリを他のチームメンバーに配布することもできますが、先の通り、デバッグ実行が可能なプロファイルなので、あまり安全ではありません。そのため、 メンバーへの配布時は、「チームの Distribution証明書(+鍵)」を使用してください。その場合に使用するプロビジョニングプロファイルはAd Hoc Distributionプロファイルです。
Ad Hoc Distributionプロファイルの画面には、デバイスを選ぶリストはありますが、開発者を選ぶリストはありません。これは、署名に使用する鍵が開発者個人のものではなく、チームの鍵を使用するためです。
iOSによる検証手順
ちょっと複雑になってきたので、少し視点を変えてiOSの気持ちになってみましょう。iOSがあるアプリのインストールや起動を許可する際に、何をみて判断しているのかを紐解いていきます。
- Step1 プロビジョニングプロファイル自体の検証
- プロビジョニングプロファイル自体が改竄されていないことを検証します
- プロビジョニングプロファイルはAppleの秘密鍵で署名されているので、OSにインストールされたルート証明書を辿っていき、本物のAppleが署名しているかを検証します(詳しくは後述)
- この検証はオフラインでも可能です
- プロビジョニングプロファイル自体が改竄されていないことを検証します
- Step2 許可された端末 (UDID) リストに含まれているか
- プロビジョニングプロファイルに含まれるデバイスのリストに、この端末のUDIDが含まれているか検証します
- 含まれていない場合はインストール自体も拒否されます
- Step3 プロビジョニングプロファイルの有効期限の確認
- 有効期限をすぎていたら起動させません
- Step4 コード署名の検証
- アプリの起動前に、コードが改変されていないかを検証します
- 開発者個人による署名(Development)の場合
- 署名の検証のために、Development証明書(=公開鍵)を使います
- Developmentプロビジョニングプロファイルの中には、許可された開発者とそのDevelopment証明書(秘密鍵は入っていない)がずらずらとリストとして入っています
- この証明書(公開鍵)が正当なものかどうかは第三者(Apple)によって証明されます
- 証明書には有効期限もかかれているので、期限内かどうかも検証されます
- 証明書の検証はオフラインで可能で、秘密鍵も不要です
- 正しい証明書でバイナリのコード署名が施されているかを確認し、問題なければ起動します
- チームによる署名(Ad Hoc Distribution)の場合
- 署名の検証のために、Ad Hoc Distribution証明書(=公開鍵)を使います
- Ad Hocプロビジョニングプロファイルの中にAd Hoc Distribution証明書が入っています
- 証明書には有効期限もかかれているので、期限内かどうかも検証されます
- 証明書の検証はオフラインで可能で、秘密鍵も不要です
- 正しい証明書でバイナリのコード署名が施されているかを確認し、問題なければ起動します
このような検証により、Appleが認めた限定的な範囲でだけアプリが配れるようになっています。
Development / Ad Hocの使い分け
あるアプリの Developmentプロファイルを作る際に、以下のような使い方をしているチームは割と多いと思います。
- Certificatesの項目で「Select All」をチェックし、全開発メンバーをチェック
- Devicesの項目で「Select All」をチェックし、全デバイスをチェック
私も面倒なのでこうしてしまうことが多いのですが、本来のAppleの設計からすると、例えば以下のような使い方を想定してしているのだろうと思います。
- この企業Z(Apple的にいうところの"チーム")にはアプリAの開発グループ、アプリBの開発グループ……というように、アプリごとに分かれたグループがある(一部のメンバーは複数のグループを兼任することもある)
- アプリAのDevelopmentプロファイルには、アプリAの開発メンバーのみを含める
- Devicesには、アプリAのチームに割り当てた端末だけを入れる
- →さらに厳しくする場合は、開発者ごとにプロファイルを分け、各開発者に割り当てた端末だけを許可する
- Devicesには、アプリAのチームに割り当てた端末だけを入れる
- アプリBのDevelopmentプロファイルには、アプリBの開発メンバーのみを含める
- Devicesには、アプリBのチームに割り当てた端末だけを入れる
- →さらに厳しくする場合は、開発者ごとにプロファイルを分け、各開発者に割り当てた端末だけを許可する
- Devicesには、アプリBのチームに割り当てた端末だけを入れる
このようにすることで、以下が実現できます。
- アプリAのチームに社外のメンバーSさんを助っ人として入れる場合
- Sさんは自分のDevelopment証明書(+鍵)だけを持っているので、ビルド時に自分の鍵で署名して実機デバッグが可能
- SさんはアプリBの署名をすることはできないので、事故や悪戯を防止できる
- Sさんには、企業Z(チームZ)の Distribution証明書(+鍵)を渡す必要がない
- → Ad Hocインストール用のビルドは CIサーバーなど署名する(CIサーバーには社員が Distribution証明書(+鍵)をセットしておく)ので、関係者の配布はそれを使って行う
- → Appleへの提出用のビルドをこっそり作る(差し替える)こともできない
- Sさん専用のプロビジョニングプロファイルを作り、デバイス一覧にはSさんに割り当てた端末だけを有効にすれば、SさんがこっそりアプリAに悪意を持ったコードを仕込んで関係者に配ることも防止できる
実際にここまで厳密な運用をしているチームは少ないかもしれませんが、本気で管理するならこのような制御もできるようになっています。
余談3
2016年のXcode 8あたりで導入された Automatically Managed Signingを使うと、証明書やプロビジョニングプロファイルの管理の面倒な部分がかなり自動化されるので、今ではみんなこれを使っていると思います。後述しますが、Automatically Managed Signingを使う場合は Team Provisioning Profileという、「チーム全員が入ったプロファイル」を使うようになるので、基本的には開発チームメンバーであれば誰でも開発できるようになります。
もしチームごとに入れるメンバーを厳密に管理したい場合は、ユーザー権限で証明書周りの権限をOFFにすればできそうではありますが、実際にやったことはありません。
いずれにせよ、Team Provisioning Profileという概念を持ち出したあたりで、Appleとしても開発メンバーごとに細かく権限を分けるというのはそこまで重要視しなくなったのではと思います。(そうじゃ無いよという例があれば教えてください)
プロビジョニングプロファイルの実体
プロビジョニングプロファイルがどのように作用しているのかを理解できたとおもいますので、ここからは実際にプロビジョニングプロファイルの実体を見ていき、どのようなファイル構造をしているのかを見ていこうと思います。
まず、iOSアプリの実体である .ipa ファイルをバラすと、以下のような構造をしています。( .ipaファイルはZIPファイルなので、zipコマンドで展開できます)
MyApp.ipa
└─ Payload/
└─ MyApp.app/
├─ MyApp ← 実行ファイル(署名済)
├─ Info.plist
├─ embedded.mobileprovision ← ★これ
└─ _CodeSignature
プログラム(実行ファイル)の本体は MyAppというものですが、それとは別に入っている embedded.mobileprovision というファイルがプロビジョニングプロファイルの実体です。このファイルはバイナリファイルですが、テキストエディタで無理やり開くと XMLテキストが入っているのがわかります。
0��x *�H��
���h0��c10 +�0�� *�H��
���r��m<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppIDName</key>
<string>My App</string>
<key>ApplicationIdentifierPrefix</key>
<array>
このバイナリファイルは実は PKCS (Public-Key Cryptography Standards: RSAセキュリティにより考案され公開された公開鍵暗号標準) のファイルフォーマットの一つである PKCS#7というファイル形式になっています。
PKCS#7 は署名付きのドキュメントを格納する形式で、ドキュメント(XML)部分だけを抽出したい場合や、逆に証明書部分だけをみたい場合は、以下のコマンドで行えます。
# ドキュメント部分を見たい場合
> security cms -D -i embedded.mobileprovision | less
# 証明書部分を見たい場合
> openssl pkcs7 -inform der -print_certs -in embedded.mobileprovision -text | less
DevelopmetプロビジョニングプロファイルのXML構造
上記コマンドで Developmetプロビジョニングプロファイルの中身を抽出してみると以下のようになっています(チームIDやアプリのBundle IDなどは仮のものに差し替えてあります)。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppIDName</key>
<string>HogeHoge App</string>
<key>ApplicationIdentifierPrefix</key>
<array>
<string>123ABCDEF</string>
</array>
<key>CreationDate</key>
<date>2026-01-14T13:33:13Z</date>
<key>Platform</key>
<array>
<string>iOS</string>
</array>
<key>IsXcodeManaged</key>
<true />
<key>DeveloperCertificates</key>
<array>
<data>MIIFvTCCBKWgAwIB.....</data>
<data>MIIFvTCCBKWgAwIB.....</data>
</array>
<key>DER-Encoded-Profile</key>
<data>MIIuOwYJKoZIhvcNA.....</data>
<key>Entitlements</key>
<dict>
<key>application-identifier</key>
<string>123ABCDEF.com.example.hogehogeapp</string>
<key>keychain-access-groups</key>
<array>
<string>123ABCDEF.*</string>
<string>com.apple.token</string>
</array>
<key>get-task-allow</key>
<true />
<key>com.apple.developer.team-identifier</key>
<string>123ABCDEF</string>
</dict>
<key>ExpirationDate</key>
<date>2027-01-14T13:33:13Z</date>
<key>Name</key>
<string>iOS Team Provisioning Profile: com.example.hogehogeapp</string>
<key>ProvisionedDevices</key>
<array>
<string>00008130-0008685830AXXXXX</string>
<string>00008027-001904CC3ABCCCCC</string>
<string>00008110-001C5C62020WWWWW</string>
<string>00008030-000C182A224XXXXX</string>
</array>
<key>TeamIdentifier</key>
<array>
<string>123ABCDEF</string>
</array>
<key>TeamName</key>
<string>HogeHoge Company</string>
<key>TimeToLive</key>
<integer>365</integer>
<key>UUID</key>
<string>36c97823-9e2f-4ac2-a888-08cff9935739</string>
<key>Version</key>
<integer>1</integer>
</dict>
</plist>
DeveloperCertificates
DeveloperCertificatesには、開発可能な(署名可能)な開発者の一覧が入っています。一つ一つの <data> はBase64エンコードされた証明書になっていて、これをデコードすれば、サーバーから各開発者の証明書をDLしなくても良いようになっています。
<key>DeveloperCertificates</key>
<array>
<data>MIIFvTCCBKWgAwIB.....</data>
<data>MIIFvTCCBKWgAwIB.....</data>
</array>
実際に確かめたい方は、お手元のプロビジョニングプロファイルから <data> 部を抜き出して cert.b64 というファイルで保存し、以下のようにデコードしてみてください。
> base64 -D cert.b64 > cert.der
> openssl x509 -inform DER -in cert.der -text -noout
これで、この <data> 部に DERフォーマットの証明書が入っていることが確認できると思います。(なお証明書のファイフォーマットについて詳しく知りたい場合は、私の「RSA鍵、証明書のファイルフォーマットについて」という記事を参照してください)
実際にやってみると、この <data> 部は以下のような内容になっているのがわかります。
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
7d:f8:31:7f:60:02:7c:ad:70:c5:04:b1:fd:e2:f0:ca
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=Apple Worldwide Developer Relations Certification Authority, OU=G3, O=Apple Inc., C=US
Validity
Not Before: Feb 27 00:12:58 2025 GMT
Not After : Feb 27 00:12:57 2026 GMT
Subject: UID=L89QSV97S9, CN=iPhone Developer: Kunihiko Ohnaka (ABCDE12345), OU=123ABCDEF, O=Hoge Hoge CO., C=US
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:ba:49:22:05:43:93:b3:32:c0:f4:38:fa:f6:74:
1b:0f:46:e6:40:5d:a0:86:6f:68:95:84:21:4d:70:
41:d8:7a:57:0d:e3:d7:b8:9d:a9:aa:72:b9:e0:a2:
f2:1e:b9:fe:57:72:d0:78:90:dc:b0:76:ab:20:a7:
12:7f:7f:40:8b:7e:56:16:10:b1:f6:e5:25:c3:b6:
d5:83:c2:4b:72:f7:65:44:78:bb:3a:c3:4e:45:2c:
ee:c5:db:a4:eb:a9:03:7c:4b:9b:f1:51:c7:0a:7b:
(以下略)
ここから、
- Issuer が Apple
- 2048 bitの RSAの公開鍵である
ということが読み取れます。つまりこの証明書は私の公開鍵をAppleが証明しているもので、具体的にいうと、以下のように Appleの中間証明書によって署名されています。
Apple Root CA
└─ Apple Worldwide Developer Relations Certification Authority (G3)
└─ iPhone Developer: Kunihiko Ohnaka (...)
iPhone端末にはApple Root CAが信用できる証明書としてあらかじめ入っているので、中間証明書の正当性が確認でき、さらにその中間証明書によって私の証明書が保証される、ということです。
この構造を絵にすると、以下のようになります。
この証明書による信頼の連鎖も「Chain of Trust(信用の連鎖)」の一種です。
ルート証明書で直接署名しない理由は、安全性のためです。Development証明書はAppleのサーバーで頻繁に署名する必要がありますが、署名するということは秘密鍵が使われるということです。つまり、中間証明書の秘密鍵は頻繁に利用されるため、漏洩リスクがあります。もちろん厳重に管理することでそういう事故は起こさないようにするわけですが、万が一中間証明書の秘密鍵が漏洩するなどの事故があった場合は、
- 漏洩した中間証明書をブラックリストに入れ手使用不可能にする
- ルート証明書で新たな中間証明書を作製し、それをOSアップデートなどで再配布
- Development証明書も新しい中間証明書で再署名
とすることで回復することができます。
一方、もしルート証明書の秘密鍵が漏洩してしまうとこういった対応が難しいので、「滅多なことでは表に出さない」ようにし、厳重に管理しています。
Entitlements
Entitlements には、以下が入っています。
-
application-identifier- アプリの Bundle ID
-
keychain-access-groups- アプリのキーチェーンを共有できるアプリのグループID
- 同一ベンダーのアプリ同士で認証を共有するために使用できます
- 例:Gmailアプリでログインしているユーザーアカウントが Chromeアプリでもみられるなど
-
get-task-allow- アプリにデバッガを接続してよいかどうか
-
com.apple.developer.team-identifier- 所属しているチームのID
これにより、「どのチームの」「なんというIDのアプリが」「どういう許可をされているのか」が示されます。改めて説明しますが、プロビジョニングプロファイルは Appleの署名があるので、この部分を勝手に書き換えることはできません。
ExpirationDate
ExpirationDate にはこのプロビジョニングプロファイルの有効期限が書かれています。
この日付を過ぎると起動できなくなります。
ProvisionedDevices
ProvisionedDevices には、このアプリをインストール可能なUDIDの一覧が入っています。
プロビジョニングプロファイルの全体像
ここまでの説明をもとに、Development署名されたアプリの全体像を絵にすると以下のようになります。
ちょっと省略して書いてしまっているところもありますが、この図で大体の雰囲気は把握できると思います。
- アプリの中には実行バイナリとプロビジョニングプロファイルが入っている
- もちろんそれ以外にリソース類も入っています
- 実行バイナリはある開発者の秘密鍵で署名されている
- そのことはDevelopment証明書(=公開鍵)で検証できる
- 証明書が本物であるかどうかは、Appleの中間証明書によって検証できる
- Appleの中間証明書の正当性は、iPhone内部に入っている Appleのルート証明書で検証できる
- プロビジョニングプロファイルには、以下のようなことが書かれていて、それに反する利用はできない
- インストール可能なデバイスのUDID一覧
- デバッグ実行可能かどうかなどの情報(Entitlements)
- 有効期限(これを過ぎたら起動できなくなる)
上記は Developmentプロファイルのケースですが、Ad Hoc Distributionプロファイルの場合も同様です(Development証明書ではなく、Distribution証明書が使われるだけ)。
Xcodeでビルドする際のプロファイル、署名、自動署名について
ここまでの理解が進むと、Xcodeでアプリをビルドする際は以下のようなことをする必要があるとわかります。
- ①開発者の Development証明書の準備
- Mac上の Keychainに秘密鍵に保存し、その公開鍵を「Development証明書」としてAppleのサーバーに登録し、Appleに署名してもらう
- ②Developmentプロビジョニングプロファイルの準備
- ①の署名済みの証明書を含むプロビジョニングプロファイルをAppleのサーバー上で生成し、Appleに署名してもらう
- プロビジョニングプロファイルには実行可能な端末の一覧や有効期限なども一緒に書き込まれている
- ③コンパイラが実行バイナリを生成し、①の秘密鍵で署名
- ④上記②のプロビジョニングプロファイルと③の署名付きバイナリをZIPで固めて .ipa というパッケージを作成する
つまり、Xcodeがビルドする際、Mac上には以下のファイルが全て揃っている必要があります。
- 自分のDevelopment証明書 (公開鍵+秘密鍵)
- 自分のDevelopment証明書や端末が含まれるProvisioning Provile(Appleの署名が必要なので、署名後にダウンロードしておく)
10年前の2016年に発表された Xcode 8で「Automatically Manage Signing」の仕組みが入る前は、新しい開発メンバーが入ったりOSを入れ替えたりするたびに、Developer Portal上で Development証明書を追加したプロビジョニングプロファイルを生成してダウンロードしてくる……という割と面倒な手間がありました。
- 参考: 「DevelopersIO - [iOS 10] Xcode 8 の新しい Signing 機能について」
Automatically Manage Signingが導入されてからは、このチェックをONにするだけで、Xcodeが面倒な作業を一手に引き受けてくれるようになり、かなり便利になりました。
Automatically Manage Signing は、以下のようなことをしてくれます。
- iOS Team Provisioning Profileの作成
- このプロビジョニングプロファイルは位置付けとしては Developmeentプロビジョニングプロファイルです
- 「Xcodeが勝手に作る Developmentプロファイル」と思ってください
- このプロファイルにはチーム全員と全端末が含まれています
- 新しいメンバーが追加された場合
- 新メンバーが追加され得た場合や、OSの入れ替えなどでDevelopment証明書を作り直した場合、Xcodeは自動的に iOS Team Provisioning Profileにこのメンバーを追加し、署名されたプロビジョニングプロファイルをダウンロードしてきてくれます
なお、この Team Provisioning Profileは以前は Developer Portal上でも見えた(ような記憶がある)のですが、今は一覧には出てこなくなっています。ですが、Mac上の ~/Library/Developer/Xcode/UserData/Provisioning Profiles/ フォルダの下には、ダウンロードされたチームプロビジョニングプロファイルファイルが存在しますので、気になる方はそちらを見てみてください。
(Xcode16以前は ~/Library/MobileDevice/Provisioning Profiles/ にあったのですが、パスが変わったようです)
Ad Hoc配布するためには?
XcodeからAd Hoc署名されたアプリ(.ipa)を生成する最も簡単な方法は、
- [Product]-[Archive]で Xcodeアーカイブ (.xcarchive)をビルドする
- [Window]-[Organize]を開き、作製したXcodeアーカイブを選んで[Distribute App]を選ぶ
- 以下のメニューで [Release Testing]を選んで[Distribute]ボタンを押す
なお、当然ながらこのMacのKeychainには Distribution証明書とその秘密鍵が入っている("自分の証明書"になっている)必要があります。
AppStore Connectに提出するためのビルドをアップロードするには?
上記Ad Hoc配布と同じように Xcodeアーカイブを生成し、[Distribute App]で「App Store Connect」を選べばOKです。
この場合もこのMacのKeychainに Distribution証明書とその秘密鍵が入っている("自分の証明書"になっている)必要があります。
CIサーバーで署名する際の注意
Xcodeを使って手元のMac上でビルドする際は前述の通りなのですが、いざ、Bitriseなどの CIサーバーでビルドしようとした時に、署名がうまくいかずにハマるケースがあります(私もハマりました)。
よくあるトラブルの例は以下のような感じでしょうか。
- 自分の証明書(証明書+秘密鍵)がなく署名できない、というエラーで失敗
- 適切なプロビジョニングプロファイルがない、というエラーで失敗
- Ad Hoc配信の.ipaファイルを作りたいのに、出てきたものが Developmentプロファイル
- → 配布自体はできてしまうが、気持ち悪い
- CIサーバー上でAutomatically Manage Signingのプロジェクトをビルドする際、App Store Connectに接続しようとするも、サーバーへの接続ができず(認証がなく)失敗
- 仕方なくCIサーバー上では署名なしで .ipaや .xcarchiveを作ろうとするも、出来上がったファイルを使おうとすると「Team IDがない」などのエラーが出て失敗
本記事に書かれていることを理解するとこれらのエラーがなぜ発生するのかよくわかると思います。
自分の証明書(+鍵)が無い問題
手元の Mac上で開発している際は、キーチェーンに自分の Development証明書(+鍵)を生成するという処理を勝手にやってくれますが、素のビルドサーバーの場合、Keychainが空の状態からスタートするので、署名するための秘密鍵が無く、署名できずに失敗します。
この場合、以下のいずれかを行う必要があります。
- だれかの Development証明書(+鍵)を .p12 ファイル形式でエクスポートし、サーバーに渡す
- この場合は Developmentプロファイルでビルドする必要がります
- Distribution証明書(+鍵)を .p12 ファイル形式でエクスポートし、サーバーに渡す
- この場合は Ad Hoc Distributionプロファイルでビルドする必要がります
- App Store Connectの APIキーもしくは Apple IDとPWをサーバーに設定し、CIサーバー自身にDevelopment証明書(+鍵)を作らせる
- 作った証明書の秘密鍵を毎回捨てることになると良く無いので、このケースは稀だと思います
適切なプロビジョニングプロファイルが無い問題
手元の Mac上で開発している際は、Xcodeのアカウント設定で自分の Apple IDでログインしているため、プロビジョニングプロファイルは勝手にダウンロードしてきてくれます。 Automatically Manage Signingが OFFの場合でも、Xcodeのアカウント設定画面で Download Manual Profiles を押せば全プロファイルをDLしてインストールしてくれます。
CIサーバーの場合は Apple IDでログインしていない状態なので、以下をする必要があります。
- Automatically Manage Signing = OFFの場合
- 手動作製したプロビジョニングプロファイルを CIサーバーに事前にインストールする
- Developmentプロファイルを使う場合は、Development証明書+鍵(.p12)も必要
- Ad Hocプロファイルを使う場合は、Distribution証明書+鍵(.p12)も必要
- 手動作製したプロビジョニングプロファイルを CIサーバーに事前にインストールする
- Automatically Manage Signing = ONの場合
- 手動作製したプロビジョニングプロファイルもしくはTeam Provisioning Profileを CIサーバーに事前にインストールする
- Developmentプロファイルを使う場合は、Development証明書+鍵(.p12)も必要
- Ad Hocプロファイルを使う場合は、Distribution証明書+鍵(.p12)も必要
- App Store Connectの APIキーをセットし、自動でやらせる
- Developmentプロファイルを使う場合は、Development証明書+鍵が勝手に生成され、Team Provisioning Profileも更新される
- Ad Hocプロファイルを使う場合は、Distribution証明書+鍵(.p12)が別途必要
- 通常これを勝手に作るということはしないはず
- 手動作製したプロビジョニングプロファイルもしくはTeam Provisioning Profileを CIサーバーに事前にインストールする
ただ、CIサーバーに自分の証明書やプロファイルをアップロードしただけでは、「ビルド時に」そのファイルをビルドインスタンスにインストールしてくれ無い場合もあるので注意が必要です。
たとえば Bitriseの場合は Manage iOS Code Signing というステップを追加することでこれを行ってくれます。
Manage iOS Code Signingステップの説明文(機械翻訳)
ビルド前にコード署名アセットを自動的に管理します。
Manage iOS CodeSigning ステップでは、 Bitriseでプロジェクトをビルドする前に必要なコード署名アセットの設定を行います。このステップでは、
- Bitrise にアップロードされた証明書をダウンロードしてインストールします
- iOS プロジェクトに必要なプロビジョニング プロファイルを生成、更新、ダウンロードします
- Apple Developer サイトでプロジェクトのバンドル ID を検証して登録します
- Bitrise アカウントに接続されている iOS または tvOS デバイスを Apple Developer Site に登録します
Bitriseでの設定について
Bitriseを使った iOSの署名管理の勘所については別の独立した記事を書きたいと思いますが、それまでは上記をヒントに対応していただければと思います。
おわりに
今まで何がどうなって行われていたのかよくわからなかった「プロビジョニングプロファイル」「証明書」「署名」まわりを、かなり深くまで、具体的な例を交えて掘り下げてみました。
まだちょっと誤魔化しながら書いてしまっているところも残ってしまっています。また、私の理解が足りおらず間違っている部分もあるかもしれません。
もし「ここは違うんじゃ無いか?」「ここはよくわからなかった」というところがありましたらコメントにてお知らせいただければ幸いです。















