无法在Xcode外部对Mac应用程序进行数字签名 [英] Failure digitally signing a Mac app outside Xcode

查看:1379
本文介绍了无法在Xcode外部对Mac应用程序进行数字签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开发一个Mac应用程序与Qt5,所以外部Xcode。我希望GateKeeper允许我的应用程序在客户端的计算机上运行,​​而不是发出无法打开,因为开发人员的身份无法确认警告。



我已成功地数字签名的应用程序,但GateKeeper仍然有这个投诉。我有一个苹果开发人员证书(我是团队代理),我的钥匙串说它是有效的。我还安装了两个Apple根证书。



我使用命令行实用程序 codesign 在应用程序文件夹内,此外我数字签名的应用程序文件夹本身。在所有情况下,codesign的响应是信息性的,并且不显示错误。使用codesign我可以检查所有的二进制文件是否都签名,运行

  $ codesign --verify --deep --verbose = 2 MyApp.app 

显示所有 em>已验证。此外,它还报告:


MyApp.app:在disk上有效$ 0047 $ MyApp.app:必须


执行:

  $ codesign -v --verbose = 4 --display MyApp.app 

/ p>


Executable = / Users / xxx / trunk / yyy / deploy / release / MyApp.app / Contents / MacOS / MyAppploy b $ b标识符= aaaa.MyApp

格式=包含Mach-O thin(x86_64)

CodeDirectory v = 20200 size = 12461 flags = 0x0(none)hashes = 616 + 3位置=embedã¥b $ b哈希类型= sha1大小= 20
¥b $ b CDHash = d1c12c783dac0e8d9a2b749fb896b11558cec8b6

签名大小= 8532

Authority =开发人员ID应用程序:XXXXX

Authority =开发人员身份证书颁发机构
认证机构_ 2015 12; 04:40

Info.plist entries = 8

TeamIdentifier = YYYYY

Sealed Resources version = 2 rules = 12 files = 10

内部需求量= 1 size = 180


这似乎没问题。



运行

  $ spctl -a -t exec -vv MyApp.app $二进制文件中的b $ b  

结果


MyApp.app:accepted

source =开发人员ID

origin =开发人员ID应用程序:XXXX


这似乎也不错



运行XCode命令行工具检查签名
应用程式或应用程式资料夹中的二进制文件:

  $ ./check-signature /Users/xxx/trunk/yyy/release/MyApp.app 

给出结果


(c)2014 Apple Inc.保留所有权利。

YES


全部



但是GateKeeper仍然不接受应用程序,并抱怨开发人员无法确认。



[由2015年7月17日星期五添加作者]



我认为我发现了问题。我不知道它是一个特性或OSX错误。我被stackoverflow帮助了很多



我的错误



我做了大量的安装和签名而不显式地检查每个二进制的结果。之后,我将使用 otool -L 在一些二进制文件,但不是所有。我错过了从早期的Qt版本升级到Qt 5.5的事实,二进制 libqminimal.dylib 已获得额外的依赖,即: QtDBus 。我没有注意到,但GateKeeper。



Qt开发人员可能想知道为什么我们不只是使用 macdeployqt Qt应用程序在Mac上。首先,我们不喜欢不使用不良记录的黑盒子实用程序。在Internet论坛上,有很多人报告了 macdeployqt 的问题。此外,当比较不同的Qt版本时,Qt库可以有不同的安装位置(由 otool-L 报告)。当我们有一个新的Qt版本,我们的脚本将立即开始大声喊出禁止的依赖。以这种方式,我们获得关于这个新版本中已经改变的信息。


I develop a Mac app with Qt5, so outside Xcode. I want GateKeeper to allow my app to run on clients' computers rather than issuing the "Can't be opened because the identity of the developer cannot be confirmed" warning.

I have successfully digitally signed the app but GateKeeper still comes with this complaint. I have an Apple developer certificate (I am the Team Agent) and my keychain says it is valid. I also have installed two Apple root certificates.

I use the command line utility codesign to digitally sign all the binaries inside the app folder and in addition I digitally sign the app folder itself. In all cases the response of codesign is informative and displays no error. With codesign I can check that indeed all the binaries are signed, running

$ codesign --verify --deep --verbose=2 MyApp.app

shows that all binaries are validated. And in addition it reports:

MyApp.app: valid on disk
MyApp.app: satisfies its Designated Requirement

Running:

$ codesign -v --verbose=4 --display MyApp.app   

gives

Executable=/Users/xxx/trunk/yyy/deploy/release/MyApp.app/Contents/MacOS/MyApp
Identifier=aaaa.MyApp
Format=bundle with Mach-O thin (x86_64)
CodeDirectory v=20200 size=12461 flags=0x0(none) hashes=616+3 location=embedded
Hash type=sha1 size=20
CDHash=d1c12c783dac0e8d9a2b749fb896b11558cec8b6
Signature size=8532
Authority=Developer ID Application: XXXXX
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=29 jul. 2015 12;04:40
Info.plist entries=8
TeamIdentifier=YYYYY
Sealed Resources version=2 rules=12 files=10
Internal requirements count=1 size=180

which seems OK.

Running

$ spctl -a -t exec -vv MyApp.app

on all binaries gives as result

MyApp.app: accepted
source=Developer ID
origin=Developer ID Application: XXXX

which also seems OK

Running the XCode command line tool check-signature on the app or on the binaries inside the app folder:

$ ./check-signature /Users/xxx/trunk/yyy/release/MyApp.app

gives as result

(c) 2014 Apple Inc. All rights reserved.
YES

which in all cases is the desired result.

But GateKeeper still does not accept the app and complains about the fact that the developer cannot be confirmed.

[added by author on Friday July 17, 2015]

I think I have found the problem. I do not know whether it is a feature or an OSX bug. I was helped a lot by stackoverflow question 19551298.

Whenever a file is downloaded from the internet it gets an extended file attribute com.apple.quarantine associated with it. When double-clicking on this downloaded file in Finder, GateKeeper has two possibilities:

  1. When the file is not signed it issues the "Unidentified developer etc" message

  2. When the file is digitally signed it issues the "Developer cannot be confirmed etc" message

In both cases the MessageBox has only one button, an OK button. When this button is clicked nothing happens, apart from the MessageBox closing.

If the extended attribute is deleted (xattr -d) the applications runs, signed or not.

The behavior is different when the applications is started by right-mouse-button-clicking in Finder on the app and then click on the "open" menu action. Again one of the two messageboxes is shown, but now with an extra button to allow the user to open the app anyway. Again the only difference between signed and not signed is the "Unidentified" or the "Not confirmed" message. I do not expect my customers to be able to tell the differende. As a result signing the app is an exercise in futility.

On the basis of the Apple Support Documentation I expected another, much nicer behavior of GateKeeper when double clicking a downloaded app (perhaps the documentation is outdated, or I misread it):

  1. if the app is signed GateKeeper should show a MessageBox with "Downloaded from the internet etc" and a button with "Proceed anyway?"

  2. if the app is not signed a MessageBox with a single OK button and a text "Unidentified developer etc.."

解决方案

Sorry for answering my own question, but I see no other way as editing the original question would lead to spaghetti text.

I finally solved my problem. First the credit: (i) The answer to my other stackoverflow question was very useful and (ii) I got very good (paid) advice from an official Apple developer, by filing a so-called Technical Support Incident (TSI).

On the basis of all this I am now able to give here a very concise recipe of how getting your Mac app successfully treated by GateKeeper. After detailing the recipe I will show what my original mistake was.

Goal: After having developed a Mac app outside Xcode to have GateKeeper issuing the warning "Downloaded from the Internet ..." with three buttons, one of which is "open".

Failure: When GateKeeper issues a warning with either the text ".. unidentified developer.." or the text ".. unconfirmed developer .. " with - in both cases - a messagebox with a single OK button.

Getting your app GateKeeper-ready involves three steps:

  • Make your app standalone with no unacceptable external dependencies. The only acceptable external dependences are system libraries. All other dependencies should have been copied to your MyApp.app folder. GateKeeper rejects any app that has non-system external dependencies
  • Binaries should not be located at illegal positions inside the MyApp.app folder. Libraries go into MyApp/Contents/Frameworks and the executable goes into MyApp/Contents/MacOS
  • All binaries inside MyApp should be digitally signed. Then the MyApp.app folder should be signed. For this signing an Apple "Developer ID Application ..." certificate is necessary

Our recipe is automatic. All the work is done by one script. In case of Qt Creator we use a qmake script where we access the system shell through the $$system command. When using either of the (Xcode) system commands codesign, spctl or check-signature we assume you have redirected stderr to stdout as outlined in answer to question . Otherwise you will not be able to catch the system response when running these utilities. In the following we will not explicitly show this redirection.

HERE IS OUR RECIPE

A. Making the app stand-alone:

  1. copy (with a script) all the needed binaries to the MyApp.folder
  2. run (with a script) install_name_tool -change and install_name_tool -id such that all dependences inside the app are of the relative type @executable_path/../MacOS.. or @executable_path/../Frameworks
  3. run (with a script) otool -L on all binaries inside the MyApp.app folder and flag any illegal dependence, like "@rpath..." or absolute file paths not being system paths. Note that otool -L is not guaranteed to find all dependencies. Plugins are often beyond the horizon of otool. That is why you need the next check.
  4. start a terminal at the location "MyApp.app/Contents/MacOS". Run export DYLD_PRINT_LIBRARIES=1. Then run inside the same terminal window ./MyApp. Your terminal will fill up with over hundred loaded libraries. Check this list again for forbidden libraries (libraries present on your computer, but not on the computer of your customers).
  5. proof of the pudding is in the eating. We use the MacInCloud virtual machines and check whether or not our app runs there. Alternative solution could be the Mac of a relative who is not a developer. Or you could also create a new user ("test") on your own Mac and copy the app to its Download (or Desktop folder, or ...). In the latter case you must temporarily rename the root folder of your IDE as otherwise the user "test" will find the missing binaries there.

B Signing the app

  1. Signing: With our script we run codesign --force --verify --verbose --sign \"Developer ID Application: ....\" \"/path/to/binary\" on all the binaries in the app and then on the app folder itself. In each case the system response is caught. It should contain in each case the string "signed Mach-O thin".
  2. Verification: Run (with a script) command codesign --verify --verbose \"/path/to/binary\" on each binary in your app and on the app itself and catch the system response. It should in each case contain the strings "valid on disk" and "satisfies its Designated Requirement".
  3. GateKeeper check: Run (with a script) spctl -a -t exec -vv /path/to/binary\" on each binary and on the app folder itself. The system response is caught. It should contain in all cases the string "accepted source".
  4. check-signature: Run (with a script) check-signature \"/path/to/banary\" on each binary and on the app folder itself. The system response is caught. It should contain the string "YES" in each case.

C External check

  1. zip your app into a single zip file. Upload to one of your cloud servers

  2. GateKeepers keeps a long list (typically hundreds of items) of exceptions on its general gate-keeper role. Your app must not be in that list if you want to test GateKeeper. Rather than editing this list a much simpler trick is creating a new user on your Mac. Log in to that user and download the zip file from the Internet cloud server. Finder will automatically uncompress it. Click on it. If GateKeeper tells you that it can open the application but it warns you at the same time that it is downloaded from the Internet, it is time to grab a (white) beer.

Here the desired GateKeeper warning:

My mistake

I did much of the installing and signing without explicitly checking the result for each binary. After that I would use otool -L on a number of binaries but not on all. I missed the fact that upgrading to Qt 5.5 from an earlier Qt version the binary libqminimal.dylib has acquired an extra dependency, viz.: QtDBus. I had not noticed it, but GateKeeper did.

Qt developers might wonder why we not just use macdeployqt for deploying Qt application on a Mac. In the first place we do not like not to use ill-documented black-box utilities. On Internet fora there are quite a number of people reporting issues with macdeployqt. In addition the Qt libraries can have different install locations (as reported by otool-L) when comparing different Qt versions. When we have a new Qt version our script will immediately start to yell about forbidden dependencies. In this way we get information about what has changed in this new version.

这篇关于无法在Xcode外部对Mac应用程序进行数字签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆