公証がThe binary uses an SDK older than the 10.9 SDK.で失敗したときは、バイナリのビルドバージョンをvtoolで確認する。vtool -show-buildで、バイナリのデプロイメントターゲット(minos)とビルドに使ったSDK(sdk)が表示される。

$ xcrun vtool -show-build [バイナリ名]

ビルドし直せないバイナリでは、vtool -set-build-versionでSDKのバージョンを書き換えて、署名し直せば公証が通る。

実行例はXcode 27.0、macOS 27.0.1のもの。

ビルドバージョンを確認する (-show-build)

vtool -show-buildは、Mach-OバイナリのLC_BUILD_VERSIONを表示する。

$ xcrun vtool -show-build [バイナリ名]
[バイナリ名]:
Load command 10
      cmd LC_BUILD_VERSION
  cmdsize 32
 platform MACOS
    minos 27.0
      sdk 27.0
   ntools 1
     tool LD
  version 27037.1

主な項目は次のとおりである。

項目内容
platform対象のプラットフォーム。MACOSなど
minos動作する最小のOSバージョン(デプロイメントターゲット)
sdkビルドに使ったSDKのバージョン
toolバイナリを作ったツールとバージョン。LDはリンカー

Intel MacとApple siliconの両方に対応したユニバーサルバイナリでは、アーキテクチャごとに表示される。

$ xcrun vtool -show-build [ユニバーサルバイナリ名]
[ユニバーサルバイナリ名] (architecture x86_64):
Load command 10
      cmd LC_BUILD_VERSION
  cmdsize 32
 platform MACOS
    minos 27.0
      sdk 27.0
   ntools 1
     tool LD
  version 27037.1
[ユニバーサルバイナリ名] (architecture arm64):
Load command 10
      cmd LC_BUILD_VERSION
  cmdsize 32
 platform MACOS
    minos 27.0
      sdk 27.0
   ntools 1
     tool LD
  version 27037.1

-archでアーキテクチャを絞り込める。

$ xcrun vtool -arch arm64 -show-build [ユニバーサルバイナリ名]

Mach-Oではないファイルを指定すると、エラーメッセージが表示されて終了コードが1になる。

$ xcrun vtool -show-build /etc/hosts
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/vtool error: /etc/hosts file is not mach-o
$ echo $?
1

-showを指定すると、ビルドバージョン以外のロードコマンドも表示される。

公証で「SDKが古い」と言われる

公証に提出したバイナリのSDKが10.9より古いと、公証がInvalidになる。notarytool logで取得したログのissuesには、次のメッセージが記録される。

{
  "status": "Invalid",
  "statusSummary": "Archive contains critical validation errors",
  "statusCode": 4000,
  "archiveFilename": "[バイナリ名].zip",
  "ticketContents": null,
  "issues": [
    {
      "severity": "error",
      "code": null,
      "path": "[バイナリ名].zip/[ディレクトリ名]/[バイナリ名]",
      "message": "The binary uses an SDK older than the 10.9 SDK.",
      "docUrl": "https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/resolving_common_notarization_issues#3087723",
      "architecture": "arm64"
    }
  ]
}

参考: 【notarytool】公証に失敗した理由をログから調べる

Appleのドキュメントによると、macOS 10.9より前はコード署名の仕組みが大きく異なるため、公証は10.9以降のSDKでリンクされたバイナリにだけ対応する。新しいSDKでビルドしても、以前のmacOSとの互換性には影響しない。互換性はデプロイメントターゲットで決まる。

参考: 公証に関するよくある問題の解決

minosとsdkのどちらが判定に使われるか

minosとsdkの組み合わせを変えたバイナリを、Developer IDで署名して公証に提出した結果は次のとおりである。

minossdk公証の結果
27.027.0Accepted
10.827.0Accepted
27.010.8Invalid
10.810.8Invalid

minosが10.8でも、sdkが新しければ公証が通る。sdkが10.8であれば、minosによらず失敗する。判定に使われるのはsdkである。

SDKが古いバイナリに対処する

新しいSDKでビルドし直す

自分でビルドしたバイナリであれば、新しいXcodeでビルドし直す。古いmacOSもサポートする場合は、デプロイメントターゲットで下限を指定する。

vtoolでSDKを書き換える

サードパーティのバイナリなどビルドし直せない場合は、vtool -set-build-versionでSDKのバージョンを書き換える。引数は[プラットフォーム] [minos] [sdk]の順である。出力先は-outputで指定する。

$ xcrun vtool -set-build-version macos 10.8 10.9 -output [出力先のバイナリ名] [入力のバイナリ名]
$ xcrun vtool -show-build [出力先のバイナリ名] | grep -E "minos|sdk"
    minos 10.8
      sdk 10.9

SDKは10.9を指定すれば公証が通る。sdkが10.9のバイナリは、Developer IDで署名して提出するとAcceptedになる。

Developer IDなどで署名済みのバイナリをvtoolで書き換えると、署名が無効になる。vtoolは書き換え時に警告を表示する。

$ xcrun vtool -set-build-version macos 10.8 10.8 -output [出力先のバイナリ名] [署名済みのバイナリ名]
.../vtool warning: code signature will be invalid for [出力先のバイナリ名]
$ codesign -v [出力先のバイナリ名]
[出力先のバイナリ名]: invalid signature (code or signature have been modified)
In architecture: arm64

署名が無効になったバイナリは、実行すると強制終了される。終了コードは137(128にSIGKILLのシグナル番号9を足した値)である。

$ ./[出力先のバイナリ名]
$ echo $?
137

書き換えた後は、codesignで署名し直す。公証に提出するには、Hardened Runtimeの有効化とセキュアタイムスタンプが必要である。

$ codesign --force --options runtime --timestamp -s "[署名ID]" [出力先のバイナリ名]
[出力先のバイナリ名]: replacing existing signature
$ codesign -v [出力先のバイナリ名]
$ echo $?
0

署名し直したバイナリをzipにして、公証に提出する。ファイルを--keepParentでzipにすると、親ディレクトリの名前もzip内のパスに含まれる。

$ ditto -c -k --keepParent [出力先のバイナリ名] [出力先のバイナリ名].zip
$ xcrun notarytool submit [出力先のバイナリ名].zip --keychain-profile "[プロファイル名]" --wait
...
  status: Accepted

参考: 【notarytool】アプリを公証してstaple(公証チケットを添付)する

vtoolが変更するのは、ロードコマンドに記録されたバージョン情報である。新しいSDKでリンクし直したわけではないため、新しいSDKに依存する挙動は有効にならない。ビルドし直せるバイナリは、ビルドし直す方が望ましい。

clangがリンク時に付けるad-hoc署名(linker-signed)だけのバイナリでは、vtoolが署名を付け直すため、警告は表示されない。公証に提出するには、いずれの場合もDeveloper IDで署名し直す。

vtoolの書き換えオプション

-set-build-version以外にも、バージョン情報を操作するオプションがある。

オプション内容
-set-build-version [プラットフォーム] [minos] [sdk]LC_BUILD_VERSIONを書き換える
-set-version-min [プラットフォーム] [minos] [sdk]古い形式のLC_VERSION_MIN_MACOSXなどに書き換える
-remove-build-version [プラットフォーム]LC_BUILD_VERSIONを削除する
-arch [アーキテクチャ]ユニバーサルバイナリの指定したアーキテクチャだけを対象にする
-output [出力先]出力先のファイル。書き換えでは必須

-outputを省略すると、error: one output file must be specifiedで失敗する。入力と同じファイルを-outputに指定すると、上書きできる。

ユニバーサルバイナリは、-archで指定したアーキテクチャだけが書き換わる。

$ xcrun vtool -arch x86_64 -set-build-version macos 10.8 10.8 -output [出力先のバイナリ名] [ユニバーサルバイナリ名]
$ xcrun vtool -show-build [出力先のバイナリ名] | grep -E "architecture|minos|sdk"
[出力先のバイナリ名] (architecture x86_64):
    minos 10.8
      sdk 10.8
[出力先のバイナリ名] (architecture arm64):
    minos 27.0
      sdk 27.0

-set-version-minで書き換えたバイナリには、LC_BUILD_VERSIONの代わりにLC_VERSION_MIN_MACOSXが記録される。-show-buildでは次のように表示される。

$ xcrun vtool -set-version-min macos 10.8 10.8 -output [出力先のバイナリ名] [入力のバイナリ名]
$ xcrun vtool -show-build [出力先のバイナリ名]
[出力先のバイナリ名]:
Load command 16
      cmd LC_VERSION_MIN_MACOSX
  cmdsize 16
  version 10.8
      sdk 10.8