2012年7月13日金曜日

AppleScript や Automator の CodeSign

Mac OS X 10.8 (Mountain Lion)からは、Appleから認証されたDeveloper IDのCode Sign のないアプリケーションをダウンロードするとデフォルトの状態では起動できない。

通常、Developer IDのCode Signは、Xcode のbuild 時に付加される。


ところで、AppleScript のアプリケーションや Automatorのワークフローも Developer IDのCode Sign がついていないと同じように起動できない。
ところが、これらのアプリケーションの作成には Xcode は使わない。AppleScript のアプリケーションは AppleScript Editor で、Automatorのワークフローは Automator.app で作成する。

では、どうやってAppleScript や Automator に CodeSigin を付加するのか。以下のようにすると Xcodeを使わず codesign コマンドを使って直接アプリケーションに CodeSign を付加することができる。

AppleScript アプリケーション

1) CodeSignのためには、アプリケーションパッケージ内の info.plist のなかに Bundle Identifier が定義されていなければならない。AppleScriptアプリケーションには一般にBundle Identifierが定義されていないので、以下のようなBundle Identifierを追加する。

           <key>CFBundleIdentifier</key>
            <string>jp.co.yourCompany.applescript.foo</string>

Bundle Identifierの追加はテキストエディタとかplistエディタを使う。なお、Mountain LionのAppleScriptエディタには新たにBundle Identifierの編集機能が追加されているので、この機能を使っても行える。

2) アプリケーションパッケージ内のmain.scpt を chmod コマンドで書き込み禁止にする。

$ chmod a-w foo.app/Contents/Resources/Scripts/main.scpt

3 ) codesign コマンドで CodeSign を書き込む。

$ codesign --sign 'Developer ID Application: YourCompany.' 
-i jp.co.yourCompany.applescript.foo -f foo.app 

4 )うまく CodeSign が付加されたかどうかは以下のコマンドで確認できる。

$ codesign --display -vvv foo.app

Automator ワークフロー

1) Bundle Identifier の修正。Automator のワークフローのパッケージ内の info.plist には、すでに Bundle Identifier が備わっているので、それを希望の文字列、たとえば
jp.co.yourCompany.automator.foo
に書き換える。

2) codesign コマンドで CodeSign を書き込む。

$ codesign --sign 'Developer ID Application: YourCompany.' 
-i jp.co.yourCompany. automator.foo -f foo.app 

3)うまく CodeSign が付加されたかどうかは以下のコマンドで確認できる。

$ codesign --display -vvv foo.app


Xcode 3.2.6 と Gate Keeper

Mac OS X 10.8 (Mountain Lion)では、ダウンロードされた不正なアプリからシステムを防御するため Gate Keeper という機能が新設された。デフォルトのシステム設定では、Appleから認証されたCode Sign (Mac Developer ID)が付加されていないアプリをダウンロードし起動しようとすると、以下のようアラートが出て起動することができない。


というわけで、Mac OS X 10.8 からはデベロッパーは自分の製品を「Mac Developer ID」で Code Sign しないといけなくなった。


まず、Mac Developer ID をAppleから発行してもらう。Mac Developer ID をどうやって発行してもらうかは、この投稿の本題ではない。Mac Developer IDをまだもっていないひとは以下のページを参照して取得してください。
Developer Certificate Utility - Mac Developer Program - Support - Apple Developer

無事、Mac Developer ID が取得できたら Xcodeで目的の製品をbuildするときに、buildオプション CodeSign にMac Developer ID :Application を指定しbuildすればよい(詳しくは以下を参照)。
Tools Workflow Guide for Mac: Distributing Outside the Mac App Store

以上でめでたく Mac Developer ID でCode Sign された製品ができる(はずな)わけである。(だったら、なんでわざわざこんな投稿をする必要があるのか。)


それがどっこい、そうではないのだ。


Mac Developer ID で Code Sign をするときは、
Xcode 4.3 でないといけないのだ!!

Xcode 4.2 とか Xcode 3.2. でbuild してしまうと、付加される Code Sign の内容が異なり、Mac OS X 10.5あるいは Mac OS 10.6 で動作させたときMac Developer ID がシステムに正しく認識されないという問題が生じる。
Mac Developer ID が認識されなくとも、もともと、Mac OS X 10.5/10.6 では Gate Keeper が動作していないので問題ないではないか思うかもしれないが、そうではない。

たとえば KeyChain に登録されているベーシック認証のページを開こうとすると、初めて開くときだけ 開くかどうか許可ダイアログで聞いてくるが、2回目以降は許可ダイアログなしに直接開くことができる。


Xcode3.2.6やXcode4.2 で Developer ID のCodeSign でbuild したアプリでは、システムに正しく認証されていないので、ベーシック認証のページを開こうとすると、毎回、許可ダイアログが表示されてしまうのだ。このようなアプリのもとでは KeyChain が正しく動作しないのだ。


では、実際に Xcode4.3でbuildされた場合と、Xcode3.2でbuildされたときで、Mac Developer ID のCode Signのどこが違うのか、ターミナルから コマンド「codesign -d -r-」で直接みてみよう。


Xcode3.2.6 でビルドされた foo.app では
$ codesign -d -r- foo.app 
Executable= foo.app/Contents/MacOS/foo
library => identifier "com.apple.Cocoa" and anchor apple or identifier "com.apple.Carbon"  <--  略 --> and anchor apple or identifier "com.apple.Foundation" and anchor apple
# designated => identifier "jp.co.artman21.foo" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = F8FSQZCDV7


緑の字の部分[ designated record ]は、ルート証明書「anchor apple generic」と中間証明書「certificate 1[field.1.2.840.113635.100.6.2.6] 」そして自分のDeveloper IDの証明書「certificate leaf[field.1.2.840.113635.100.6.1.13]が存在しなければならず、Developer IDのの値はF8FSQZCDV7 であると指定している。
ところが、なんと中間証明書 certificate1[field.1.2.840.113635.100.6.2.6]は、Mac OS 10.5/10.6 ではシステムに実装されていないだ。これが foo.app がシステムに正しく認証されない原因だ。

一方、Xcode 4.3 でビルドされた bar.app では

$ codesign -d -r- bar.app  
Executable= foo.app/Contents/MacOS/bar
library => identifier "com.apple.Cocoa" and anchor apple or identifier "com.apple.Carbon"  <--  略 --> and anchor apple or identifier "com.apple.Foundation" and anchor apple
designated => anchor apple generic and identifier "jp.co.artman21.bar" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = F8FSQZCDV7)

緑の字の部分[ designated record ] の内容が Xcode3.2.6 の場合とちがって、中間証明書が「(certificate leaf[field.1.2.840.113635.100.6.1.9] または certificate 1[field.1.2.840.113635.100.6.2.6] 」のどちらかとなっている。中間証明書 のどちらかが存在すればエラーにならない。すなわち、bar.app はMac OS 10.5/10.6 でもシステムに正しく認証される。

でも、Xcode 4.3 に移行できない
ときはどうすればいいのだ 

新製品はともかく、昔からある製品は、過去のしがらみがあって、Mac OS X 10.4 もサポートしなければいけなかったり、ppc もサポートしななればいけなかったりで、Xcode 4.3 に移行できない場合も多い。

そんなときは、とりあえず Xcode 3.2.6 でbuild して、CodeSign の[ designated record ]の部分だけ、以下のように Xcode4.3 の[ designated record ]に入れ替えるとうまくいく。

まず、Xcode4.3 でbuild した bar.app[ designated record ]をもとに foo.app の[ designated record ]を以下のように作成しファイル「fooDR.txt」として保存する。

designated => anchor apple generic and identifier "jp.co.artman21.foo" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = F8FSQZCDV7)

そして、ターミナルから以下のコマンドで

$ codesign -r fooDR.txt -s "Developer ID Application: Artman21 Inc." -f foo.app
foo.app: replacing existing signature

うまくかきかわったかどうかは、

$ codesign -d -r- foo.app 

で調べられる。
ー 以上 ー

<謝辞> 今回の投稿の作成にあたっては、BRadikoの作者 bui さんからいろいろ貴重な示唆をいただきました。また、Daniel Jalkut さんの Red Seater Blog の投稿 Developer ID Gotcha も非常に参考になりました。