AndroidApp签名和权限
签名简介
在Android系统中,所有安装到系统的应⽤程序都必有⼀个Android数字证书,此数字证书⽤于标识应⽤程序的作者和在应⽤程序之间建⽴信任关系,如果⼀个per mission的protectionLevel为signature,那么就只有那些跟该permission所在的程序拥有同⼀个数字证书的应⽤程序才能取得该权限。
Android使⽤Java的数字证书相关的机制来给apk加盖数字证书,要理解Android数字证书,需要先了解以下数字证书的概念和java的数字证书机制。
基础概念:数字证书蒸包子的做法和技巧
数字证实是采⽤数字⼿段来证实⽤户⾝份的⼀种⽅法。数字证书含有两部分数据:⼀部分是对应主体(单位或个⼈)的信息,另⼀部分是这个主体所对应的公钥。即数字证书保存了主体和它的公钥的⼀⼀对应关系,⽤于⾃我认证(向其他的⽤户证明⾃⼰的⾝份)。
Java数字证书⼯具
Java中的可以⽤来创建数字证书,所有的数字证书是以⼀条⼀条(采⽤别名区别)的形式存⼊
证书库的中,证书库中的⼀条证书包含该条证书的私钥,公钥和对应的数字证书的信息。证书库中的⼀条证书可以导出数字证书⽂件,数字证书⽂件只包括主体信息和对应的公钥。
每⼀个证书库是⼀个⽂件组成,它有访问密码,在⾸次创建时,它会⾃动⽣成证书库,并要求指定访问证书库的密码。
在创建证书的的时候,需要填写证书的⼀些信息和证书对应的私钥密码。这些信息包括 CN=xx,OU=xx,O=xx,L=xx,ST=xx,C=xx,它们的意思是:
CN
OU
O(Organization组织名称)
L(Locality城市或区域名称)
ST
C(Country国家名称)
可以采⽤交互式让⼯具提⽰输⼊以上信息,也可以采⽤参数
-dname "CN=xx,OU=xx,O=xx,L=xx,ST=xx,C=xx"来⾃动创建。
例如这条命令:
keytool -genkey -alias testCA -keyalg RSA -keysize 1024 -keystore testCALib -validity 3650
在数字证书库testCALib中创建了⼀个别名为testCA,使⽤RSA算法加密的,有效期为3650天的数字证书。
证书⽣成以后,我们可以使⽤命名将数字证书导出为⼀个⽂件。
keytool -export -alias testCA - -keystore testALib -rfc
有关keytool的其他⽤法可以查询keytool的帮助⽂档。
数字证书⽣成以后,我们需要使⽤⽣成的数字证书给程序包签名,这个是使⽤jarsigner ⼯具。例如,如果我们有⼀个android的程序包calendar.apk.,我们就可以使⽤刚⽣成的testCA给改程序包签名。
手机开不了机怎么办jarsigner -keystore testCALib calendar.apk testCA.
签名的意义
保证每个应⽤程序开发商合法ID,防⽌部分开放商可能通过使⽤相同的Package Name来混淆替换已经安装的程序,需要对我们发布的APK⽂件进⾏唯⼀签名,保证我们每次发布的版本的⼀致性(如⾃动更新不会因为版本不⼀致⽽⽆法安装)。
Android数字证书概述
Android系统要求每⼀个安装进系统的应⽤程序都是经过数字证书签名的,数字证书的私钥则保存在程序开发者的⼿中。Android将数字证书⽤来标识应⽤程序的作者和在应⽤程序之间建⽴信任关系,⽽不是⽤来决定最终⽤户可以安装哪些应⽤程序。这个数字证书并不需要权威的数字证书签名机构认证,它只是⽤来让应⽤程序包⾃我认证的。
Android数字证书包含以下⼏个要点:
1. 所有的应⽤程序都必须有数字证书,Android系统不会安装⼀个没有数字证书的应⽤程序
2. Android程序包使⽤的数字证书可以是⾃签名的,不需要⼀个权威的数字证书机构签名认证
3. 如果要正式发布⼀个Android应⽤,必须使⽤⼀个合适的私钥⽣成的数字证书来给程序签名,⽽不能使⽤adt插件或者ant⼯具⽣成的调试证书来发布。
4. 数字证书都是有有效期的,Android只是在应⽤程序安装的时候才会检查证书的有效期。如果程序已经安装在系统中,即使证书过期也不会影响程序的正常功能。但过期后⽆法升级
5. Android使⽤标准的java⼯具 Keytool and Jarsigner 来⽣成数字证书,并给应⽤程序包签名。
Android系统不会安装运⾏任何⼀款未经数字签名的apk程序,⽆论是在模拟器上还是在实际的物理设备上。Android的开发⼯具(ADT插件和Ant)都可以协助开发者给apk程序签名,它们都有两种模式:调试模式(debug mode)和发布模式(relea mode)。
在调试模式下,android的开发⼯具会在每次编译时使⽤调试⽤的数字证书给程序签名,开发者⽆须关⼼。当要发布程序时,开发者就需要使⽤⾃⼰的数字证书给apk包签名,可以有两种⽅法。
1. 在命令⾏下使⽤JDK中的和Keytool(⽤于⽣成数字证书)和Jarsigner(⽤于使⽤数字证书签名)来给apk包签名 使⽤ADT
2. Export Wizard进⾏签名(如果没有数字证书可能需要⽣成数字证书)
签名策略
同⼀个开发者尽量使⽤同⼀个数字证书同⼀个开发者的多个程序尽可能使⽤同⼀个数字证书,这可以带来以下好处。
1. 有利于程序升级,当新版程序和旧版程序的数字证书相同时,Android系统才会认为这两个程序是同⼀个程序的不同版本。如果新版
程序和旧版程序的数字证书不相同,则Android系统认为他们是不同的程序,并产⽣冲突,会要求新程序更改包名。
2. 有利于程序的模块化设计和开发。Android系统允许拥有同⼀个数字签名的程序运⾏在⼀个进程中,Android程序会将他们视为同⼀
个程序,可以单独对他们升级更新,这是⼀种App级别的模块化思路。所以开发者可以将⾃⼰的程序分模块开发,⽽⽤户只需在需要的时候下载适当的模块。
3. 可以通过权限(permission)的⽅式在多个程序间共享数据和代码。Android提供了基于数字证书的权限赋予机制
(l:
数字证书的有效期
1. 数字证书的有效期要包含程序的预计⽣命周期,⼀旦数字证书失效,持有该数字证书的程序将不能正常升级。
2. 如果多个程序使⽤同⼀个数字证书,则该数字证书的有效期要包含所有程序的预计⽣命周期。
3. Android Market强制要求所有应⽤程序数字证书的有效期要持续到2033年10⽉22⽇以后。
权限
要区分apk运⾏时的拥有的权限与在⽂件系统上被访问(读写执⾏)的权限两个概念
linux⽂件系统上的权限
-rwxr-x--x system system 4156 2010-04-30 16:13 test.apk
代表的是相应的⽤户/⽤户组及其他⼈对此⽂件的访问权限,与此⽂件运⾏起来具有的权限完全不相关。
⽐如上⾯的例⼦只能说明system⽤户拥有对此⽂件的读写执⾏权限;system组的⽤户对此⽂件拥有读、执⾏权限;其他⼈对此⽂件只具有执⾏权限。
⽽test.apk运⾏起来后可以⼲哪些事情,跟这个就不相关了。
千万不要看apk⽂件系统上属于system/system⽤户及⽤户组,或者root/root⽤户及⽤户组,就认为ap
k具有system或root权限。
Android的权限规则
1. Android中的apk必须签名
这种签名不是基于权威证书的,不会决定某个应⽤允不允许安装,⽽是⼀种⾃签名证书。
重要的是,android系统有的权限是基于签名的。⽐如:system等级的权限有专门对应的签名,签名不对,权限也就获取不到。默认⽣成的APK⽂件是debug签名的。
2. 基于UrID的进程级别的安全机制
进程有独⽴的地址空间,进程与进程间默认是不能互相访问的,是⼀种很可靠的保护机制。
Android通过为每⼀个安装在设备上的包(apk)分配唯⼀的Linux urID来实现,名称为”app_”加⼀个数字,⽐如app_43。不同的UrID,运⾏在不同的进程,所以apk之间默认便不能相互访问。
Android提供了如下的⼀种机制,可以使两个apk打破前⾯讲的这种壁垒:
在l中利⽤sharedUrId属性给不同的package分配相同的urID,通过这样做,
两个package可以被当做同⼀个程序,系统会分配给两个程序相同的UrID。当然,基于安全考虑,两个package需要有相同的签名,否则没有验证也就没有意义了(详见<;共享UID>)。
(这⾥补充⼀点:并不是说分配了同样的UrID,两程序就运⾏在同⼀进程, 下⾯为PS指令摘取的,显然,system、app_2分别对应的两个进程的PID都不同)
Ur PID PPID
system 953 883 187340 55052 ffffffff afe0cbcc S system_rver
app_2 1072 883 100264 19564 ffffffff afe0dcc4 S com.android.inputmethod.
system 1083 883 111808 23192 ffffffff afe0dcc4 S srvi
app_2 1088 883 156464 45720 ffffffff afe0dcc4 S android.process.acore
3. 默认apk⽣成的数据对外是不可见的
实现⽅法是:Android会为程序存储的数据分配该程序的UrID。借助于Linux严格的⽂件系统访问权限,便实现了apk之间不能相互访问似有数据的机制。
例:我的应⽤创建的⼀个⽂件,默认权限如下,可以看到只有UrID为app_21的程序才能读写该⽂件。
-rw app_21 app_21 87650 2000-01-01 09:
如何对外开放:使⽤MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE 标记。
应⽤创建的任何⽂件都会被赋予应⽤的⽤户标识,并且正常情况下不能被其他包访问。当通过
getSharedPreferences(String,int)、openFileOutput(String、int)或者openOrCreateDataba(String、int、SQLiteDataba.CursorFactory)创建⼀个新⽂件时,开发者可以同时或分别使⽤MODE_WORLD_READABLE和
MODE_WORLD_WRITEABLE 标志允许其他包读/写此⽂件。当设置了这些标志后,这个⽂件仍然属于⾃⼰的应⽤程序,但是它的全局读/写和读/写权限已经设置,所以其他任何应⽤程序可以看到它。
4. l中的显式权限声明
Android默认应⽤是没有任何权限去操作其他应⽤或系统相关特性的,应⽤在进⾏某些操作时都需要显式地去申请相应的权限。
元旦是什么节日
⼀般以下动作时都需要申请相应的权限:
A particular permission may be enforced at a number of places during your program’s operation:
At the time of a call into the system, to prevent an application from executing certain functions.
When starting an activity, to prevent applications from launching activities of other applications.
Both nding and receiving broadcasts, to control who can receive your broadcast or who can nd a broadcast to you.
When accessing and operating on a content provider.
Binding or starting a rvice.
在应⽤安装的时候,package installer会检测该应⽤请求的权限,根据该应⽤的签名或者提⽰⽤户来分配相应的权限。在程序运⾏期间是不检测权限的。如果安装时权限获取失败,那执⾏就会出错,不会提⽰⽤户权限不够。
⼤多数情况下,权限不⾜导致的失败会引发⼀个 SecurityException, 会在系统log(system log)中有相关记录。
5. 权限继承/UrID继承
当我们遇到apk权限不⾜时,我们有时会考虑写⼀个linux程序,然后由apk调⽤它去完成某个它没有权限完成的事情,很遗憾,这种⽅法是⾏不通的。前⾯讲过,android权限是经营在进程层⾯的,也就是说⼀个apk应⽤启动的⼦进程的权限不可能超越其⽗进程的权限(即apk的权限),即使单独运⾏某个应⽤有权限做某事,但如果它是由⼀个apk调⽤的,那权限就会被限制。实际上,android是通过给⼦进程分配⽗进程的UrID实现这⼀机制的。
常见权限不⾜问题分析
⾸先要知道,普通apk程序是运⾏在⾮root、⾮system层级的,也就是说看要访问的⽂件的权限时,看的是最后三位。另外,通过system/app安装的apk的权限⼀般⽐直接安装或adb install安装的apk的权限要⾼⼀些。
运⾏⼀个android应⽤程序过程中遇到权限不⾜,⼀般分为两种情况:
1. Log中可明显看到权限不⾜的提⽰永远的永远
此种情况⼀般是l中缺少相应的权限设置,查找权限列表就可解决,是最易处理的情况。
有时权限都加上了,但还是报权限不⾜,是什么情况呢?
Android系统有⼀些API及权限是需要apk具有⼀定的等级才能运⾏的。
加班工资基数⽐如 SystemClock.tCurrentTimeMillis()修改系统时间,WRITE_SECURE_SETTINGS权限,都是需要有system级的权限才⾏,也就是说UrID是system.
2. Log⾥没有报权限不⾜,⽽是⼀些其他Exception的提⽰,这也有可能是权限不⾜造成的。
⽐如:我们常会想读/写⼀个配置⽂件或其他⼀些不是⾃⼰创建的⽂件,常会报java.io.FileNotFoundException错误。
系统认为⽐较重要的⽂件⼀般权限设置的也会⽐较严格,特别是⼀些很重要的(配置)⽂件或⽬录。
如
r–r—- bluetooth bluetooth 935 2010-07-09 20:f
drwxrwx–x system system 2010-07-07 02:05 data
没有。
/data⽬录下存的是所有程序的私有数据,默认情况下android是不允许普通apk访问/data⽬录下内容的,通过data⽬录的权限设置可知,其他⽤户没有读的权限。
所以adb普通权限下在data⽬录下敲ls命令,会得到opendir failed, Permission denied的错误,通过代码file.listfiles()也⽆法获得data⽬录下的内容。
上⾯两种情况,⼀般都需要提升apk的权限,⽬前所知的apk能提升到的权限就是system(具体⽅法见:
共享UID
安装在设备中的每⼀个Android包⽂件(.apk)都会被分配到⼀个属于⾃⼰的统⼀的Linux⽤户ID,并且为它创建⼀个沙箱,以防⽌影响其他应⽤程序(或者其他应⽤程序影响它)。⽤户ID在应⽤程序安装到设备中时被分配,并且在这个设备中保持它的永久性。土豆炖鸡肉
通过Shared Ur id,拥有同⼀个Ur id的多个APK可以配置成运⾏在同⼀个进程中,所以默认就是可以互相访问任意数据。也可以配置成运⾏成不同的进程,同时可以访问其他APK的数据⽬录下的数据库和⽂件,就像访问本程序的数据⼀样。
对于⼀个APK来说,如果要使⽤某个共享UID的话,必须做三步:
1. 在Manifest节点中增加android:sharedUrId属性,⽐如android:sharedUrId=”android.uid.system”
2. 在Android.mk中增加LOCAL_CERTIFICATE的定义。如果增加了上⾯的属性但没有定义与之对应的LOCAL_CERTIFICATE的
话,APK是安装不上去的。提⽰错误是:Package
has no signatures that match tho in shared ur
android.uid.system;
ignoring!也就是说,仅有相同签名和相同sharedUrID标签的两个应⽤程序签名会被分配相同的⽤户ID。例如所有和
media/download相关的APK都使⽤dia作为sharedUrId的话,那么它们必须有相同的签名media。
3. 把APK的源码放到packages/apps/⽬录下,⽤mm进⾏编译。
举例说明⼀下:
系统中所有使⽤android.uid.system作为共享UID的APK,都会⾸先在manifest节点中增加
android:sharedUrId=”android.uid.system”,然后在Android.mk中增加 LOCAL_CERTIFICATE := platform。可以参见Settings等
思想道德素质系统中所有使⽤android.uid.shared作为共享UID的APK,都会在manifest节点中增加
android:sharedUrId=”android.uid.shared”,然后在Android.mk中增加 LOCAL_CERTIFICATE := shared。可以参见Launcher等
系统中所有使⽤dia作为共享UID的APK,都会在manifest节点中增加 android:sharedUrId=”dia”,然后在Android.mk中增加LOCAL_CERTIFICATE := media。可以参见Gallery等。
关于签名
build/target/product/curity⽬录中有四组默认签名供Android.mk在编译APK使⽤:
1. testkey:普通APK,默认情况下使⽤。
退刀槽标注2. platform:该APK完成⼀些系统的核⼼功能。经过对系统中存在的⽂件夹的访问测试,这种⽅式编译出来的APK所在进程的UID为
system。
3. shared:该APK需要和home/contacts进程共享数据。
4. media:该APK是media/download系统中的⼀环。
应⽤程序的Android.mk中有⼀个LOCAL_CERTIFICATE字段,由它指定⽤哪个key签名,未指定的默认⽤testkey.
system权限获取
以修改系统时间为例:
1. 在应⽤程序的l中的manifest节点中加⼊android:sharedUrId=”android.uid.system”这个属性。
2. 使⽤⽬标系统的platform密钥来重新给apk⽂件签名。
最后解释⼀下原理,⾸先加⼊android:sharedUrId=”android.uid.system”这个属性。通过Shared Ur id把程序的UID配成android.uid.system,也就是要让程序运⾏在系统进程中,这样就有权限来修改系统时间了。
只是加⼊UID还不够,如果这时候安装APK的话发现⽆法安装,提⽰签名不符,原因是程序想要运⾏在系统进程中还要有⽬标系统的platform key。⽤这两个key签名后apk才真正可以放⼊系统进程中。这也有⼀个问题,就是这样⽣成的程序只有在原始的Android系统或者是⾃⼰编译的系统中才可以⽤,因为这样的系统才可以拿到platform.pk8和platform.x509.pem两个⽂件,要是别家公司做的Android 上连安装都安装不了。试试原始的Android中的key来签名,程序在模拟器上运⾏OK,不过放到G3上安装直接提⽰”Package … has no signatures that match tho in shared ur android.uid.system”,这样也是保护了系统的安全。
第三⽅应⽤商店的安装权限
经过对第三⽅AppStore“应⽤汇”的测试,发现第三⽅AppStore默认⽆法获取安装权限,只有设置了“安全-允许安装不是从电⼦市场获取的的应⽤程序”(Settings. Secure.INSTALL_NON_MARKET_APPS)才⾏。默认安装应⽤程序需要获得android.permission.INSTALL_PACKAGES权限,这个权限只有platform key签名才能获取。如果设置了“安全-允许安装不是从电⼦市场获取的的应⽤程序”,系统在安装应⽤时就不会检查这个权限,第三⽅AppStore就可以安装应⽤程序