手机删除了一个getsystemservice service文件,无法安装apk但可以安装nbs文件,什么意思?

&nbsp&nbsp &
&nbsp&nbsp
&nbsp&nbsp &
&nbsp&nbsp
初次接触androidstudio大家可能总会拿它与eclipse做比较,有很多习惯与eclipse不同,现在就跟大家分享一下我是如何加载第三方软件包的。初次建一个android项目打开的页面如下所示:现单击项目名称如下图所示:第二步:单击上图的app(即蓝色区域),得到下图所示:这个时候我们就看到了libs的文件夹,双击libs文件夹,得到下图:复制需要加载的ksoap包,粘贴在libs下,会弹出如下对话框:点击ok,得出以下效果图:将鼠标放在包上,选择点右键会出现下图所示
[Unity]Unity3D如何搭建Android开发环境,并生成apk1、前言跨平台是&unity3D引擎引以为傲的黑科技特性,在这里,我们将学习如何使用Unity直接生成能在安卓上安装并运行的软件包xxx.apk。2、原理与流程由于Unity需要直接生成apk文件,所以其在内部必须使用AndroidSDK(ADK)&来生成程序,而AndroidSDK则需要安装JavaSDK(JDK)为前提,所以我们需要做的就是:1、安装与配置JDK2、安装与配置ADK
ViewPager是谷歌官方提供的兼容低版本安卓设备的软件包,里面包含了只有在安卓3.0以上可以使用的api。Viewpager现在也算是标配了,如果一个App没有用到ViewPager感觉还是比较罕见的,导航和页面菜单常用的功能,ViewPager与LisstView类似,ListView经常会用到BaseAdapter,ViewPager则继承的是PagerAdapter,关于简单的使用可以去官网可以http://developer.android.com/referen
android的后台运行在很多service,它们在系统启动时被SystemServer开启,支持系统的正常工作,比如MountService监听是否有SD卡安装及移除,ClipboardService提供剪切板功能,PackageManagerService提供软件包的安装移除及查看等等,应用程序可以通过系统提供的Manager接口来访问这些Service提供的数据。&&&&getSystemService是Android很重要的一个
这是谷歌官方给我们提供的一个兼容低版本安卓设备的软件包,里面包含了只能在android3.0以上可以使用的api。ViewPager的功能就是可以使视图滑动。分三个步骤来使用它:1.在布局文件里加入&android.support.v4.view.ViewPagerandroid:id=&@+id/viewpager&android:layout_width=&wrap_content&android:layout_height=
这是谷歌官方给我们提供的一个兼容低版本安卓设备的软件包,里面包囊了只有在安卓3.0以上可以使用的api。而viewpager就是其中之一利用它,我们可以做很多事情,从最简单的导航,到页面菜单等等。那如何使用它呢,与LisstView类似,我们也需要一个适配器,他就是PagerAdapter。看一下api的图片,ViewPager的功能就是可以使视图滑动,就像Lanucher左右滑动那样。分三个步骤来使用它:1.在住布局文件里加入&android.support.v4.v
使用Eclipse做Android开发,需要先在Eclipse上安装ADT(AndroidDevelopmentTools)插件。1.安装JDK1.7JDK官网http://www.oracle.com/technetwork/java/javase/downloads/index.html,下载JDK7软件包,我安装的是1.7.0_55版本。安装完JDK后,配置下JDK环境即可。2.下载EclipseEclipse官网:http://www.eclipse.org/down
1.Re-installationfailedduetodifferentapplicationsignatures.将原来的软件包删除掉,然后重新安装一次就好了。在命令行上运行:adbuninstall,这里的package_name也就是AndroidManifest.xml里面元素package属性的数据值。例如:adbuninstallorg.andriod.demo。关于删除也可以参考6.如何删除软件包这个部分的说明2.emulator:ERROR:unknownv
1.Re-installationfailedduetodifferentapplicationsignatures.将原来的软件包删除掉,然后重新安装一次就好了。在命令行上运行:adbuninstall&package_name&,这里的package_name也就是AndroidManifest.xml里面&manifest/&元素package属性的数据值。例如:adbuninstallorg.andriod.demo。关于删除也可以参考6.如何
默认bashshell的$PS1值:/[/e]0;//h:/w/a/]${debian_chroot:+($debian_chroot)}//h:/w/$Ubuntu设置PS1属性达到SHELL提示符下短路径默认情况下,Ubuntu终端会输出完整路径,在路径名很长的时候,提示方式很不友好,通过以下步骤修改PS1变量的设置,可以让终端输出短路径。修改用户目录下的.bashrc文件aliyunzixun@
ViewPager这个组件是谷歌官方给我们提供的一个兼容低版本安卓设备的软件包,里面包含了只有在安卓3.0以上可以使用的api。而viewpager就是其中之一,利用它我们可以做很多事情,从最简单的导航,到页面菜单等等。它在android.support.v4包中。API:http://developer.android.com/intl/zh-cn/reference/android/support/v4/view/ViewPager.html1,实现程序导航页(初次使用屏
&&&&&Android系统下的apk程序都是通过名为PackageManagerService的包管理服务来管理的。PacketManagerService是安卓系统的一个重要服务,由SystemServer启动,主要实现apk程序包的解析,安装,更新,移动,卸载等服务。不管是系统apk(/system/app),还是我们手工安装上去的,系统所有的apk都是由其管理的。&&&&&nbs
目前配置的环境是Android的42开发环境。目前使用ADT包开发。1、下载文件(ADTbundleforwindows-ADT捆绑的软件包)http://developer.android.com/sdk/index.html点击downloadSDKADTBundleforWindows,出来以下对话框,另外,如果安装了eclipse的,可以下载USEANEXISRINGIDE。选择32-bit的下载。下载完成后解压,如图打开eclipse文件夹,(如果安装了JDK,你就
之前做了一个基于ffmpeg的软解播放器,熟悉了NDK开发的配置环境过程,但是由于太忙一直没有时间写笔记。首先,介绍一下在这里所参与协作的软件包:1、JDK:这个软件被Eclipse依赖。2、Eclipse:也就是我们所需要配置的目标IDE环境了。版本可以选择EclipseSE版,也可以选择J2EE版。3、AndroidSDK:也就是Android开发所需要的Java包以及一些编译工具等。4、AndroidNDK:包含了一些头文件以及交叉编译器。5、ADT(AndroidDe
&在做项目的过程中,我用到了腾讯微博的分享功能!但是呢,我只要一个发送微博的功能,在网上找了好多,都没有合适的!如果直接使用腾讯的SDK的话,软件包的体积会很大的,我们可以只使用有用的东西!因此我整理了一下,现在做成了一个教程:效果图如下:网上有一个类似的教程!可是里面的功能不是很多,我现在写的这个东西,你们可以直接使用到项目中,只要简单修改写就可以了!例如我增加了字数的判断,在发布微博的时候,如果你的字数超过140字就用红色字体标注,并把发送按钮置为不可见!让用户
编译环境:Vmwareworkstation7&+ubuntu10.0432位&&1、安装androidsdk&2、32和64位均需要安装的软件包:git-coregnupgflexbisongperflibsdl1.2-devlibesd0-devlibwxgtk2.6-devsquashfs-toolsbuild-essentialzipcurllibncurses5-devzlib1g-devsun-java6-jdkpngcru
&老早之前,写了一个android软件包静默安装的应用,放在工作空间很久了,今天整理项目的时候发现了,拿出来晒晒!&&软件实现的功能:当需要静默安装(即不弹出安装确认对话框,后台自动安装)时,启动安装服务,将待安装的软件包的路径放置到intent中,然后启动安装服务即可,在软件成功安装以后,会发送广播,你只需要接受对应的广播,即可获知软件是否安装成功!&&软件的关键代码:Java代码&&&&nbs
系统环境的要求可以参考http://androidappdocs.appspot.com/sdk/requirements.html一,下载所软件包&&(1)JDK&要求JDK1.6+,建议JDK7.X下载网址http://java.sun.com/javase/downloads/index.jsp或http://www.oracle.com/technetwork/java/javase/downloads/jdk-6u27-download
用SAX(simpleAPIforXML)解析方式逐行扫描文档,可以在任一时刻停止解析,但是操作复杂、使用其SAX向文档添加或者删除内容比较困难。它既是一种接口,也是一个软件包。SAX工作原理:对文档进行顺序扫描,当扫描到文档开始与结束、元素开始与结束,产生事件通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。文档处理事件、元素事件、DTD或Schema事件、错误事件SAX接口:ContentHandler、ErrorHandler、DTDHand
本来打算在ubuntu中装openjdk的,但是在新立得软件包出现了无法下载,到官网去了也没找到其下载链接,恼火了就直接转向sun的jdk,发现unbuntu中移除了下载sun公司的jdk链接,只能重建链接啦!1,编辑源列表:~$sudovim/etc/apt/sources.list,在最后添加一行:debhttp://archive.canonical.com/lucidpartner2,更新:~$sudoapt-getupdate3.安装jdk:sudoapt-geti
你可能还喜欢
你可能感兴趣
阿里云教程中心为您免费提供信息,所有相关内容均不代表阿里云的意见!投稿删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内答复
售前咨询热线
支持与服务
资源和社区
关注阿里云
International求windows&xp&sp3系统的东亚语言安装包,主要是i386/lang文件里面的东亚语言安装包。
我安装office&2010的时候总是提示系统语言不受安装程序语言支持,想要个东亚语言安装程序包。
14-09-28 &Android签名机制及PMS中校验签名
一、签名机制
众所周知,在Android系统中,应用想要安装到设备中,必须要有签名才行,及时是debug的时候,开发工具也会对要运行的应用自动签名,那么我们先来了解一下这个签名究竟是什么。
首先Android系统为了防止以安装的应用被篡改,推出来的签名自检机制,来维护应用的安全性,可以说,签名就是一个保护个人应用不受侵害的一种机制。而且这里面说道了自检,也就是说,在Android系统中,应用的签名在安装的过程中是自行检查的,这点在本文的下面会详细说道。
那么,我们先来了解一下Android签名的整个过程,这个过程大体可以分为三个步骤,也对应了Android系统在校验签名时的三个步骤:
1、android应用打包apk的时候,会先对应用中所有的文件进行遍历,做一次SHA1算法,也就是计算出文件的摘要信息,然后再用Base64进行编码,然后将这些信息写入到MANIFEST.MF文件中。这个文件在META-INF文件夹下面,解压APK文件时就会看见了。当然,在对APK所有文件进行SHA1算法的时候,是不包括META-INF文件夹的(这句是废话)。
下文是支付宝的MANIFEST.MF文件打开后的一部分(用记事本就可以打开);
Manifest-Version: 1.0
Created-By: 1.7.0_67 (Oracle Corporation)
Name: assets/emoji/emoji_195.png
SHA1-Digest: 2YVKFrBF45WOANd9G7zdpbAm6w0=
Name: assets/emoji/emoji_356.png
SHA1-Digest: VlPA09nbS5e3iae9WvAMq8O88BM=
Name: res/layout/envelope_notify.xml
SHA1-Digest: r4LUgyo6YoeqTvKQUyME82+XINs=
Name: lib/armeabi/libsgavmp.so
SHA1-Digest: DOwWbBL42qy+Z16EggSnCNYseDI=
从上文中可以看出,在MANIFEST.MF文件中,针对应用中每个文件的SHA1算法都会以键值对的方式写入。这是为了防止APK内容被篡改。想要验证的话,可以通过安装HashTab软件(下载地址:
),然后找到对应的文件,右键用HashTab打开,就能看到对应的SHA1值,再到Base64网站上转码后对比一下就可以了(网址:
2、第二个步骤是将上文的MANIFEST.MF文件进行一次SHA1计算,然后同样Base64转码后写入到CERT.SF文件中,然后读取MANIFEST.MF文件中子条目的值再次SHA1计算,Base64转码写入到这个文件中。这个步骤同下面的第三个步骤统称为签名,都是用的签名工具来完成的。
Signature-Version: 1.0
Created-By: 1.0 (Android SignApk)
SHA1-Digest-Manifest: zg9ZRuDj9mgYGA7zbHu5lysUuxc=
Name: assets/emoji/emoji_195.png
SHA1-Digest: +HilKtU8N3AsfscyZ/3uHBc/5n4=
Name: assets/emoji/emoji_356.png
SHA1-Digest: 50goYIrW1+7aVHdrwsihe5t06pY=
Name: res/layout/envelope_notify.xml
SHA1-Digest: 1qtgFSOLlupySOaKR7m9pEgYrZM=
上文是支付宝应用中CERT.SF文件中的部分内容,我们可以发现确实和MANIFEST.MF文件中的内容类似(有的朋友可能会觉得这个步骤是重复的,下文中我会进行解释)。
3、第三个步骤是关键的步骤了,也就是对APK进行加密的过程。我们在打包签名的时候,开发工具会让我们使用一个私钥文件,如果没有就会让我们去创建一个。在Eclipse中,默认格式是.keystore文件,而在Android Studio中默认格式是.jks文件。这两个文件都是私钥库文件,里面存储了一些信息,包括所有者、有效期、证书指纹及算法等。有了私钥文件后,就可以对第二个步骤中生成的CERT.SF文件进行加密了,加密后会生成CERT.RSA文件。在Android中,总共有两种签名工具,一个是jarsigner,需要的是.keystore文件,而另一个是signapk签名用的是pk8、x509.pem文件(keystore文件可以和pk8、x509.pem文件互相转化)。这两个签名工具不同的是,jarsigner签名后生成的.SF和.RSA的名字是私钥库中别名的名字,而signapk生成的两个文件名则是默认的CERT。
生成.keystore文件是用的keytool.exe生成的,这个文件在jdk的bin目录下。生成.keystore的指令如下(最好手打,有时候编码问题,直接复制不一定能过):
keytool -genkey -v -keystore highball-key.keystore(文件名) -alias highball-key(别名) -keyalg RSA -keysize 2048 -validity 10000
查看私钥文件的指令如下:
keytool -list -keystore a.keystore(keystore文件) -alias a(别名) -v
从上图中可以看到私钥库文件中的内容。这个文件是签名机制中最重要的文件,一定要保存好,如果丢失,那么上架的应用想要更新可就麻烦了。
私钥文件已经有了,就可以对APK进行加密了。加密的指令如下:
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore a.keystore(keystore文件) a.apk(apk文件) a-key(别名)
其实这一个指令可以完成整个这三个步骤,因为要对应到Android系统的签名校验原理,所以分开来说明。
生成.RSA文件后用记事本打开是乱码的,可以将后缀名改成.p7r。然后就可以打开查看了,截图如下:
从上图可以看出.RSA文件包含了加密后的公钥和指纹,这两个信息是非常重要的,在Android系统对apk签名校验的时候也要用到。手动加密完之后最好对apk进行对其操作:
zipalign -v 4 a.apk
a-release.apk(新的apk的命名)
校验对齐是否成功:
zipalign -c -v 4 a-release.apk(apk名)
二、多重签名
Android系统是支持多重签名的,这是在签名校验时,系统只是对相同名的.SF文件和.RSA文件进行校验,而且是在while循环中进行的校验,也就是说可以不止一对.SF和.RSA文件,详情在下文中会有说明。
如果要进行多重签名,那么经过本人多次试验后得出的结果是,不能用Eclipse或者Android Studio签名后的apk来进行二次签名,而是要导出未签名的apk。
导出未签名的apk,在Android Studio的操作如下图所示:
在Gradle视图中,找到对应的项目下的assemble,执行双击操作,然后就会在module所在文件夹的outputs\apk文件夹中出现未签名的apk。这个操作很简单就不详细描述了,得到的apk文件中是没有META-INF文件夹的。
接下来就可以按照前文介绍的操作,生成两个密钥库文件(.keystore文件)。然后对未签名apk进行两次签名操作,得到了两次签名的apk文件,其META-INF文件夹内容如下:
从上图我们可以发现,MANIFEST.MF文件只有一个,而.SF和.RSA文件则是有两对,名字是对应密钥库文件的别名。OK多重签名就成功搞定了,下面来看看在Android系统中,是如何针对应用签名进行校验的。
三、Android签名校验
Android系统下,针对应用的安装是在PMS(Package Manager Service)中完成的, PMS是一个比较庞大的系统服务,我们今天只研究PMS中关于签名校验的部分。同apk签名三步骤相对应,PMS中对签名校验也是分成了三个步骤,先是 验证CERT.SF文件的摘要信息和CERT.RSA中的签名信息数据是否一致、验证MANIFEST.MF文件和CERT.SF文件是否匹配,最后是校验 APK 中每个文件的算法同 MANIFEST.MF 文件中是否一致。
1、校验CERT.SF文件和CERT.RSA文件
首先说明一下校验这两个文件的原理。Android系统对这两个文件的校验,是拿到.RSA文件的流对象,用ASN1解码后拿到签名数据,然后从签名信息中分别获取相关信息(发布机构、序列号、签名算法、公钥等)。然后通过公钥对签名进行解密,获得了解密后的数据同.SF文件进行对比。
Android 系统对应用的安装过程是在 PackageManagerService 类中通过发送 handler 来处理针对包的一些消息的 , 比如说清理安装包 , 发送安装等 , 其中针对签名校验也在这里处理。
在PackageManagerService.java中执行安装的方法是installPackageLI(args,res);在这个方法中获取了PackageParser对象,PackageParser.java是用来解析APK包的,也就是说签名信息校验的工作是在这个类中开始进行的。而执行校验的方法是PackageParser.java中的collectCertificates方法:
private static void collectCertificates(Package pkg, File apkFile, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
StrictJarFile jarFile = null;
jarFile = new StrictJarFile(apkPath);
final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
if (manifestEntry == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Package " + apkPath + " has no manifest");
这里面new了一个StrictJarFile对象,这个类的构造方法中完成了CERT.SF文件和CERT.RSA文件中关于证书的校验。因为在这个构造方法中有个Boolean类型的变量的赋值,就是完成证书校验的关键。那么我们看一下这个变量是如何赋值的:
public StrictJarFile(String fileName) throws IOException {
this.nativeHandle = nativeOpenJarFile(fileName);
this.raf = new RandomAccessFile(fileName, "r");
HashMap&String, byte[]& metaEntries = getMetaEntries();
this.manifest = new Manifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
this.verifier = new JarVerifier(fileName, manifest, metaEntries);
isSigned = verifier.readCertificates() && verifier.isSignedJar();
} catch (IOException ioe) {
nativeClose(this.nativeHandle);
guard.open("close");
从截图中可以看到,总共执行了两个方法,先看一下readCertificates方法:
synchronized boolean readCertificates() {
if (metaEntries.isEmpty()) {
return false;
Iterator&String& it = metaEntries.keySet().iterator();
while (it.hasNext()) {
String key = it.next();
if (key.endsWith(".DSA") || key.endsWith(".RSA") || key.endsWith(".EC")) {
verifyCertificate(key);
it.remove();
return true;
从方法中可以看出,这个方法首先判断一下是否有MANIFEST.MF文件,如果没有就返回false,那么isSigned就会被赋值为false(这个变量的赋值会在后续方法中作为校验的参数)。然后会去找证书文件,而在android的项目中,一般多采用的是RSA加密算法,也就是生成的.RSA文件,所以这个方法会把.RSA文件找到,执行verifyCertificate方法:
private void verifyCertificate(String certFile) {
String signatureFile = certFile.substring(0, certFile.lastIndexOf('.')) + ".SF";
byte[] sfBytes = metaEntries.get(signatureFile);
if (sfBytes == null) {
byte[] manifestBytes = metaEntries.get(JarFile.MANIFEST_NAME);
if (manifestBytes == null) {
byte[] sBlockBytes = metaEntries.get(certFile);
Certificate[] signerCertChain = JarUtils.verifySignature(
new ByteArrayInputStream(sfBytes),
new ByteArrayInputStream(sBlockBytes));
if (signerCertChain != null) {
certificates.put(signatureFile, signerCertChain);
} catch (IOException e) {
} catch (GeneralSecurityException e) {
throw failedVerification(jarName, signatureFile);
这是方法的一部分,通过.RSA文件的名字找到对应的.SF文件,然后在获取MANIFEST.MF文件和.RSA文件,到这里三个文件就都拿到了。拿到三个文件数据后,会调用JarUtils.verifySignature方法, 这个方法,参数是.SF文件和.RSA文件的内存流对象;这个方法中通过用X509文件解析了.RSA文件,这个方法最终的目的是获取到RSA文件中的证书;然后同.SF文件的数据进行对比,如果不相同会抛出异常:
if (md == null && daName != null) {
md = MessageDigest.getInstance(daName);
if (md == null) {
return null;
byte[] computedDigest = md.digest(sfBytes);
if (!Arrays.equals(existingDigest, computedDigest)) {
throw new SecurityException("Incorrect MD");
然后回到JarVerifier.java的verifyCertificate方法中,会将之前拿到的签名数据封装到集合中:
if (signerCertChain != null) {
certificates.put(signatureFile, signerCertChain);
在创建StrictJarFile对象的时候,就完成了对.RSA和.SF文件的校验操作。
2 、校验MANIFEST.MF文件和.SF文件对应的摘要值
在JarVerifier.java中的verifyCertificate方法中,会继续执行校验MANIFEST.MF文件和.SF文件的操作:
private void verifyCertificate(String certFile) {
Attributes attributes = new Attributes();
HashMap&String, Attributes& entries = new HashMap&String, Attributes&();
ManifestReader im = new ManifestReader(sfBytes, attributes);
im.readEntries(entries, null);
} catch (IOException e) {
if (attributes.get(Attributes.Name.SIGNATURE_VERSION) == null) {
boolean createdBySigntool = false;
String createdBy = attributes.getValue("Created-By");
if (createdBy != null) {
createdBySigntool = createdBy.indexOf("signtool") != -1;
if (mainAttributesEnd & 0 && !createdBySigntool) {
String digestAttribute = "-Digest-Manifest-Main-Attributes";
if (!verify(attributes, digestAttribute, manifestBytes, 0, mainAttributesEnd, false, true)) {
throw failedVerification(jarName, signatureFile);
String digestAttribute = createdBySigntool ? "-Digest" : "-Digest-Manifest";
if (!verify(attributes, digestAttribute, manifestBytes, 0, manifestBytes.length, false, false)) {
Iterator&Map.Entry&String, Attributes&& it = entries.entrySet().iterator();
while (it.hasNext()) {
Map.Entry&String, Attributes& entry = it.next();
Manifest.Chunk chunk = manifest.getChunk(entry.getKey());
if (chunk == null) {
if (!verify(entry.getValue(), "-Digest", manifestBytes,
chunk.start, chunk.end, createdBySigntool, false)) {
throw invalidDigest(signatureFile, entry.getKey(), jarName);
metaEntries.put(signatureFile, null);
signatures.put(signatureFile, entries);
上面代码中,前面一部分基本是校验的准备工作,主要是封装数据,真正的校验操作是上文中的verify方法:
private boolean verify(Attributes attributes, String entry, byte[] data,
int start, int end, boolean ignoreSecondEndline, boolean ignorable) {
for (int i = 0; i & DIGEST_ALGORITHMS. i++) {
String algorithm = DIGEST_ALGORITHMS[i];
String hash = attributes.getValue(algorithm + entry);
if (hash == null) {
md = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
if (ignoreSecondEndline && data[end - 1] == '\n' && data[end - 2] == '\n') {
md.update(data, start, end - 1 - start);
md.update(data, start, end - start);
byte[] b = md.digest();
byte[] hashBytes = hash.getBytes(StandardCharsets.ISO_8859_1);
return MessageDigest.isEqual(b, Base64.decode(hashBytes));
调用这个方法就完成了MANIFEST.MF文件和.SF文件的对比.
3、校验APK文件摘要值同MANIFEST.MF文件中对应值是否相同
接下来回到PackageParser.java中的collectCertificates方法,创建 StrictJarFile对象后, 会检查一下是否有清单文件(AndroidManifest.xml),如果没有则直接抛出异常。然后遍历查询到META-INF文件
private static void collectCertificates(Package pkg, File apkFile, int flags)
throws PackageParserException {
final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
if (manifestEntry == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Package " + apkPath + " has no manifest");
final List&ZipEntry& toVerify = new ArrayList&&();
toVerify.add(manifestEntry);
if ((flags & PARSE_IS_SYSTEM) == 0) {
final Iterator&ZipEntry& i = jarFile.iterator();
while (i.hasNext()) {
final ZipEntry entry = i.next();
if (entry.isDirectory()) continue;
if (entry.getName().startsWith("META-INF/")) continue;
if (entry.getName().equals(ANDROID_MANIFEST_FILENAME)) continue;
toVerify.add(entry);
for (ZipEntry entry : toVerify) {
final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
if (ArrayUtils.isEmpty(entryCerts)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Package " + apkPath + " has no certificates at entry "
+ entry.getName());
final Signature[] entrySignatures = convertToSignatures(entryCerts);
if (pkg.mCertificates == null) {
pkg.mCertificates = entryC
pkg.mSignatures = entryS
pkg.mSigningKeys = new ArraySet&PublicKey&();
for (int i=0; i & entryCerts. i++) {
pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
if (!Signature.areExactMatch(pkg.mSignatures, entrySignatures)) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath
+ " has mismatched certificates at entry "
+ entry.getName());
} catch (GeneralSecurityException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
"Failed to collect certificates from " + apkPath, e);
} catch (IOException | RuntimeException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath, e);
} finally {
closeQuietly(jarFile);
对toVerify集合遍历, 这里面涉及到了最关键的方法:loadCertificates方法。
private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry)
throws PackageParserException {
InputStream is = null;
is = jarFile.getInputStream(entry);
readFullyIgnoringContents(is);
return jarFile.getCertificateChains(entry);
} catch (IOException | RuntimeException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed reading " + entry.getName() + " in " + jarFile, e);
} finally {
IoUtils.closeQuietly(is);
先看一下getInputStream方法:
public InputStream getInputStream(ZipEntry ze) {
final InputStream is = getZipInputStream(ze);
if (isSigned) {
JarVerifier.VerifierEntry entry = verifier.initEntry(ze.getName());
if (entry == null) {
return new JarFile.JarFileInputStream(is, ze.getSize(), entry);
在这个方法中,显示根据isSigned进行判断(上文中已经说明),然后获取的VerifierEntry对象。在initEntry方法:
VerifierEntry initEntry(String name) {
ArrayList&Certificate[]& certChains = new ArrayList&Certificate[]&();
Iterator&Map.Entry&String, HashMap&String, Attributes&&& it = signatures.entrySet().iterator();
while (it.hasNext()) {
Map.Entry&String, HashMap&String, Attributes&& entry = it.next();
HashMap&String, Attributes& hm = entry.getValue();
if (hm.get(name) != null) {
String signatureFile = entry.getKey();
Certificate[] certChain = certificates.get(signatureFile);
if (certChain != null) {
certChains.add(certChain);
if (certChains.isEmpty()) {
return null;
Certificate[][] certChainsArray = certChains.toArray(new Certificate[certChains.size()][]);
for (int i = 0; i & DIGEST_ALGORITHMS. i++) {
final String algorithm = DIGEST_ALGORITHMS[i];
final String hash = attributes.getValue(algorithm + "-Digest");
if (hash == null) {
byte[] hashBytes = hash.getBytes(StandardCharsets.ISO_8859_1);
return new VerifierEntry(name, MessageDigest.getInstance(algorithm), hashBytes,
certChainsArray, verifiedEntries);
} catch (NoSuchAlgorithmException ignored) {
return null;
这个方法的目的是构造VerifierEntry对象,所以这个方法主要进行的就是准备构建这个对象的参数。这些参数分别是要验证文件的名字。第二个是计算摘要的对象。第三个参数是对应文件的摘要值,从代码中可以看到是ISO_8859_1格式的。第四个参数是证书链。最后一个参数是已经验证过的文件列表。
VerifierEntry对象创建成功后,会返回JarFileInputStream流对象。然后回到PackageParser.java的loadCartificates方法中,会继续走到readFullyIgnoringContents方法:
public static long readFullyIgnoringContents(InputStream in) throws IOException {
byte[] buffer = sBuffer.getAndSet(null);
if (buffer == null) {
buffer = new byte[4096];
int n = 0;
int count = 0;
while ((n = in.read(buffer, 0, buffer.length)) != -1) {
sBuffer.set(buffer);
这里面就是正常的读取但是我们从第一点中知道 , 这个 InputStream 对象实际上是 JarInputStream 对象 , 所以这里面 read 执行的方法实际上是在 JarInputStream 中重写的 read 方法。
public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
if (done) {
return -1;
if (count & 0) {
int r = super.read(buffer, byteOffset, byteCount);
if (r != -1) {
int size =
if (count & size) {
size = (int)
entry.write(buffer, byteOffset, size);
count = 0;
if (count == 0) {
done = true;
entry.verify();
done = true;
entry.verify();
return -1;
verify方法:
void verify() {
byte[] d = digest.digest();
if (!MessageDigest.isEqual(d, Base64.decode(hash))) {
throw invalidDigest(JarFile.MANIFEST_NAME, name, name);
verifiedEntries.put(name, certChains);
OK,通过这些方法的执行,便可以校验了APK中各文件摘要值同MANIFEST.MF中对应的值是否相同 。
最后回到PackageParser.java中的collectCertificates方法,下面这个方法是上文中所有方法的初始位置:
public void collectCertificates(Package pkg, int flags) throws PackageParserException {
pkg.mCertificates = null;
pkg.mSignatures = null;
pkg.mSigningKeys = null;
collectCertificates(pkg, new File(pkg.baseCodePath), flags);
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (String splitCodePath : pkg.splitCodePaths) {
collectCertificates(pkg, new File(splitCodePath), flags);
执行完了重载的collectCertificates方法后,会去判断是否是一次安装,如果不是第一次安装,会再次执行 collectCertificates方法,如果签名不一致,会抛出异常:
private static void collectCertificates(Package pkg, File apkFile, int flags)
throws PackageParserException {
for (ZipEntry entry : toVerify) {
final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
if (ArrayUtils.isEmpty(entryCerts)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Package " + apkPath + " has no certificates at entry "
+ entry.getName());
final Signature[] entrySignatures = convertToSignatures(entryCerts);
if (pkg.mCertificates == null) {
pkg.mCertificates = entryC
pkg.mSignatures = entryS
pkg.mSigningKeys = new ArraySet&PublicKey&();
for (int i=0; i & entryCerts. i++) {
pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
if (!Signature.areExactMatch(pkg.mSignatures, entrySignatures)) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath
+ " has mismatched certificates at entry "
+ entry.getName());
} catch (GeneralSecurityException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
"Failed to collect certificates from " + apkPath, e);
} catch (IOException | RuntimeException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath, e);
} finally {
closeQuietly(jarFile);
总结一下,在APK签名的时候,首先会遍历APK所有的文件,生成对应的摘要数据写入到META-INF文件夹的MENIFEST.MF文件中,接下来会将MENIFEST.MF文件的摘要值和它里面每个数据块的值重新做一次摘要算法,然后写入到.SF文件中.最后是通过秘钥库文件对.SF文件进行加密,然后将公钥数据,签名数据等信息写入到.RSA文件中.如上便是android下对APK的签名机制.
而在android中对APK的签名校验则顺序相反,先是解开.RSA文件,提取公钥和签名数据,找到对应的.SF文件进行对比.然后进行MENIFEST.MF和.SF文件的对比,最后则是对apk中各个文件的摘要数据同MANIFEST.MF文件进行校验.不过在最后还会校验在覆盖安装的时候,会再次执行校验流程.
看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?}

我要回帖

更多关于 systemctl service 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信