notarytool 公證失敗:2026 排查清單

Apple 公證服務從 2023 年 11 月 1 日起不再接受 altool 或 Xcode 13 以前版本發起的上傳。 如果你遇到 notarytool 公證失敗,先保存 Submission ID,再讀取公證日誌;不要直接重傳同一個檔案。(Apple Developer:公證 macOS 軟體)

這篇適合三類人:

  • 直接發布 DMG、PKG 或 ZIP 的 macOS 獨立開發者。
  • 使用腳本或 CI 自動簽名、公證,但偶爾收到 Invalid 或長時間 In Progress 的維護者。
  • 想把發布工作搬到遠端 Mac,並需要驗證憑證、Keychain 與公證流程的小型團隊。

01 先把「公證失敗」拆成四個不同故障層

公證、程式碼簽名、票據附加和 Gatekeeper 驗證不是同一件事。公證服務會檢查 Developer ID 簽名與提交內容;Gatekeeper 則在使用者安裝或首次啟動時,重新評估簽名、票據及其他安全資訊。公證成功,不代表每一個分發檔案都已完成 stapler,也不代表所有 macOS 版本都會以相同方式通過驗證。(Apple Developer:Developer ID 支援)

先用這個判斷表分層:

看到的狀態或現象 先判定哪一層 下一個動作
Submit 指令直接回報認證或網路錯誤 上傳/認證 檢查憑據、網路與工具版本
Submission 狀態為 In Progress 公證掃描仍未完成 保存 ID,稍後查詢,不要重複提交
狀態為 Invalid 公證掃描拒絕 讀取 log,按 issues 陣列逐項修復
狀態為 Accepted,但 stapler 失敗 票據附加 檢查檔案是否可寫、格式與工具環境
stapler 成功,但使用者仍被 Gatekeeper 阻擋 本機驗證 用 codesign、spctl 和實際分發檔案重新驗證

notarytool 可以查詢提交狀態和取得日誌。遇到問題時,應先查看 status log,而不是只依賴終端機最後一行訊息。可參考 Apple 的 notarytool 工作流程文件

02 第一步:保存 Submission ID,再讀取 Invalid 的具體日誌

提交時,命令中的檔案路徑、Team ID 與 Keychain Profile 都應使用你自己的值;排查紀錄則要移除帳戶識別資料。

xcrun notarytool submit /PATH/TO/RELEASE_FILE \
  --keychain-profile "KEYCHAIN_PROFILE_NAME" \
  --wait

如果指令已經回傳 Submission ID,先記下它,再執行:

xcrun notarytool info SUBMISSION_ID \
  --keychain-profile "KEYCHAIN_PROFILE_NAME"

查看完整日誌:

xcrun notarytool log SUBMISSION_ID \
  --keychain-profile "KEYCHAIN_PROFILE_NAME" \
  /PATH/TO/NOTARIZATION_LOG.json

日誌中的 statusissuesseveritypathmessage 才是主要證據。若是 Invalid,應先按日誌指出的檔案路徑定位,不要把錯誤歸類成單純「Apple 不接受」。若仍是 In Progress,則代表掃描結果尚未完成或尚未能讀取完整日誌;Apple 官方文件沒有承諾固定處理時間,因此不要用固定分鐘數判斷服務是否異常。(Apple Developer:Notary API)

注意: 公證服務已接受檔案,只能證明上傳請求成立;它不等於軟體已通過公證,也不等於票據已附加到 DMG、PKG 或 ZIP。

03 Developer ID 簽名鏈要從內到外檢查

macOS 站外分發通常需要與用途匹配的 Developer ID 身份。Developer ID Application 用於 App、Plug-in 和其他程式碼;Developer ID Installer 用於安裝套件。不能只查看最外層 .app 的簽名,因為 Framework、XPC Service、Plug-in、命令列工具和第三方二進位檔也可能造成拒絕。

先檢查外層 App:

codesign --verify --deep --strict --verbose=4 \
  /PATH/TO/App.app

再查看實際簽名身份:

codesign -dv --verbose=4 /PATH/TO/App.app

對每個巢狀元件重複執行,不要只依賴 --deep 的單一結果。你需要特別確認:

檢查項目 日誌或命令可能暴露的問題 修復方向
簽名身份 出現 Mac Development、Ad Hoc 或本機開發身份 改用與分發方式匹配的 Developer ID 身份
私鑰 Keychain 找不到私鑰、簽名無法建立 在發布用 Keychain 匯入完整憑證與私鑰
憑證鏈 找不到中繼憑證或驗證鏈不完整 更新並保留 Apple 要求的中繼憑證
安全時間戳 簽名資料缺少 secure timestamp 重新簽名並確認發布命令使用正確參數
內容被修改 簽名後檔案雜湊或封裝內容改變 將所有修改、壓縮和封裝動作放在簽名之前

Apple 的 Developer ID 文件確認,站外分發需要使用相應的 Developer ID 憑證;公證工作流程也要求程式碼具備 Hardened Runtime 與適當的簽名資訊。證書有效性、撤銷狀態和安裝套件所用的身份,會影響後續驗證。

如果你正在做 Developer ID 憑證遷移與 Keychain 安全配置,先確保新環境能看見私鑰,再開始測試公證。否則你可能花時間修復 entitlements,實際上卻是簽名身份根本沒有被正確載入。

04 Hardened Runtime 與 entitlements 不要只看「已啟用」

啟用 Hardened Runtime 只是符合公證要求的起點。真正要檢查的是:App 實際使用的能力,是否與簽名時寫入的 entitlements 一致。

常見問題可分成三類:

  1. 格式錯誤:entitlements 檔案編碼不正確、包含 BOM,或 key 的結構不符合要求。
  2. 不必要的權限:為了讓程式「先跑起來」而加入除錯、JIT 或未簽名可執行記憶體相關權限。
  3. 簽名後被修改:App 已簽名後,腳本、封裝工具或安裝器又更換了二進位檔。

可先檢查簽名內實際載入的 entitlements:

codesign -d --entitlements :- \
  /PATH/TO/App.app

再查看完整簽名資訊:

codesign -dvvv /PATH/TO/App.app

不要把「本機可以啟動」當成公證通過證據。Apple 的常見問題文件指出,格式不正確的 embedded entitlements 可能導致程式碼簽名驗證錯誤;entitlements 檔案本身也必須使用正確的 ASCII 編碼,不能含有 Unicode BOM。(Apple Developer:解決常見公證問題)

05 第二步:按巢狀程式碼逐項驗證

外層 App 簽名通過,不代表內部程式碼合格。建議按以下順序展開:

  • Contents/Frameworks/
  • Contents/PlugIns/
  • Contents/XPCServices/
  • Contents/MacOS/
  • 由 App 啟動的命令列工具
  • 第三方 SDK、原生二進位檔與額外載入模組

對每一個元件執行:

codesign --verify --strict --verbose=4 \
  /PATH/TO/NESTED_OBJECT

若你在簽名完成後才複製 Framework、替換資源、注入設定檔或重新壓縮二進位檔,外層簽名可能仍然存在,但內部完整性已被破壞。這也是「macOS App 已簽名,為什麼仍然無法通過公證」的常見答案:簽名結果只證明某一時刻的內容,不能替你驗證後續封裝是否又修改了檔案。

06 DMG、PKG 和 ZIP 要分開看待

三種格式都可以提交公證,但排查重點不同。Apple 的公證工作流程涵蓋 DMG、平面安裝套件和 ZIP;自訂第三方安裝器則可能需要分別處理內部 App 與外部安裝器。

分發格式 主要檢查對象 常見後續問題
ZIP ZIP 內的 App 與巢狀程式碼 解壓後內容與提交前不一致
DMG DMG 內的 App、映像檔可寫性 公證成功但無法 stapler
PKG App、安裝腳本與 Developer ID Installer 安裝套件簽名身份錯誤或封裝後被修改

因此,不能用「ZIP 成功」推導「DMG 和 PKG 一定成功」。你應對最後實際提供給使用者的檔案執行 stapler 與 Gatekeeper 驗證,而不是只驗證中間產物。

07 公證成功但 stapler 失敗,要先查檔案與工具

如果 notarytool info 顯示公證已接受,但:

xcrun stapler staple /PATH/TO/RELEASE_FILE

仍然失敗,先不要重新簽名。這時問題可能位於票據下載、工具版本、網路連線或目標檔案不可寫,而不是程式碼被公證服務拒絕。

完成後再驗證:

xcrun stapler validate /PATH/TO/RELEASE_FILE

若是 DMG,確認映像檔沒有以唯讀或鎖定方式掛載;若是 PKG,確認目前帳戶有權修改檔案;若是 ZIP,確認你操作的是最終發布檔,而不是尚未完成封裝的中間檔。Apple 的公證問題文件也把舊版工具、磁碟映像檔不可寫和網路問題列為需要分開處理的範圍。

08 Gatekeeper 驗證是最後一關,不是公證日誌的替代品

完成 stapler 後,在接近使用者實際環境的 Mac 上執行:

spctl --assess --type execute --verbose=4 \
  /PATH/TO/App.app

對安裝套件則使用相應的評估類型。你要同時保留:

  • codesign --verify 的結果。
  • notarytool infonotarytool log
  • stapler validate 的結果。
  • spctl 的 Gatekeeper 評估結果。

Gatekeeper 會檢查站外分發軟體的 Developer ID 資訊;票據可以附加在可執行檔上,也可能由 Gatekeeper 在線上尋找。因此,「公證成功」與「使用者端能正常開啟」必須分開驗證。

09 遠端 Mac 的公證環境,按條件決定是否可交付

如果你只偶爾發布一次,短期使用現有 Mac 修復問題通常足夠。如果你每週或每天都要簽名、公證和發布,就應把憑證、Keychain、Command Line Tools、腳本與日誌保存方式固化在同一台可持續運行的 Mac 上。

使用下面的決策條件:

  • 若只需要一次性修復,且本地 Mac 能穩定保留私鑰與 Keychain Profile,則先在本地完成驗收。
  • 若本地 Mac 會睡眠、多人共用、憑證經常遺失,則改用隔離的遠端 Mac 發布環境。
  • 若需要長時間執行 CI,且每次都要保留 Submission ID、JSON 日誌與 stapler 結果,則把發布腳本和工具鏈固定在可持續運行的主機。
  • 若團隊要求實體 USB、特定硬體或本機互動式除錯,則不要只依賴遠端 Mac;保留本地設備作為補充。

在遠端環境保存憑據時,不要把 Issuer ID、Key ID、私鑰內容或 Keychain 密碼直接寫進 Git。優先使用 notarytool 支援的 Keychain Profile,並限制登入帳戶、檔案權限及遠端連線權限。Apple 的遷移文件說明,notarytool 可使用 Keychain 儲存的憑據,並可透過 Command Line Tools 取得,不一定要在每台主機安裝完整 Xcode。(Apple Developer:遷移至最新公證工具)

若你需要把這套流程長期化,可再查看 遠端 Mac 自動構建 macOS AppmacOS 發布伺服器驗收方向。重點不是「有沒有一台 Mac」,而是每次發布是否都能重現相同的簽名、提交、日誌、票據和 Gatekeeper 驗證結果。

10 發布前可勾選的最小驗收卡

  • [ ] xcode-select 指向預期的 Xcode 或 Command Line Tools。
  • [ ] xcrun notarytool 可以正常執行。
  • [ ] App 使用 Developer ID Application 身份。
  • [ ] PKG 使用 Developer ID Installer 身份。
  • [ ] 所有 Framework、Plug-in、XPC Service 和命令列工具都已驗證。
  • [ ] Hardened Runtime 與 entitlements 和實際功能一致。
  • [ ] 簽名包含安全時間戳。
  • [ ] 已保存 Submission ID。
  • [ ] 已下載並保存脫敏後的公證 JSON 日誌。
  • [ ] 最終 DMG、PKG 或 ZIP 已完成 stapler 驗證。
  • [ ] 已在測試 Mac 上取得 spctl 評估結果。
  • [ ] 憑據沒有出現在原始碼庫、公開 CI 輸出或聊天紀錄中。

如果你目前的方案是偶爾借用同事的 Mac、依賴會睡眠的本地電腦,或把憑證和發布腳本散落在多台主機,常見缺點是環境不一致、Keychain 難以持久保存,以及失敗後缺少完整日誌。對需要重複發布的獨立開發者而言,CALMVPS 的遠端 Mac 能提供一個長時間在線、可保留工具鏈與發布腳本的 macOS 環境;但若你是長期滿載編譯、需要實體介面,或已經擁有穩定本地 Mac,直接自購設備可能更合適。你可以先完成上面的驗收卡,再判斷租用遠端 Mac 是否能真正解決目前的公證維護成本。