Notarize a Command Line Tool
經(jīng)過(guò)初淺的Google 翻譯:
macOS 10.15 Catalina 要求 應(yīng)用程序和工具 需要經(jīng)過(guò)公證。
Developer ID Certificates
要在App Store之外分發(fā)二進(jìn)制文件(應(yīng)用程序和命令行工具),您需要“開(kāi)發(fā)者ID應(yīng)用程序”證書(shū)。要簽署安裝程序包以在Mac App Store之外分發(fā),您需要提供“開(kāi)發(fā)人員ID安裝程序”證書(shū)。
如果尚未創(chuàng)建它們,則可以在Xcode或Developer Portal中進(jìn)行。如果您已經(jīng)擁有證書(shū),但在另一臺(tái)Mac上,則需要導(dǎo)出它們,然后在新Mac上重新導(dǎo)入它們。創(chuàng)建新證書(shū)可能會(huì)使現(xiàn)有證書(shū)無(wú)效! 所以要當(dāng)心。
在工作計(jì)算機(jī)上創(chuàng)建或?qū)胱C書(shū)后,您可以使用以下方法在終端中驗(yàn)證證書(shū)的存在:
$ security find-identity -p basic -v
此命令將列出此Mac上的所有可用證書(shū)。檢查您是否可以看到“開(kāi)發(fā)人員ID應(yīng)用程序”和“開(kāi)發(fā)人員ID安裝程序”證書(shū)。如果您是多個(gè)團(tuán)隊(duì)的成員,則每個(gè)團(tuán)隊(duì)可能會(huì)看到多個(gè)證書(shū)。
您以后可以通過(guò)長(zhǎng)十六進(jìn)制數(shù)字或描述性名稱(chēng)來(lái)標(biāo)識(shí)證書(shū)(或“身份”),例如 "Developer ID Installer: Armin Briegel (ABCD123456)"
名稱(chēng)末尾的十個(gè)字符代碼是您的開(kāi)發(fā)人員團(tuán)隊(duì)ID。記下它。如果您是多個(gè)開(kāi)發(fā)人員團(tuán)隊(duì)的成員,則可以擁有多個(gè)開(kāi)發(fā)人員ID證書(shū),團(tuán)隊(duì)ID將幫助您區(qū)分它們。
您的開(kāi)發(fā)者帳戶(hù)的應(yīng)用專(zhuān)用密碼
Apple要求使用兩因素身份驗(yàn)證保護(hù)開(kāi)發(fā)者帳戶(hù)。要允許需要身份驗(yàn)證的自動(dòng)化工作流,您可以創(chuàng)建應(yīng)用程序特定的密碼。
在Apple ID門(mén)戶(hù)中為您的開(kāi)發(fā)人員帳戶(hù)創(chuàng)建一個(gè)新的應(yīng)用程序?qū)S妹艽a。
- Apple支持:使用應(yīng)用專(zhuān)用密碼
創(chuàng)建密碼時(shí),只會(huì)顯示密碼。立即在您的鑰匙串中使用以下字段創(chuàng)建“新密碼項(xiàng)”:
- 鑰匙扣物品名稱(chēng): Developer-altool
- 帳戶(hù)名稱(chēng):您的開(kāi)發(fā)者帳戶(hù)電子郵件
- 密碼:您剛創(chuàng)建的應(yīng)用專(zhuān)用密碼
這將創(chuàng)建一個(gè)開(kāi)發(fā)人員專(zhuān)用的密碼項(xiàng)目,我們可以從工具中安全地訪問(wèn)該項(xiàng)目。
如果需要,您還可以將應(yīng)用程序特定的密碼存儲(chǔ)在其他密碼管理器中,但是Xcode工具具有使用“鑰匙串”的特殊選項(xiàng)。
啟用強(qiáng)化運(yùn)行時(shí)
啟用“ Hardened Runtime”將以某種方式編譯二進(jìn)制文件,從而使外部進(jìn)程更難以注入代碼。從2020年1月開(kāi)始,這是成功進(jìn)行公證的要求。
- 在更改簽名選項(xiàng)的視圖中,單擊上方選項(xiàng)卡行中的“構(gòu)建設(shè)置”
- 單擊“全部”以顯示所有可用設(shè)置
- 在搜索字段中輸入“ enable hardened”,這將顯示“ Enable Hardened Runtime”設(shè)置
- 將項(xiàng)目列(藍(lán)色圖標(biāo))中的值設(shè)置為
YES

建立pkg
命令行工具可以簽名,但不能直接公證。但是,您可以對(duì)包含命令行工具的zip,dmg或pkg文件進(jìn)行公證。同樣,當(dāng)您的工具帶有正確的安裝包時(shí),對(duì)于用戶(hù)和管理員來(lái)說(shuō),安裝起來(lái)也容易得多。
我們可以使用pkgroot目錄作為有效負(fù)載來(lái)構(gòu)建安裝程序包:
pkgbuild --root build/pkgroot \
--identifier "com.example.hello" \
--version "1.0" \
--install-location "/" \
--sign "Developer ID Installer: Armin Briegel (ABCD123456)" \
build/hello-1.0.pkg
為了清楚起見(jiàn),我將命令分為多行,您可以在一行中輸入該命令,而無(wú)需使用行尾反斜杠\。您想用數(shù)據(jù)替換標(biāo)識(shí)符,版本和簽名證書(shū)的值。
對(duì)安裝程序包進(jìn)行公證
Xcode有一個(gè)命令行工具altool,您可以使用該工具上載工具以進(jìn)行公證:
xcrun altool --notarize-app \
--primary-bundle-id "com.example.com" \
--username "username@example.com" \
--password "@keychain:Developer-altool" \
--asc-provider "ABCD123456" \
--file "build/hello-1.0.pkg"
這username是您的開(kāi)發(fā)者帳戶(hù)電子郵件。
該asc-provider是你的十位數(shù)組ID。如果您只是單個(gè)團(tuán)隊(duì)的成員,則無(wú)需提供此信息。
密碼使用一個(gè)特殊的@keychain:關(guān)鍵字,該關(guān)鍵字告訴altool您從名為的鑰匙串項(xiàng)中獲取應(yīng)用程序?qū)S玫拿艽aDeveloper-altool。(還記得我們之前創(chuàng)建的嗎?)
這需要一段時(shí)間。當(dāng)命令成功將pkg上傳到Apple的公證服務(wù)器時(shí),它將返回RequestUUID。您的公證請(qǐng)求將被排隊(duì),并最終得到處理。您可以使用以下方法檢查請(qǐng)求的狀態(tài):
xcrun altool --notarization-info "Your-Request-UUID" \
--username "username@example.com" \
--password "@keychain:Developer-altool"
該過(guò)程完成后,Apple還將向您的開(kāi)發(fā)人員帳戶(hù)發(fā)送電子郵件。我的經(jīng)驗(yàn)很少會(huì)花費(fèi)一兩分鐘以上的時(shí)間。(在中歐時(shí)區(qū)可能是一個(gè)優(yōu)勢(shì))。該過(guò)程完成后,您可以運(yùn)行上面的notarization-info命令以獲取一些詳細(xì)信息。該信息將包含一個(gè)包含更多信息的鏈接,當(dāng)您的請(qǐng)求被拒絕時(shí),該鏈接將非常有用。
請(qǐng)注意,信息鏈接會(huì)在24小時(shí)左右后失效。您應(yīng)該復(fù)制任何您想要保留的信息。
驗(yàn)證成功后的郵件

完成流程
除了確認(rèn)或拒絕您的請(qǐng)求外,您不會(huì)從Apple得到任何回報(bào)。Mac下載您的安裝程序包并驗(yàn)證其公證狀態(tài)時(shí),它將與Apple的公證服務(wù)器聯(lián)系,他們將確認(rèn)或拒絕該狀態(tài)。
如果Mac當(dāng)前處于脫機(jī)狀態(tài),或者位于阻止訪問(wèn)Apple服務(wù)器的代理或防火墻之后,則它無(wú)法驗(yàn)證您的pkg文件是否已經(jīng)過(guò)公證。
但是,您可以將公證單“裝訂”到pkg文件,因此客戶(hù)端不需要連接到服務(wù)器:
xcrun stapler staple build/hello-1.0.pkg
您還可以stapler用來(lái)驗(yàn)證過(guò)程是否順利:
xcrun stapler validate build/hello-1.0.pkg
但由于stapler取決于要安裝的開(kāi)發(fā)人員工具,因此通常應(yīng)首選spctl檢查公證:
spctl --assess -vvv --type install build/hello-1.0.pkg
流程自動(dòng)化
顯然,我構(gòu)建了一個(gè)腳本來(lái)自動(dòng)化所有這些。將以下腳本放在項(xiàng)目文件夾的根目錄中,使用您的信息在腳本的開(kāi)頭(第20-38行)修改變量,然后運(yùn)行它。
該腳本將構(gòu)建該工具,創(chuàng)建一個(gè)已簽名的pkg,將其上傳以進(jìn)行公證,等待結(jié)果,然后裝訂pkg。
#!/bin/zsh
# pkgAndNotarize.sh
# 2019 - Armin Briegel - Scripting OS X
# place a copy of this script in in the project folder
# when run it will build for installation,
# create a pkg from the product,
# upload the pkg for notarization and monitor the notarization status
# before you can run this script:
# - set release signing of the tool to 'Developer ID Application'
# - enable the hardened run-time
# - change the 'Installation Build Products Location' to `$SRCROOT/build/pkgroot`
#
# you want to add the `build` subdirectory to gitignore
# put your dev account information into these variables
# the email address of your developer account
dev_account="user@example.com"
# the name of your Developer ID installer certificate
signature="Developer ID Installer: First Last (ABCD123456)"
# the 10-digit team id
dev_team="ABCD123456"
# the label of the keychain item which contains an app-specific password
dev_keychain_label="Developer-altool"
# put your project's information into these variables
version="1.0"
identifier="com.example.hello"
productname="Hello"
# code starts here
projectdir=$(dirname $0)
builddir="$projectdir/build"
pkgroot="$builddir/pkgroot"
# functions
requeststatus() { # $1: requestUUID
requestUUID=${1?:"need a request UUID"}
req_status=$(xcrun altool --notarization-info "$requestUUID" \
--username "$dev_account" \
--password "@keychain:$dev_keychain_label" 2>&1 \
| awk -F ': ' '/Status:/ { print $2; }' )
echo "$req_status"
}
notarizefile() { # $1: path to file to notarize, $2: identifier
filepath=${1:?"need a filepath"}
identifier=${2:?"need an identifier"}
# upload file
echo "## uploading $filepath for notarization"
requestUUID=$(xcrun altool --notarize-app \
--primary-bundle-id "$identifier" \
--username "$dev_account" \
--password "@keychain:$dev_keychain_label" \
--asc-provider "$dev_team" \
--file "$filepath" 2>&1 \
| awk '/RequestUUID/ { print $NF; }')
echo "Notarization RequestUUID: $requestUUID"
if [[ $requestUUID == "" ]]; then
echo "could not upload for notarization"
exit 1
fi
# wait for status to be not "in progress" any more
request_status="in progress"
while [[ "$request_status" == "in progress" ]]; do
echo -n "waiting... "
sleep 10
request_status=$(requeststatus "$requestUUID")
echo "$request_status"
done
# print status information
xcrun altool --notarization-info "$requestUUID" \
--username "$dev_account" \
--password "@keychain:$dev_keychain_label"
echo
if [[ $request_status != "success" ]]; then
echo "## could not notarize $filepath"
exit 1
fi
}
# build clean install
echo "## building with Xcode"
xcodebuild clean install -quiet
# check if pkgroot exists where we expect it
if [[ ! -d $pkgroot ]]; then
echo "couldn't find pkgroot $pkgroot"
exit 1
fi
## build the pkg
pkgpath="$builddir/$productname-$version.pkg"
echo "## building pkg: $pkgpath"
pkgbuild --root "$pkgroot" \
--version "$version" \
--identifier "$identifier" \
--sign "$signature" \
"$pkgpath"
# upload for notarization
notarizefile "$pkgpath" "$identifier"
# staple result
echo "## Stapling $pkgpath"
xcrun stapler staple "$pkgpath"
echo '## Done!'
# show the pkg in Finder
open -R "$pkgpath"
exit 0
鏈接和視頻
這些鏈接和視頻,特別是霍華德·奧克利(Howard Oakley)的帖子以及湯姆·布里奇(Tom Bridge)的PSU演示文稿,都非常有用。也要感謝同事阿諾德(Arnold)向我展示了這一點(diǎn)甚至是可能的。
- 霍華德·奧克利(Howard Oakley):為Catalina構(gòu)建和交付命令工具
- 湯姆·布里奇(Tom Bridge):PSU會(huì)議討論與跟進(jìn)
- Apple支持:為macOS Catalina公證Mac軟件
- Apple支持:使用應(yīng)用專(zhuān)用密碼
- Apple Developer:分發(fā)前對(duì)您的應(yīng)用程序進(jìn)行公證
- WWDC 2019:關(guān)于公證的一切
- WWDC 2019:macOS安全性方面的進(jìn)步
- Apple Developer:解決常見(jiàn)的公證問(wèn)題
- Apple Developer: Customizing the Notarization Workflow