Posted at

PHP で .ipa ファイルからプロビジョニングプロファイルの情報を取り出す

iOS のエンプラ配布でやらかしたのでメモ。

iOS アプリの配布には、大きく2種類分けて、通常のストア配布用の Apple Developer Program と、特定の企業向けに配布する Apple Developer Enterprise Program があります。

今回は後者の通称「エンプラ配布」でのお話。

一般的に iOS アプリの配布には、配布用証明書とプロビジョニングプロファイルが必須ですが、このプロファイルには有効期限があり作成日から365日という制限があります。通常はアプリビルド時にチェックが入り、期限が切れたプロファイルでのビルドが出来ない、というだけなのですが、エンプラ配布の場合はそれに加えて、有効期限を1秒でも過ぎるとアプリが起動できなくなる、という罠があります。

ええ、やらかしました。本日。

小さなシステムだとアップデートしてもらえば良いのですが、全国支店に何百台と配布しているシステムではそう簡単な話ではなく、相当な人的コストをかけて一台一台アップデートしなければなりません。

そんなわけで朝から関係各所からお叱りを受け、とてもテンション下がってますが、繰り返さないためにもここにメモを残します。(こういったことを避けるために MDM やデプロイサービスというものが存在することは重々承知ですが、大人の事情で導入できていません。In-House だと TestFlight も使えないし...)

そもそも MDM なしでインストール済みのアプリ内プロファイルの証明書をリモートから読み取る手段はありません(私の知る限り)。

そこで、弊社では配布する ipa ファイルを S3 上で配布バージョンごとに残しておき、現場で動いているアプリバージョンと比較して、ざっくりと1年間という期限を管理していました。

よく勘違いするのですが、この有効期限は、アプリをビルドしてから365日、ではなく、また、配布用証明書の有効期限でもなく、プロビジョニングプロファイルを作成してから365日です。「こまめにアプリをアップデートしているから大丈夫」「配布用証明書の有効期限は2年くらい先だからまだまだ大丈夫」とった慢心が今回のような事故を招きます。(自戒)

で、このプロビジョニングプロファイルですが、ipa ファイルさえ残っていれば、どのプロファイルを使用してビルドしたか、や、有効期限そのものを直接参照することができます。今までは S3 上の ipa ファイルを落としてきて zip で解凍して、embedded.mobileprovision ファイルを開いて、、、ということを手作業でやってきましたが、それを PHP で実装したら10行程度だったので公開します。

$appName = 'AppName'; // アプリ名称

$profilePath = "zip://{$appName}.ipa#Payload/{$appName}.app/embedded.mobileprovision";
$ipa = file_get_contents($profilePath);
// 正規表現で plist タグ内の XML を取得
if (preg_match('/<plist version="1.0">([\s\S]+)\<\/plist>/', $ipa, $matches)) {
$plist = simplexml_load_string($matches[1]);
$currentKey = null;
foreach ($plist as $key => $value) {
if ((string)$key === 'key') {
$currentKey = trim((string)$value);
} else {
$params->$currentKey = trim((string)$value);
}
}
}

echo $params->ExpirationDate;
// 2020-06-05T03:47:37Z

ipa ファイルは zip アーカイブファイルなので、 PHP では、zip ストリームラッパー (zip://ipaファイル#圧縮内パス) で透過的にプロビジョニングプロファイルの中身を直接参照しています。

プロビジョニングプロファイルの情報は下記のような構成になっています。

〜バイナリ〜

<!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>アプリ名</string>
〜省略〜
<key>CreationDate</key>
<date>2019-06-05T03:47:37Z</date> ← プロファイル作成日
〜省略〜
<key>ExpirationDate</key>
<date>2020-06-05T03:47:37Z</date> ← プロファイル有効期限
<key>Name</key>
<string>プロビジョニングプロファイル名</string>
〜省略〜
</dict>
</plist>
〜バイナリ〜

この通り、XMLの前後にバイナリが記載されており、XML も Mac や iOS でお馴染みの plist 形式になるので、直接読み取ることができないので、雑な正規表現で取ってきて key と value を適当にパースしています。

同様に Info.plist の情報も読み取れるので、バージョン情報と共に、定期的にチェックして有効期限が近づくとアラートを出すとか出来そうですね。

やってみれば何てことはないしPHPじゃなくても出来ますが、これで同様の事故の防止に役立てば幸いです!