Android应用程序签名
-
本指南依据安卓官网 Signing Your Applications 翻译整理。
-
Android要求所有已安装的应用程序都使用数字证书做数字签名, 数字证书的私钥由应用开发者持有. Android使用证书作为标识应用程序作者的一种方式, 并在应用程序之间建立信任关系. 证书并不用来控制用户能否安装哪个应用. 证书不需要由证书认证中心签名: 完全可以使用自签名证书(self-signed certificates).
理解Android应用签名的要点有:
- 所有应用程序都必须签名. 系统将不会安装一个没签名的应用.
- 可以用自签名证书来签名应用程序. 并不需要证书认证中心.
- 当准备好向最终用户发布应用时, 必须用一个合适的私钥对其签名. 不能发布用SDK工具生成的debug key签名的应用.
- 系统仅在安装时检查签名者的证书的过期时间. 如果应用程序签名者的证书在安装后的时间过期, 该应用将仍然正常工作.
- 可以使用标准工具 — Keytool 和 Jarsigner — 生成key并为应用的 .apk 文件签名.
- 当应用签名完成, 使用 zipalign 工具来优化最终的 APK 包.
没有正确签名的应用, Android系统不会安装或运行. 此规则适用于在任何地方运行的Android系统, 不管是在模拟器还是真实设备上. 因为这个原因, 在真机或模拟器上运行或者调试应用前, 必须为其设置好签名.
在调试期间, Android SDK工具协助你为应用做好签名工作. Eclipse的ADT插件和Ant build工具都提供2种签名模式 – debug 模式和 release 模式.
- 开发和调试的时候, 可在debug模式下编译. 在debug模式下, build工具使用Keytool工具, 它被包含在JDK中, 用于创建一个keystore和key, 别名和密码. 在每个编译过程中, 工具使用debug key来签名应用的.apk文件. 因为事先知道密码, 所以在每一次编译keystore/key需要密码的时候不会提示.
- 当应用准备好发布时, 必须在release模式下编译,然后用你的私钥对.apk文件签名.
- 有2种方法:
-
- 在命令行下使用 Keytool 和 Jarsigner.
- 使用此方法, 首先需要将应用编译成一个未签名的 .apk . 然后必须手动使用Jarsigner(或类似工具)和你的私钥为.apk签名. 如果没有合适的私钥, 可以手动运行Keytool来生成你自己的 keystore/key ,然后用Jarsigner给应用签名.
- 使用ADT的Export Wizard.
- 如果在Eclipse下使用ADT插件开发, 可以使用Export Wizard来编译应用, 生成私钥(如果有必要), 以及为.apk签名, 使用Export Wizard可以简单地在几个步骤中完成所有的事情.
一旦应用完成签名, 不要忘了为APK运行zipalign来完成额外的优化.
签名策略
应用程序签名的一些方面可能会影响应用程序的开发过程, 尤其是当你计划发布多个应用时. 通常情况下, 对于所有开发者而言, 推荐的策略是:在应用程序的整个生命周期,所有的应用程序使用相同的证书签名.
为什么这么做的原因:
- 应用程序升级 – 当发布应用的更新时, 如果想让用户无缝地升级到新版本, 需要继续使用相同的某个或者某一套证书来签名更新包. 当系统安装应用的更新时, 它会比较现有版本和新版本的证书. 如果证书吻合, 包括证书数据和顺序都吻合, 那么系统允许更新. 如果新版本所做的签名不是匹配的, 那么将需要给应用起一个不同的包名 — 在这种情况下, 用户相当于安装了一个完全的新程序.
- 应用程序模块化 – Android允许由相同证书签名的应用程序运行在相同的进程中, 此时系统会将它们作为单个应用程序对待. 在这种方式中, 可以按模块化的方式部署应用, 用户可以根据需要独立地更新每一个模块.
- 代码/数据 的授权共享 – Android 提供模式匹配的权限控制机制, 因此一个应用可以暴露功能给另一个用指定证书签名的应用. 通过用相同证书签名多个应用,以及使用模式匹配的权限检查, 应用程序可以以安全的方式共享代码和数据.
另一个影响签名策略的重要考量是, 如何设置签名应用的key的有效期.
- 如果计划为某个单独的应用程序提供更新支持, 那么应该确认key的有效期要比应用的寿命长. 推荐25年或者更长的有效期. 当key的有效期过期, 用户将再也不能无缝地更新到应用程序的新版本.
- 如果要使用相同的key为多个不同的应用签名, 应当确认key的有效期比所有这些应用的所有版本的生命周期还长, 包括要比将来加到这个套件中的额外的关联应用的生命周期更长.
- 如果计划将应用程序发布到Android Market, 为应用签名的key的有效期必须在2033年10月22日以后. Market服务器强制执行这个规则, 来保证当新版本可用时, 用户可以无缝地更新Market应用.
当设计的时候, 须牢记这些要点, 以确保使用合适的证书来签名应用程序.
签名基础设置
在开始之前, 应当确保Keytool对 SDK build tools 可用. 在大多数情况下, 我们可以通过设置JAVA_HOME环境变量来引用一个合适的JDK, 告诉SDK build tools如何找到Keytool. 或者, 可以添加 Keytool的JDK版本到 PATH 环境变量.
如果在某个自带GNU Java编译器版本的Linux下开发, 请确认系统使用的是JDK版本的Keytool, 而不是 gcj 版本. 如果 Keytool 已经存在于 PATH, 它可能会指向一个 /usr/bin/keytool 的符号链接. 在这种情况下, 检查符号链接目标, 确保它指向JDK中的Keytool.
如果将公开发布应用程序, 还需要 Jarsigner 工具. Jarsigner 和 Keytool 都包含在 JDK 中.
Debug模式下的签名
Android build tools 提供了debug签名模式, 帮助简化应用的开发和调试, 而仍然符合Android系统签名.apk的需求. 当使用debug模式来构建app时, SDK 工具调用 Keytool来自动创建一个用于debug的 keystore 和 key. 然后这个debug key被用来自动签名 .apk, 所以不必用自己的key来签名包.
SDK 工具使用预定义的 名称/密码 来创建keystore/key :
- Keystore name: "debug.keystore"
- Keystore password: "android"
- Key alias: "androiddebugkey"
- Key password: "android"
- CN: "CN=Android Debug,O=Android,C=US"
如有必要, 可以更改 debug keystore/key 的 location/name 或自己提供一个自定义的 debug keystore/key. 然而, 任何自定义的debug keystore/key必须使用和默认debug key(如之前所述)相同的debug keystore/key 名称和密码. (Eclipse/ADT中, Windows > Preferences > Android > Build)
注意: 当用debug证书签名时, 应用程序不能对外发布.
Eclipse用户
如果使用 Eclipse/ADT (并且根据之前"签名基础设置"一节所述设置好了Keytool), debug模式下的签名是默认开启的. 运行或者调试程序时, ADT自动使用debug证书对.apk进行签名, 并对安装包使用zipalign, 然后将它安装到选定的模拟器或者连接的设备上. 这一切不用我们亲自动手, ADT可以自行访问Keytool.
Ant用户
如果使用Ant来构建.apk文件, debug签名模式通过ant命令(假设使用由android tool生成的build.xml文件)下使用debug选项开启. 当运行ant debug来编译app时, build脚本生成一个 keystore/key 并为.apk做签名. 然后脚本也会使用zipalign工具优化.apk. 仍然不需要你做额外的事情.
Debug证书的过期.
用于debug模式(Eclipse/ADT和Ant的默认值)签名的自签名证书, 有效期为创建之日起的365天. 当证书过期, 将得到一个build错误.
使用Ant时, 错误看起来如下所示:
debug: [echo] Packaging bin/samples-debug.apk, and signing it with a debug key... [exec] Debug Certificate expired on 8/4/08 3:43 PM
在 Eclipse/ADT 中, 将在Android控制台看到类似的错误.
要修正这个问题, 只需删除 debug.keystore 文件. AVDs默认存储的位置是: OS X 和 Linux : ~/.android/ , Windows XP: C:\Documents and Settings\<user>\.android\ , Windows Vista 和 Windows 7: C:\Users\<user>\.android\
下次build时, 工具会重新生成一个新的keystore和debug key.
注意, 如果你的开发机使用的是非公历语言环境(non-Gregorian locale), build工具可能会错误地生成一个已过期的debug证书, 因此在编译的时候你会得到一个错误.
具体的错误解决办法,请查阅查官方相关文档.公开发布版的签名
当应用准备好对外发布, 必须:
- 获取一个合适的私钥.
- 在release模式下编译应用程序.
- 使用你的私钥对应用程序进行签名.
- 使用zipalign来调整和优化APK包
如果使用Eclipse+ADT插件进行开发, 可以使用Export Wizard来处理整个的编译, 签名, 和优化过程. 在处理过程中, Export Wizard甚至允许生成一个新的keystore和私钥.
1. 获取一个合适的私钥
在给应用程序签名的准备阶段, 必须首先确保有一个用于签名的合适的私钥. 合适的私钥有如下要求:
- 是你所拥有的.
- 代表个人, 公司, 或者组织实体, 在应用里可以被识别的.
- 拥有比应用程序或者应用程序套件生命周期更长的有效期. 推荐大于25年的有效期. 如果计划在Android Market上发布应用, 注意需要有效期截止时间为 2033-10-22 以后.
- 不能上传一个有效期在此之前的应用.
- 不是由Android SDK工具生成的debug key
密钥可以是自签名的. 推荐从第三方CA申请的证书, 点击这里在线申请。 使用第三方CA签发的证书的好处:(1) 符合国家有关法规,确保开发者身份可信;(2) 如果私钥丢失,可以免费重新颁发。 如果没有一个合适的密钥, 必须使用Keytool生成一个.
要通过Keytool生成一个自签名的key, 使用keytool命令行工具, 并传入下面的任何一个参数选项(以及任何其他需要的参数.)
警告: 需要保证私钥的安全. 在运行Keytool之前, 请确认已经阅读了 保证你的私钥安全 一节. 通常情况下, 生成key时, 对于key和keystore应当使用强密码.
Keytool 选项 描述 -genkey 生成一个key pair (公钥和私钥) -v 允许详细内容输出. -alias <alias_name> key的别名. 只会用到前8个字符. -keyalg <alg> 生成key时的加密算法. 支持 DSA 和 RSA. -keysize <size> 生成的key的大小(bits). 如果不提供, Keytool使用默认的Key大小:1024.
通常情况下,我们推荐使用 2048或者更大的key尺寸.-dname <name> 描述key的创建者的标识名称. 在自签名证书中, 本参数会出现在发布者和主题字段.
注意, 不要在命令行下指定这个选项. 此时 Jarsigner会提示你输入每一个标识名称字段(CN, OU,等)-keypass <password> key的密码. 安全起见, 不要在命令行中包含这个选项. 此时Keytool会提示你输入密码.
这种方式中, 密码不会被保存在shell历史数据中.-validity <valdays> key的有效期, 以天数为单位. 注意: 推荐使用10000或更大的数字. -keystore <keystore-name>.keystore 保存私钥的keystore名称. -storepass <password> keystore的密码. 安全起见, 不要在命令行中包含这个选项.
此时, Keytool会提示输入这个密码.
在这种方式中, 密码不会被保存在shell历史数据中.一个使用Keytool命令生成私钥的例子:
$ keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
运行上面的命令行例子, Keytool会提示输入keystore和key的密码, 以及key的标识名称字段. 然后它为keystore生成一个文件 my-release-key.keystore. keystore和key由输入的密码保护. keystore包含一个单个的key, 有效期10000天. 别名是以后将用到的名称, 当为应用签名时, 来指定这个keystore.
关于Keytool的更多信息, 参考文档:
http://java.sun.com/j2se/1.5.0/docs/tooldocs/#security 2. 在release模式编译应用程序.
为了向用户发布应用程序, 必须在release模式编译. 在release模式下, 编译好的应用程序默认是没签名的, 需要用私钥为它签名.
警告: 不能发布一个没有签名或者用debug key签名的应用程序.
使用Eclipse
要从Eclipse导出一个未签名的 .apk, 在Package Explorer中的project上点击鼠标右键, 选择Android Tools > Export Unsigned Application Package. 然后给未签名的.apk指定一个文件路径. (或者在Eclipse中打开AndroidManifest.xml文件, 点开Overview标签, 然后点击Export an unsigned .apk.)
注意, 完全可以在Export Wizard中执行整个的编译和签名步骤. 参见: 使用Eclipse ADT编译和签名.
使用Ant
如果使用Ant, 可以通过ant命令行的release选项开启release模式.
举个例子, 如果从build.xml文件的目录中运行Ant, 命令行看起来将是这样:ant release
默认地, build脚本编译应用程序.apk时不做签名. 项目中 bin/ 下的的输出文件将是: <your_project_name>-unsigned.apk. 因为应用程序.apk仍然是未签名的, 必须人工使用私钥为它签名, 然后使用zipalign优化它.
然而, 如果在项目的build.properties文件中提供了keystore的路径和key的别名, 那么Ant build 脚本也可以处理签名和优化(align)过程, 在提供了相关信息的情况下, 当执行ant release, build脚本将提示输入keystore和别名的密码, 然后它将为包签名, 并进行优化. bin/中的最后输出将是 <your_project_name>-release.apk.
使用这些自动化步骤, 可以跳过下面的人工处理过程(第3和4步).3. 使用私钥签名应用程序.
当准备好一个应用程序包, 在确保机器上Jarsigner工具和keystore里的私钥有效的情况下. 可以使用Jarsigner工具来对它做签名.
运行Jarsigner对应用签名, 同时引用应用的.apk和用来签名.apk的私钥的keystore. 下面的表列出了可以使用的选项.
Jarsigner选项 Description -keystore <keystore-name>.keystore 包含私钥的keystore的名称. -verbose 允许详细输出. -storepass <password> keystore的密码, 安全起见, 不要把此参数包含在命令行中.
此时, Jarsigner提示输入密码.
这种方式下,密码不会被保存在shell的历史记录中.-keypass <password> 私钥的密码. 安全起见, 不要把此参数包含在命令行中.
此时, Jarsigner提示输入密码.
这种方式下,密码不会被保存在shell的历史记录中.下面是使用Jarsigner的例子,签名 my_application.apk, 使用之前创建的例子keystore.
$ jarsigner -verbose -keystore my-release-key.keystore my_application.apk alias_name
运行上面的命令行例子, Jarsigner会提示输入keystore和key的密码. 然后它修改.apk文件作签名. 注意, 可以多次用不同的key签名一个.apk文件.
要验证.apk是否已签名, 可以使用如下命令:
$ jarsigner -verify my_signed.apk
如果.apk确实已经正确地签名, Jarsigner输出"jar verified". 如果想要知道更多明细, 你可以尝试如下几个命令:
$ jarsigner -verify -verbose my_application.apk
或
$ jarsigner -verify -verbose -certs my_application.apk
上面的命令, 使用 -certs 参数, 将显示 "CN=" 行, 描述是谁创建了key.
注意: 如果看到 "CN=Android Debug", 这意味着 .apk 是由Android SDK生成的debug key签名的. 如果想发布你的应用, 必须用你的私钥签名, 而不是debug key.
Jarsigner的更多信息, 请参见文档:
http://java.sun.com/j2se/1.5.0/docs/tooldocs/#security 4. 调整对齐(align)最终的APK包
一旦使用你的私钥对.apk做了签名, 紧接着请对文件运行zipalign. 这个工具确保所有的未压缩数据相对于文件起始位置以特定的字节对齐方式开始. 当安装在某个设备上时, 确保4字节边界对齐的方式, 提供了性能上的优化. 当对齐后, 如果它们包含有对齐限制的二进制数据, Android系统甚至可以使用mmap()读取文件, 而不是从包里复制所有的数据. 另一个好处是在运行程序时, 减少了内存消耗.
zipalign 工具由Android SDK提供, 在 tools/ 目录中. 要调整对齐签名后的.apk, 执行如下命令:
zipalign -v 4 your_project_name-unaligned.apk your_project_name.apk
-v 标志开启详细输出(可选). 4是字节对齐(不要使用4以外的值). 第一个文件参数是签名后的.apk(输入), 第二个文件是目标.apk文件(输出). 如果要覆盖现存的.apk, 可以使用 -f 标志.
警告: 在使用zipalign优化包之前, 输入的.apk 必须使用私钥签名过. 如果在zipalign之后签名, 将使之前优化对齐操作无效, 回到未优化的状态.
使用Eclipse ADT编译和签名
如果使用Eclipse和ADT插件, 可以用Export Wizard来导出一个签名的.apk(如果必要,甚至可以创建一个新的keystore.) Export Wizard为你处理所有Keytool和Jarsigner的交互操作, 这允许使用一个GUI界面来操作签名包的过程, 而不必人工处理各种编译, 签名, 以及优化对齐工作. 一旦向导编译并签名完.apk包, 它将也使用zipalign对包进行优化对齐处理. 因为Export Wizard使用到Keytool和Jarsigner, 应当确保在电脑上可以访问到这2样工具.
若要在Eclipse中创建一个签名并优化对齐后的.apk:
- 在Package Explorer中选中project, 然后选择File > Export.
- 打开Android文件夹, 选择Export Android Application, 然后点击 Next.
- Export Android Application 向导开始, 它将引导你进行签名的整个过程, 包括选择用于签名.apk的私钥(或者创建一个新的keystore和私钥)
- 完成Export Wizard后, 应用程序将被编译, 签名, 对齐, 做好了发布前的所有准备.
保证私钥安全
对于你和你的用户来说, 维护你的私钥的安全相当关键和重要. 如果你允许别人使用你的key, 或者如果你将keystore和密码放在一个不安全的地方, 以至于第三方可以找到并使用它们, 那么你的作者身份和用户信任度将受到连累.
如果第三方在你不知情或没得到你的允许的情况下控制和使用你的key, 那么随便一个人就都可以签名和发布恶意的应用程序来替换你发布的应用, 或者干脆取代你的应用. 然后这个人可以用你的身份签名和发布应用程序, 攻击其他应用甚至直接攻击系统, 以及损坏或偷取用户数据.
开发者(或团体)的名声取决于你对你的私钥的适当保密, 任何时候, 直到key过期. 下面是一些保证key的安全性的小提示.
- 对keystore和key采用强密码.
- 当用Keytool生成key时, 不要在命令行提供-storepass 和 -keypass 选项参数. 如果非这么做, 你的密码将能够在shell历史中查到, 这样将导致任何用户都能访问.
- 类似地, 当使用Jarsigner签名应用时, 不要在命令行提供-storepass 和 -keypass参数选项.
- 不要给任何人你的私钥, 不要让未经授权的人知道你的keystore和key密码.
通常情况下, 当生成,使用和保存你的key时, 如果根据常识做了提前的预防, 那么它将会保证安全.