用 Developer ID 自主分發意味著什麼

將 macOS 應用程式交付給使用者,大致有兩條路。一條是透過 Mac App Store (MAS),另一條是由開發者自行製作 .dmg(或 .app)檔案,讓使用者從網站或 GitHub 等地方下載的直接分發 (direct distribution)

直接分發有明顯的優勢:不必等待 App Store 審核,沒有支付手續費,可以隨時以自己想要的方式發布更新。但相應地,原本由 App Store 代勞的事情——程式碼簽署公證 (notarization)自動更新——都需要開發者自行建置。

如果不建置這些,macOS 的安全機制 Gatekeeper 會阻止應用程式啟動。使用者第一次開啟應用程式時會看到類似「無法開啟此 App,因為它來自身份不明的開發者」的警告,大多數使用者就此放棄安裝。要讓應用程式像正規分發的應用程式那樣雙按即可開啟,就必須用 Developer ID 憑證簽署應用程式,並獲得 Apple 的公證

本系列介紹的正是這套一次性準備流程。好消息是,這裡涉及的設定大多只需做一次,即可在所有後續發布中重複使用

本系列將完成的內容

共三篇,逐步建置以下內容:

  • (第 1 篇,本文) Developer ID Application 憑證 + 公證用憑據
  • (第 2 篇) 用於 Sparkle 自動更新的 EdDSA 簽署金鑰
  • (第 3 篇) 託管更新 Feed 的公開儲存庫 + 收尾建置設定

系列結束時,你應該已經準備好以下內容。現在先瀏覽一下清單,每篇逐一完成即可。

  • macOS 鑰匙圈 (Keychain) 中的 Developer ID Application 憑證
  • 存儲在鑰匙圈中的公證用憑據設定檔
  • 用於 Sparkle 更新簽署的 EdDSA 金鑰對(公開金鑰 + 私密金鑰)
  • 私密金鑰備份到安全位置
  • 用於託管更新 Feed (appcast) 的公開 GitHub 儲存庫 + GitHub Pages
  • 建置設定檔 (ExportOptions.plist) 與應用程式端設定

範例應用程式 — FocusTimer

本系列以虛構的 macOS 應用程式 FocusTimer(一款管理專注時間的簡單計時器應用程式)為例。指令和路徑中出現的 FocusTimer、Bundle Identifier com.example.FocusTimer、網域名稱 example.com、憑證名稱、Team ID 等均為範例值。實際操作時請替換為你自己的應用程式名稱和帳戶資訊。

本文基於 Xcode 26Sparkle 2.x(自動更新框架,第 2 篇中會用到)編寫。Apple 時常調整介面佈局和選單位置,若按鈕名稱略有不同,整體流程不變。

前提條件 — 命令列工具

首先安裝建置和發布自動化所需的兩個命令列工具。前提是已安裝 macOS 套件管理器 Homebrew

brew install gh create-dmg
  • gh — GitHub 官方 CLI,稍後用於建立 GitHub Release。
  • create-dmg — 用於製作分發用 .dmg 磁碟映像檔的工具,同時產生引導使用者將應用程式拖入 Applications 資料夾的介面。

雖然現在還用不到,但發布自動化腳本通常會在開頭檢查這些工具是否存在,找不到就立即終止,因此提前安裝好。

第 1 步 — 確認 Apple Developer Program

要申請 Developer ID 憑證並對應用程式進行公證,需要 Apple Developer Program 會員資格。個人帳戶每年收費 $99。

如果已加入,只需確認狀態是否為活躍即可。

  1. 訪問 developer.apple.com/account
  2. Membership details 部分確認狀態為 Active
  3. 在同一頁面記下你的 Team ID(範例中為 ABCDE12345),後續步驟中會反覆用到。

若尚未加入,會員資格審批通常需要約一天時間。審批完成前無法申請憑證,建議優先處理。

第 2 步 — 申請 Developer ID Application 憑證

Developer ID Application 憑證是對分發用 .app.dmg 進行簽署時使用的憑證。macOS Gatekeeper 會將用此憑證簽署的應用程式識別為「已知開發者製作的應用程式」。

Apple 還有一種用於對 .pkg 安裝套件簽署的 Developer ID Installer 憑證,兩者不同。本系列以 .dmg 方式分發,因此只涉及 Application 憑證。

申請流程

最簡單的方式是透過 Xcode 操作。

  1. 啟動 Xcode → 選單 Xcode → Settings… (⌘,)
  2. 選擇 Accounts 標籤 → 在左側清單中點按你的 Apple ID
  3. 點按右下角的 Manage Certificates… 按鈕
  4. 在新彈出的視窗左下角點按 + 按鈕
  5. 從選單中選擇 Developer ID Application
  6. 一兩秒後新憑證出現在清單中,點按 Done

如此申請的憑證會自動儲存到 macOS 鑰匙圈 (Keychain) 中。憑證與對應的私密金鑰成對儲存,有了這把私密金鑰才能進行簽署。

確認申請結果

在終端機執行以下指令,確認憑證是否已正確安裝。

security find-identity -v -p codesigning | grep "Developer ID Application"

如果輸出如下一行,則表示成功。

  1) A1B2C3D4E5F6...  "Developer ID Application: Gildong Hong (ABCDE12345)"

引號內的字串是憑證的正式名稱。Gildong Hong 是註冊在 Apple 帳戶中的姓名,括號內的 ABCDE12345 是第 1 步記下的 Team ID。這個名稱在後續自動化程式碼簽署時會原樣使用,請再次核對。

憑證續期

Developer ID 憑證的有效期限為 5 年。臨近到期時,Xcode 的 Manage Certificates 清單中會顯示 Expired。屆時按照相同步驟重新申請新憑證即可。

好消息是,已用舊憑證簽署並分發的應用程式不會在憑證到期時立即失效。只有之後新建的建置才需要用新憑證簽署。因此無需過分擔心到期問題。

第 3 步 — 註冊用於公證的 App 專用密碼

什麼是公證 (notarization)

公證是在分發前將應用程式上傳至 Apple 伺服器進行惡意軟體掃描的流程。通過掃描後,Apple 會頒發「公證票據」,應用程式附帶此票據後,Gatekeeper 才會無警告地開啟應用程式。如果說簽署證明「誰製作了它」,那麼公證就是「Apple 已掃描過一次」的獨立證明流程。

公證提交使用 Apple 的 notarytool 指令,每次提交都會要求輸入 Apple ID 密碼。為避免每次手動輸入,需要建立一個 App 專用密碼 (App-Specific Password) 並提前存入鑰匙圈。

3-1. 申請 App 專用密碼

App 專用密碼是一種 16 碼密碼,用於替代主 Apple ID 密碼,僅供特定用途使用。

  1. 訪問 appleid.apple.com → 使用加入了 Apple Developer Program 的 Apple ID 登入(範例中為 [email protected]
  2. 登入與安全性 (Sign-In and Security) → 選擇 App 專用密碼 (App-Specific Passwords)
  3. 點按 + 按鈕 → 輸入密碼名稱(如 focustimer-notarize
  4. 點按 建立 (Create) → 再次輸入 Apple ID 密碼
  5. 螢幕上顯示 abcd-efgh-ijkl-mnop 格式的 16 碼密碼。

關閉此頁面後將無法再次查看該密碼。 在進入下一步之前,請先將其複製到密碼管理工具等安全位置。

3-2. 存入 notarytool 設定檔

將申請到的密碼儲存為鑰匙圈設定檔。執行以下指令前,請將 --password 的值替換為剛才獲得的實際密碼。

xcrun notarytool store-credentials "focustimer-notarize" \
  --apple-id "[email protected]" \
  --team-id "ABCDE12345" \
  --password "abcd-efgh-ijkl-mnop"
  • 第一個參數 "focustimer-notarize" — 為此設定檔指定的名稱,之後公證時將透過此名稱載入憑據。
  • --apple-id — Apple Developer Program 帳戶的電子郵件
  • --team-id — 第 1 步中的 Team ID
  • --password — 3-1 中申請的 App 專用密碼

出現如下輸出即為成功。

Credentials saved to Keychain.

憑據已存入鑰匙圈,App 專用密碼的原始文字不再需要保留。(但若計劃在其他電腦上也進行建置,建議保存在密碼管理工具中以備用。)

3-3. 驗證

確認已儲存的設定檔是否實際可用。

xcrun notarytool history --keychain-profile "focustimer-notarize"

若出現 Successfully received submission history. 的訊息,則表示正常。其下的提交歷程可能為空(因為尚未公證過任何內容,這是正常的),也可能有之前的記錄。

關鍵陷阱 — Apple ID 與 GitHub 帳戶是不同的

這是首次設定直接分發時最容易卡住的地方。

  • Apple Developer Program 帳戶 — 用於申請憑證和公證(範例:[email protected]
  • GitHub 帳戶 — 第 3 篇中用於託管更新檔案(範例:[email protected]

兩者通常使用不同的電子郵件。特別注意:notarytool store-credentials--apple-id 必須填寫 Apple Developer 帳戶電子郵件。若填寫 GitHub 帳戶電子郵件,認證會失敗,而且錯誤訊息不夠友善,難以找到原因。

若兩個帳戶的電子郵件容易混淆,建議記錄下哪個是 Apple 用、哪個是 GitHub 用。這一區別在本系列中會反覆出現。

第 1 篇小結

跟到這裡,你現在已經準備好以下內容:

  • ✅ 發布自動化用命令列工具(ghcreate-dmg
  • ✅ 活躍狀態的 Apple Developer Program 會員資格
  • ✅ 存儲在鑰匙圈中的 Developer ID Application 憑證
  • ✅ 存儲在鑰匙圈中的公證用憑據設定檔focustimer-notarize

至此,簽署公證應用程式的準備工作已完成。但幾乎沒有應用程式只發布一次就結束。你需要不斷推出修復錯誤、增加功能的新版本。如果是 App Store 應用程式,自動更新由 App Store 代勞;而直接分發則需要自行建置這一機制。

下一篇將為 macOS 應用程式事實上的標準自動更新框架 Sparkle 建立 EdDSA 簽署金鑰。這是獨立於憑證之外、專門用於驗證更新檔案的機制。