转载

使用 Developer ID 为 Mac app 签名

为何要签名

对 app 进行代码签名可让用户确信它来自已知来源,且自最后一次签名之后未被修改。在您的 Mac app 或 iOS app 可以使用商店服务,安装到 iOS 设备上进行开发或测试,或者提交到 App Store 之前,必须先使用 Apple 颁布的证书对其进行签名。

在 OS X 的 安全性与隐私 的设置里有一项设置是 允许从以下位置下载的应用程序 ,默认的设置是 Mac App Store 和被认可的开发者

也就是说在用户不手动修改设置的情况下,用户无法打开未签名的程序。

使用 Developer ID 为 Mac app 签名

下面就来介绍如何使用 Developer ID 为 app 签名。

注意:本文不适用于发布到 Mac App Store 的签名。

如何签名

将你的 Apple ID 添加到 Xcode

  1. 打开 Xcode 的 Preferences。
  2. 选择 Accounts
  3. 点击左下角的加号按钮。
  4. 填入你的 Apple ID。

加入一个 Program

在添加 Apple ID 的界面,你可以选择 Join a Program... 来加入一个 program。

你也可以让 program 的管理员来将你添加到你们公司的 program。

在 Xcode 中设置 signing identity

  1. 选择你项目的 target。
  2. 选择 General
  3. Signing 中选择 Developer ID
  4. Team 中选择你加入的 program。

创建 Developer ID certificate

方法一:

首先在自己的电脑上使用 钥匙串访问 来创建一个 certificate signing request (CSR)

  1. 打开 钥匙串访问 ,在菜单栏中选择 钥匙串访问 > 证书助理 > 从证书颁发机构请求证书
  2. 填入你的邮件地址和常用名,选择下方的 存储到磁盘 ,点击继续选择文件位置保存即可。
  3. 管理员在 Member Center 里面的 Certificates, Identifiers & Profiles 页面中,选择 Certificates 下方的 All
  4. 选择右上角的加号按钮。
  5. 选择 Production 下方的 Developer ID
  6. 选择证书类型 Developer ID Application ,点击 Continue
  7. 选择前面生成的 CSR(扩展名是 .certSigningRequest) 文件,点击 Continue
  8. 选择 Generate 并且下载证书。
  9. 将证书导入到 Keychain 中。

方法二:

  1. 打开 Xcode 的偏好设置,选择 Accounts 选项卡,选择加入的 program,点击 View Details
  2. 此时 Xcode 没有找到任何证书,会提示是否要帮你申请证书,此时只要选择以 The Developer ID Application 开头的选项即可。(也可以点击左边的加号来添加一个 Developer ID Distribution
  3. 等待 program 的管理员来审批。

验证:

方法一:

在 Xcode 的 Preference > Accounts 中选择加入的 program,选择 View Details... ,应该能看到一项 Developer ID Application

方法二:

在钥匙串访问中能找到一个名为 Developer ID Application: Your Company Name 的证书。

签名

方法一(使用 Xcode 签名):

  1. 选择菜单中的 Product > Archive
  2. (可选)选择 Validate... 来验证打包。
  3. 点击 Export...
  4. 按照提示选择 Export a Developer ID-signed Application
  5. 选择一个 team。
  6. 输入文件名,保存即可。

但是使用 Xcode 签名有可能会失败,下面的签名验证部分会提到。

方法二(使用命令行):

codesign --force --verbose=4 --sign "Developer ID Application: Your Company Name" Foo.app

codesign 要求项目中包含的所有框架、库都已经被签名,并且 codesign 不会自动帮你完成,这需要我们单独为每一个库进行签名后再为整个 app 签名。

签名或验证一个 framework 时使用的路径是 MyCustomFramework/Versions/A ,比如

codesign -s "Developer ID Application: Your Company Name" ../MyCustomFramework/Versions/A

签名时不要使用 --deep 参数。

如果 framework 签名失败,可能是由于

  • Info.plist 文件中的 CFBundleExecutable 值与可执行文件的文件名不一致。
  • 目录结构不对(比如 QT),framework 的目录结构 必须与下面完全一样。
MyFramework.framework/
MyFramework -> Versions/Current/MyFramework
Resources -> Versions/Current/Resources
Versions/
A/
MyFramework
Resources/
English.lproj/
InfoPlist.strings
Info.plist
Current -> A

签名验证

输入下面的命令:

spctl -a -v Foo.app

如果验证通过,则会提示:

Foo.app: accepted  
source=Developer ID
origin=Developer ID Application: Your Company Name

出现下面的提示:

Foo.app: rejected  
source=obsolete resource envelope

此时如果用户通过浏览器或者邮箱接收到安装包再打开 app 时就会提示文件已损坏。

解决方法:

使用下面的命令

codesign --verify --deep --verbose=3 /path/to/signed/app

找出验证不通过的重新签名,再重新签名整个 app 即可。

脚本

下面是我用来签名的脚本。

#!/usr/bin/python

import os
import commands

APP_PATH = "/path/to/Foo.app"
DEVELOPER_ID = "Developer ID Application: Your Awesome Company"

def signWithPath(path):
signCommand = "codesign --force --sign /"%s/" /"%s/"" % (DEVELOPER_ID, path)
retCode, result = commands.getstatusoutput(signCommand)
if retCode != 0:
print result
print "code sign failed"
return retCode

def validateWithPath(path):
signCommand = "codesign --verify --deep --verbose=3 /"%s/"" % path
retCode, result = commands.getstatusoutput(signCommand)
if retCode == 0:
print "Accepted!"
return 0
else:
print result
print "Rejected!"
return -2

def sign():
print "> signing frameworks & dylibs..."

if not os.path.exists(APP_PATH):
print "where's your app?!"
return

frameworkDir = os.path.join(APP_PATH, "Contents/")

# sign dylibs
for root, dirs, files in os.walk(frameworkDir):
for f in files:
if f.endswith(".dylib"):
print "signing", f
dylibPath = os.path.join(root, f)
signWithPath(dylibPath)

# sign frameworks
for root, dirs, files in os.walk(frameworkDir):
for d in dirs:
if d.endswith(".framework"):
print "signing", d
frameworkPath = os.path.join(root, d, "Versions/A")
signWithPath(frameworkPath)


print "> singing app..."
print "singing", APP_PATH
signWithPath(APP_PATH)

print "> validate code sign..."
if validateWithPath(APP_PATH) == 0:
print "Code sign completed!"
else:
print "Dohhh!"


if __name__ == '__main__':
sign()

参考

https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingCertificates/MaintainingCertificates.html#//apple_ref/doc/uid/TP40012582-CH31-SW30

http://furbo.org/2013/10/17/code-signing-and-mavericks/

https://github.com/sunuslee/sunus-cookbook/blob/master/Cocoa/codesign.md

正文到此结束
Loading...