iOSアプリのリサインに失敗しないために気をつけること

この記事は[スタンバイ Advent Calendar 2023](https://qiita.com/advent-calendar/2023/stanby "Advent Calendar 2023")の2日目の記事です。
昨日は、[@weno](https://qiita.com/weno)さんの記事でした。

# リサインに至る経緯
私は2023年12月現在、求人検索サービスを展開するスタンバイでQAグループに所属しています。
今年、dev版iOSアプリのipaファイルをリサイン（resign、リザイン、再署名）する機会に遭遇しました。

リサインはアプリを改竄する行為となりますので、自社アプリのアップデートなどの動作検証の目的だけで実施しましょう。

プログラマティックなQAスタッフはそう多くないはず、とは思いますが、リサイン自体は簡単なプログラムで動きますので、存在を知っていると、いざというときに役立つかもしれません。（たいてい、いざというときは来ないものなんですけどね。。。）

今回の目標は、リサインの罠にハマらず、スタイリッシュにリサインすることです。
リサインの泥濘みを彷徨っている方が居れば、ここの情報が役に立つかもしれません。

この記載は、なぜあなたがリサインに失敗しているのか、にヒントを与えるためのテストケースです。
ちなみに、私はプログラマーではないので深く完璧に理解できているわけではなく、厳密なところで間違っていたり、勘違いでやけに複雑に記載していたり、曖昧な記憶のまま思い込みで概念的に話したりしますが、そんな矛盾や誤りを見つけたとしても一笑に付して頂ければ幸いです。

:::note
リサインって何？
ipaファイルを新しい証明書や新しいプロビジョニングファイルを使用して再署名し、利用期限を延ばすこと
:::

## iOSアプリの期限が切れた！！
さて、スタンバイではWeb上のサービスの他、iOS/Androidアプリで求人検索サービスを華麗に提供しています。
QAテストでも使用しているdev版のiOSアプリ（ipa）ですが、通常、弊社ではサードパーティのWebサービスを利用しており、基本的にWeb経由でインストールしています。（リリース直前にはTestFlightを利用して最終チェックをしてみたり。）
ただし、古めのバージョンについては、順次削除を行っている為、Web経由でのインストールが不可能となっています。

QAの（気分的に）しんどい業務の一環として、過去バージョンからのアップデートチェックがありますが、旧バージョンについては、直接、PCと端末を接続しipaファイルをインストールしてから最新版にアップデートを行っていました。
が、忘れもしない今年の10月に、忘れた頃にやってくる期限切れによるインストール不可現象が発生しました。

## ふたつの期限切れがある
iOSアプリが起動できない要因として、2種の期限切れがあります。
プロビジョニングファイルと証明書です。（単純に、ipa内にそのふたつが入っているという理解でOKです。）

### プロビジョニングファイルの期限切れ（1年ごと）
プロビジョニングファイルは、ipaのインストール時に、ipa内のプロビジョニングファイルがiPhone内にインストールされます。

:::note
プロビジョニングファイルって何？
・Apple Developer Programのサイトからダウンロードできる
・ipaのビルド時にipaファイル内に組み込まれる
・UDIDが記載されている（インストールできる端末のID）
・プロビジョニングファイルの期限が記載されている
・使用する証明書が記載されている
・プッシュ通知など、Capabilityの設定について記載がある
:::

アプリの起動時に、端末時間とプロビジョニングファイルの期限の整合性を取っている気がします。期限切れ時には、アプリを起動するとiPhone内のプロビジョニングファイルが消去されたりします。（残っていたりもするかも）

:::note warn
回避方法は、以下です。
1. 新しいプロビジョニングファイルを使用して、ビルドしてもらう（開発）
1. 新しいプロビジョニングファイルを使用して、リサインする（開発、QAでもできる）
1. 端末時間を期限内に戻して起動する（姑息）
    ・端末時間を戻してから、アプリを（上書き）インストールすることで、起動できるようになるかもしれません。端末時間を戻す前にアプリ起動すると端末内からプロビジョニングファイルが消去されている場合は再インストールするまで起動不可です。
1. 新しいプロビジョニングファイルをiPhoneに入れる
    ・同じ証明書であれば、端末内に新しい期限のプロビジョニングファイルをインストールするだけで起動できるようになります。
    ・UDIDの追加も「新規プロビジョニングファイルの出力/インストール」で起動可能です。
    ・インストールしても起動できない場合は、端末内の古いプロビジョニングファイルを削除してみましょう。
:::

### 証明書の期限切れ（1年〜数年ごと）
期限切れになる前に更新しないといけないのが証明書です。
更新を失念していると、ある日全端末で起動できなくなり、全てのQA作業が止まってパニックになります。

:::note warn
回避方法は、以下です。
1. 新しいプロビジョニングファイルを使用して、ビルドしてもらう（開発）
1. 新しいプロビジョニングファイルを使用して、リサインする（開発、QAでもできる）
:::

古いバージョンを再ビルドは、できないときがあります。
回避方法の「１.開発が再ビルドする」は、かなりの手間らしいですが、古い開発環境を再現してビルドするっていうことだろうし、想像に難くないですね。

## リサインしよう
### リサインの流れを確認する
（ようやく本題の）QAでもできるリサインにとりかかりましょう。
必要なファイルは、以下です。
1. ipaファイル
1. 証明書（p12ファイル） ※基本、開発依頼。ちなみにcerファイルは作成したmacでしか使えない。
1. プロビジョニングファイル（appexがある場合、複数） ※基本、開発依頼
1. Xcodeがインストールされたmac（commandファイルを実行できる環境）

証明書のインストールの仕方や、macの設定などは検索しながら解決してください。
commandファイルをダブルクリックしてリサインできるようにします。
Automaterを使えば、ファイル選択でipaファイルをより簡単にリサインできるようになりますが、今回はおざなりにベタ書きで説明します。
まず、ChatGPTに向かってリサインしたいとお祈りします。

```shell:resign.command
#!/bin/bash

# 開発者ID（証明書の名前）を設定
DEVELOPER_ID="iPhone Distribution: Your Developer ID"

# 新しいプロビジョニングプロファイルのパス
PROVISIONING_PROFILE="/path/to/your_new.mobileprovision"

# 再署名する.ipaファイルのパス
IPA_PATH="./app.ipa"

# 出力される新しい.ipaファイルのパス
NEW_IPA_PATH="./resigned_app.ipa"

# ワーキングディレクトリを作成
WORKING_DIR="./working_dir"
mkdir -p "$WORKING_DIR"

# .ipaファイルを解凍
unzip -q "$IPA_PATH" -d "$WORKING_DIR"

# Payload内の.appファイルパスを取得
APP_PATH=$(find "$WORKING_DIR/Payload" -name "*.app")

# プロビジョニングプロファイルを置き換え
cp "$PROVISIONING_PROFILE" "$APP_PATH/embedded.mobileprovision"

# 再署名（エンタイトルメントを含む場合はコメントアウトを外す）
# ENTITLEMENTS="entitlements.plist" # エンタイトルメントファイルのパス
# codesign -f -s "$DEVELOPER_ID" --entitlements "$ENTITLEMENTS" "$APP_PATH"
codesign -f -s "$DEVELOPER_ID" "$APP_PATH"

# 新しい.ipaファイルを作成
cd "$WORKING_DIR"
zip -qr "$NEW_IPA_PATH" *

# ワーキングディレクトリの削除
cd ..
rm -rf "$WORKING_DIR"

echo "アプリの再署名が完了しました。"
```

文明の利器というものは、とてもありがたいことです。
これでリサインできるのかさっぱり分かりませんが、以下の手順でリサインが行えることがわかりました。

1. ipaファイルはzipファイルである
1. リサインの流れは以下である。
    1. ipaファイルを解凍する
    1. プロビジョニングファイルを差し替える
    1. 指定したプロビジョニングファイルと証明書で、codesignで再署名する
    1. zipファイルにして、拡張子をipaにする
    1. 手順1で解凍したフォルダを削除する
1. entitlements.plistが必要になるかも

ただし、まず「わからない、覚えらんない」が基本のボクらには、上記のプログラムを改変してもリサインに成功するとは到底思えません。リサインにはいくつもの罠が潜んでおり、それをひとつずつ解決していくことがとても重要だからです。

今回は、可能な限り一緒くたに解決していきましょう。

### ipaの中身を知ろう
「commandファイルをダブルクリックしてリサインする」が最終目標ですが、理解なくして進歩はないので、覚える必要はないが過ちに気づけるくらいに理解を深められるよう、まずは複製したipaの拡張子をzipに変更/解凍し中身を眺めてみましょう。

リサインに関係するフォルダ/ファイルのみ抽出したファイルツリーです。

<pre>
Payload
└── （アプリ名）.app　 ※右クリック＞ パッケージの内容を表示する
    ├── _CodeSignature  ※古い証明書
    ├── Frameworks  ※配下のファイルは再署名の必要性あり
    │   ├── Adjust.framework など
    │   └── libswift_Concurrency.dylib など
    ├── PlugIns  ※配下のappexは再署名の必要性あり
    │   └── Notification.appex　※右クリック＞ パッケージの内容を表示する
    │       ├── _CodeSignature
    │       ├── Info.plist
    │       └── embedded.mobileprovision
    ├── Info.plist  ※バンドルIDの書き換えなど
    └── embedded.mobileprovision  ※古いプロビジョニングファイル
SwiftSupport ※不要
Symbols ※不要
</pre>

:::note alert
[罠1]ipaファイルが壊れていることに気づきにくい
:::
まずipaファイルが壊れて、正常に解凍できない場合、リサインに失敗します。
リサインに失敗した理由を探す手間を予防するため、チェック機構をつけましょう。

```shell:
if ! unzip -q $IPA_PATH; then
rm -f app_resign.ipa
rm -fR Payload/
rm -fR Symbols/
rm -fR SwiftSupport/
echo
echo "ipaファイルが壊れています。リサインに失敗しました。"
echo
exit 1
fi
```

### entitlements.plistを作成しよう
:::note alert
[罠2]entitlements.plistは、プロビジョニングファイルと整合性が取らないといけない
:::
entitilements.plistはプロビジョニングファイルに合わせて、書き換える必要のあるファイルです。
整合性の取れていないplistを使用していると、リサイン後のipaができてもインストールできません。

自社のアプリの場合は特に気にせずに使用できるかもしれませんが、開発元が別会社であり、リサインする自社の証明書を使う場合には細心の注意が必要です。
ちなみにリサインは、アプリを改竄する行為となりますので、著作権の関係上、事前に両社で同意をとることが重要です。
まず、リサイン元のipaからentitlements.plistを出力しましょう。

<pre>
resign_folder
├── 01output.command  #紹介するコード
└── app.ipa  #リサイン対象のipa
</pre>

```shell:01output command
#!/bin/bash
working_dir=$(dirname "$0")

# リサイン対象のIPAファイル名
IPA_FILE=app.ipa
IPA_FILENAME=$(basename "${IPA_FILE}" .ipa)

# 現在のディレクトリに移動
cd "$(dirname "$0")" || exit

# 既存のディレクトリ削除
rm -fR Payload/

# IPAファイルの解凍
echo "UNZIP"
unzip -q "$IPA_FILE"

# appフォルダ名の取得
APP_DIR=$(basename Payload/*)
APP_PATH="Payload/${APP_DIR}"

# プロビジョニングファイルの取り出し
PROVISION_PATH="${APP_PATH}/embedded.mobileprovision"
cp "${PROVISION_PATH}" "embedded.mobileprovision.xml"
security cms -D -i "${PROVISION_PATH}" > "embedded.mobileprovision.plist"
echo "プロビジョニングファイルのコピー完了"

# info.plistの取り出し
cp "${APP_PATH}/info.plist" "info.plist"
echo "info.plistのコピー完了"

# Entitlements.plistの書き出し
codesign -d --entitlements :- "${APP_PATH}" > temp.plist
tr -d '\0' < temp.plist | xmllint --format - > entitlements.plist
rm -fR temp.plist
echo "entitlements.plistのコピー完了"


# PlugInsディレクトリから出力
(
IFS=$'\n'
for file in `find ${APP_PATH}/PlugIns | grep .appex/_CodeSignature/CodeResource`; do
  Dir_PlugIns=$(dirname "$(dirname "$file")")
  Dir_PlugIns_Folder=$(basename "${Dir_PlugIns}" .appex)

  PROVISION_PATH="${Dir_PlugIns}/embedded.mobileprovision"
  cp "${PROVISION_PATH}" "${working_dir}/${Dir_PlugIns_Folder}_embedded.mobileprovision.xml"

  security cms -D -i "${PROVISION_PATH}" > "${Dir_PlugIns_Folder}_embedded.mobileprovision.plist"
  cp "${Dir_PlugIns}/info.plist" "${working_dir}/${Dir_PlugIns_Folder}_info.plist"

  codesign -d --entitlements :- "${Dir_PlugIns}" > temp.plist
  tr -d '\0' < temp.plist | xmllint --format - > ${working_dir}/${Dir_PlugIns_Folder}_entitlements.plist
  rm -fR temp.plist
done
)

rm -fR Payload/
rm -fR Symbols/
```
ipaファイル名は、app.ipaにしています。実行権限も付与しましょう。
```shell:
chmod +x 01output.command
```
作業フォルダを作成し、作成したcommandファイルとapp.ipaを入れて、commandファイルを実行してみましょう。
ipaファイル内のprovisioningファイル（プロビジョニングファイル）とentiltements.plistが出力されます。
複数のentitlements.plistが出力された場合、それはappexに対応したファイル群となります。

:::note alert
[罠3]ipaファイルのファイルパスに半角括弧やスペースが含まれないこと
:::
ipaファイルの（親階層含めた）フルパスの中に半角括弧や半角スペースがあるとエラーが出ます。リネームなどで削除しておきましょう。

さて、ここで出力されたentitlements.plistとプロビジョニングファイルをXcodeなどで開いて、見比べてください。
ipa内のプロビジョニングファイルをplistやxmlで出力したものですが、リサインでは使いません。理解を深めるためのファイルです。
余談ですが、プロビジョニングファイルは拡張子をxmlに変更すれば、Xcodeでも開けます。Windowsでは、メモ帳にD&DでOKです。
プロビジョニングファイル内に記載されている"Entitlements"にある各キーの値と、entitlements.plist内の値は整合性が取れていないとリサインに成功したと思っても、ぬか喜びです。
各値が一致しているよう、プロビジョニングファイルの値をentitlements.plistに書き写してください。

:::note alert
[罠4]プロビジョニングファイルのCapabilityが異なるとリサインに失敗する
:::
プロビジョニングファイルを眺めていると、どのCapabilityを使用しているかがわかります。
異なってもリサインに成功することはもちろんありますが、既存のアプリと同じCapabilityを使ったプロビジョニングファイルであることが理想的です。
entitlementsに「aps-environment」が含まれていたらプッシュ通知が有効である、などがわかります。
新旧のプロビジョニングファイルを比較し、Capabilityに過不足がないか確認しましょう。
過不足がある場合は、プロビジョニングファイルの出力時に使用するAppIDのCapabilityを見直して、再出力してもらいましょう。
具体例としては、他社のアプリをリサインする場合には、plist内のbundleIDの書き換え等が必要になります。
リサインするのは開発の協力会社となりますが、著作権の関係上、勝手にリサインするのはやめましょう。（2回目）

:::note alert
[罠5]Frameworks内のフォルダ/ファイルは再署名しないといけない
:::
Frameworksのフォルダがある場合、中身のファイルを再署名してなくてはいけません。
ChatGPTには、聞かないと教えてくれません。

:::note alert
[罠6]appexも署名しなくてはいけない
:::
PlugInsフォルダがある場合、app内にappexという子アプリみたいなものがあります。
こちらもプロビジョニングファイルの差し替えや再署名を行う必要があります。
ChatGPTには、聞かないと教えてくれません。

:::note alert
[罠7]再署名の順番を間違ってはいけない
:::
署名を行うのは、app全体（必須）、各Frameworks、各appexです。
app内に「各Frameworks、各appex」が入りますので、最後にapp全体を署名するようにしましょう。
app全体を署名してから、appexなどを再署名して、中身を改変してはいけません。
これは常識というものらしく、ChatGPTは触れることすらないでしょう。

ここまでの注意点を踏まえて、リサインできるcommandファイルを作成してみましょう。
許可を得ていないアプリをリサインして改竄することは、著作権を侵害することになります。（3回目）

### リサインの最終準備をしよう
リサインする作業フォルダ内に必要なファイルを揃えましょう。
必要であれば出力したenetilements.plistは前述の通り手作業で改変してください。
自動化も行えますが、無駄にプログラムが長くなるので、ここでは割愛します。

リサイン直前のファイルツリーは以下の通りです。
最低限必要なファイルは、プロビジョニングファイル、entitlements.plist、リサイン元のipaファイルの3つです。
<pre>
resign_folder
├── 02resign.command  #紹介するコード
├── app.ipa  #リサイン対象のipa
├── Notification_embedded.mobileprovision  #appex毎に作成
├── Notification_entitlements.plist  #appex毎に作成
├── pf.mobileprovision  #新しいプロビジョニングファイル
└── entitlements.plist  #出力したplistから、新しいプロビジョニングファイルを反映
</pre>

appexがある場合を想定している為、2ファイル増えていますが、紹介するコードではappex用のファイルがない場合、app用のファイルでappexをリサインします。
appexのファイル名の頭は、appexのアプリ名です。

:::note alert
[罠8]期限が異なる同名の証明書があると、リサインに失敗する
:::
リサインで使用するmacのキーチェーンには、使用する証明書をインストールしておいてください。
期限が異なる同名の証明書をインストールしていると、リサインに失敗します。
証明書の更新は数年に1回なので、その頃には確実に忘れています。
そもそも覚えていないので、忘れているというのも誤った表現だと開き直れるくらいに忘れています。
そしてこの罠が悪質なのは、1回目の作業では証明書を消す作業が必要ないことです。

:::note alert
[罠9]証明書を初回に使用する際のパスワードは、macにログインするパスワードである
:::
証明書をインストールする時に*証明書のパスワード*を聞かれます。
パスワードを設定していない場合は、そのままreturnキーでキーチェーンにインストールされますが、リサイン時の証明書を使用するタイミングで聞かれるパスワードはmacのパスワードです。

### リサインを走らせよう
お待たせしました。
最終的なリサインのコードは以下です。
先に作成した「01output.command」を複製してコードを貼り付けましょう。
そして、macのキーチェーンにインストールしている証明書の名前を、「DEVELOPER_ID」に代入しましょう。
動けばいい、だけで素人が作成したので拙い感じかもしれませんが、動けばいいんです。

~~~shell:02resign.command
# ==================================================================
# ユーザー書き換え項目

#ipaのファイル名と、リサイン後に付与するファイル名
IPA_PATH=app.ipa
AddFileName="_resign"

#プロビジョニングファイル名
PROVISIONING=pf.mobileprovision

# 開発者ID（証明書の名前）を設定
DEVELOPER_ID="iPhone Distribution: Your Developer ID (xxxxxxxxxx)"

# ==================================================================

cd `dirname $0`
IPA_BASE=$(basename "${IPA_PATH}")
PLISTBUDDY=/usr/libexec/PlistBuddy

echo
echo "***************************************"
echo "Step1. 削除"
echo "***************************************"
rm -f ${IPA_BASE}${AddFileName}.ipa
rm -fR Payload/
rm -fR Symbols/
rm -fR SwiftSupport/

echo
echo "***************************************"
echo "Step2. 解凍"
echo "***************************************"
echo ipaを解凍中です。ファイルサイズが大きい場合、時間がかかります。
IPAsize=`wc -c < ${IPA_PATH}`
IPAsize=`expr ${IPAsize} / 1000000`
echo ipaファイルサイズ ： ${IPAsize} Mbyte
if ! unzip -q ${IPA_PATH}; then
rm -f ${IPA_BASE}${AddFileName}.ipa
rm -fR Payload/
rm -fR Symbols/
rm -fR SwiftSupport/
echo
echo "ipaファイルが壊れています。リサインに失敗しました。"
echo
exit 1
fi

echo
echo "***************************************"
echo "Step3. app名の取得"
echo "***************************************"
APP_PATH="Payload/$(basename Payload/*)"
echo "${APP_PATH}"

(
IFS=$'\n'
echo
echo "***************************************"
echo "Step4. dylibの検出"
echo "***************************************"
if [ -d ${APP_PATH}/Frameworks ]; then
for file in `find ${APP_PATH}/Frameworks | grep dylib`; do
echo `basename $file`
done
fi

echo
echo "***************************************"
echo "Step5. Frameworkの検出"
echo "***************************************"
if [ -d ${APP_PATH}/Frameworks ]; then
for file in `find ${APP_PATH}/Frameworks | grep .framework/_CodeSignature/CodeResource`; do
echo `basename $(dirname $(dirname $file))`
done
fi

echo
echo "***************************************"
echo "Step6. PlugInsの検出"
echo "***************************************"
if [ -d ${APP_PATH}/PlugIns ]; then
for file in `find ${APP_PATH}/PlugIns | grep .appex/_CodeSignature/CodeResource`; do
echo `basename $(dirname $(dirname $file))`
done
fi
)

echo
echo "***************************************"
echo "Step7. 旧証明書の削除"
echo "***************************************"
rm -fvR "${APP_PATH}/_CodeSignature"

echo
echo "***************************************"
echo "Step8. entitlements.plistの最終処理"
echo "***************************************"
# TestFlight用のentitlementsの削除
if $PLISTBUDDY -c "Print :beta-reports-active" entitlements.plist 2>/dev/null; then
$PLISTBUDDY -c "Delete :beta-reports-active" entitlements.plist
fi

echo
echo "***************************************"
echo "Step9. プロビジョニングファイルのコピー"
echo "***************************************"
if [ ! -e $PROVISIONING ]; then
rm -f ${IPA_BASE}${AddFileName}.ipa
rm -fR Payload/
rm -fR Symbols/
rm -fR SwiftSupport/
echo
echo "指定されたプロビジョニングファイルが見つかりません。"
echo "commandファイルの内容を確認してください。"
echo
exit 1
fi
cp -vf ${PROVISIONING} "${APP_PATH}/embedded.mobileprovision"

echo
echo "***************************************"
echo "Step9. リサイン - dylib"
echo "***************************************"
(
IFS=$'\n'
if [ -d ${APP_PATH}/Frameworks ]; then
for file in `find ${APP_PATH}/Frameworks | grep dylib`; do
  `codesign -f --deep --force -v -s "${DEVELOPER_ID}" $file`
done
fi

echo
echo "***************************************"
echo "Step10. リサイン - Framework"
echo "***************************************"
i=0
if [ -d ${APP_PATH}/Frameworks ]; then
for file in `find ${APP_PATH}/Frameworks | grep .framework/_CodeSignature/CodeResource`; do
Dir_Framework=`dirname $file`
i=$((i+1))

if [ `echo $Dir_Framework|grep .framework/Frameworks/` ]; then
echo "10-1.Framework> Resign$i"
echo "$Dir_Framework"
echo "リサイン済の為、除外"
else
echo "10-2.Framework> 証明書の削除$i"
rm -fR ${Dir_Framework}
Dir_Framework=`dirname ${Dir_Framework}`

j=0
for file in `find ${Dir_Framework} | grep .framework/_CodeSignature/CodeResource`; do
inDir_Framework=`dirname $file`
j=$((j+1))
echo "10-3.Framework内Framework> 証明書の削除$i-$j"
rm -fR ${inDir_Framework}
inDir_Framework=`dirname ${inDir_Framework}`
echo "10-4.Framework内Framework> Resign"
codesign -f -s "${DEVELOPER_ID}" "${inDir_Framework}" --entitlements entitlements.plist
done

echo "10-5.Framework> Resign"
codesign -f -s "${DEVELOPER_ID}" "${Dir_Framework}" --entitlements entitlements.plist
fi
echo
done
fi
)

echo
echo "***************************************"
echo "Step11. リサイン - appex"
echo "***************************************"
(
IFS=$'\n'
i=0
if [ -d ${APP_PATH}/PlugIns ]; then
for file in `find ${APP_PATH}/PlugIns | grep .appex/_CodeSignature/CodeResource`; do
Dir_PlugIns=`dirname ${file}`

i=$((i+1))
echo "11-1.PlugIns> 証明書の削除$i"
rm -fR ${Dir_PlugIns}
Dir_PlugIns=`dirname ${Dir_PlugIns}`
Dir_PlugIns_Folder=`basename ${Dir_PlugIns} .appex`

echo
echo "以下のファイルがリサインフォルダ内に存在する場合はappexのリサインに使用。"
echo "存在しない場合は、appのリサインファイルを使用。"
echo "${Dir_PlugIns_Folder}_embedded.mobileprovision"
echo "${Dir_PlugIns_Folder}_entitlements.plist"

echo
echo "11-2.PlugIns> プロビジョニングファイルのコピー"
tmpprofile=${Dir_PlugIns_Folder}_embedded.mobileprovision
if [ -e "${tmpprofile}" ]; then
plugins_PROVISIONING_PROFILE=${tmpprofile}
else
plugins_PROVISIONING_PROFILE=${PROVISIONING}
fi
cp -vf ${plugins_PROVISIONING_PROFILE} "${Dir_PlugIns}/embedded.mobileprovision"

echo
echo "11-3.PlugIns> リサイン"
tmpentitlements=${Dir_PlugIns_Folder}_entitlements.plist
if [ -e "${tmpentitlements}" ]; then
plugins_ENTITLEMENTS=${tmpentitlements}
else
plugins_ENTITLEMENTS=entitlements.plist
fi
codesign -f -s "${DEVELOPER_ID}" "${Dir_PlugIns}" --entitlements ${plugins_ENTITLEMENTS}
done
fi
)

echo
echo "***************************************"
echo "Step12. リサイン - アプリ全体"
echo "***************************************"
codesign -f -s "${DEVELOPER_ID}" "$APP_PATH" --entitlements entitlements.plist

echo
echo "***************************************"
echo "Step13. パッケージ化"
echo "***************************************"
echo ipaを作成中です。ファイルサイズが大きい場合、時間がかかります。
echo 元ipaファイルサイズ ： ${IPAsize} Mbyte
zip -yqr ${IPA_BASE}${AddFileName}.ipa Payload/
echo packaging --END--

echo
echo "***************************************"
echo "Step14. ファイルの削除と移動"
echo "***************************************"
rm -fR Payload/
rm -fR Symbols/
rm -fR SwiftSupport/

echo
echo "リサイン処理終了"
echo
~~~

:::note alert
[罠10]リサイン時に証明書がないってエラーが出る
:::
証明書の整理などを行なっていた場合、誤って消したであろうAppleの中間の証明書が必要です。
エラーに応じて、以下のサイトから証明書のDLとmacのキーチェーンへのインストールをしてください。

https://www.apple.com/certificateauthority/

数打ちゃ当たるですが、私の場合は「AppleWWDRCAG3.cer」をまずインストールして、リサインを再試行します。

:::note alert
[罠11]PCを再起動しないと、署名できない時がある
:::
どれだけ己の正しさを疑って試行錯誤していても、失敗するのがリサインです。
そんな時は、macを再起動してリサインしてみてください。

:::note alert
[罠12]バンドルIDを書き換えるか、書き換えないか
:::
「PLISTBUDDY=/usr/libexec/PlistBuddy」がプログラムに入っていますが、info.plistのバンドルIDは書き換えていません。
アプリのよっては、起動時や通信時にバンドルID(bundleID）のチェックが入っています。
基本的にアプリがインストールできた瞬間にリサインの成功を確信できますが、リサインの影響で他の設定が必要な場合もあることを頭の片隅に入れておきましょう。

## 最後に

以上が私が覚えている限りのリサイン時にハマった思い出たちです。（まだあるはずですが）
数時間の格闘の末、明け方にPC再起動で解決した時には、憤死するかと思いました。
皆さまの人生において、リサインに関わる時間が限りなく短くなり、死の恐怖から逃れられることを祈って終わりにします。

長文すぎましたが、ありがとうございました。

明日の[スタンバイ Advent Calendar 2023](https://qiita.com/advent-calendar/2023/stanby "Advent Calendar 2023")は、[@arata-honda](https://qiita.com/arata-honda)さんの「Redisのアップグレードをした話」です。
必読ですね。お楽しみに。