KC10330209-i04Android代码完整性校验_第1页
KC10330209-i04Android代码完整性校验_第2页
KC10330209-i04Android代码完整性校验_第3页
KC10330209-i04Android代码完整性校验_第4页
KC10330209-i04Android代码完整性校验_第5页
全文预览已结束

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Android代码完整性校验Android签名机制为了说明APK签名比对对软件安全的有效性,我们有必要了解一下Android APK的签名机制。为了更易于大家理解,我们从Auto-Sign工具的一条批处理命令说起。我们了解到,要签名一个没有签名过的APK,可以使用一个叫作Auto-sign的工具。Auto-sign工具实际运行的是一个叫做Sign.bat的批处理命令。用文本编辑器打开这个批处理文件,我们可以发现,实现签名功能的命令主要是这一行命令:这条命令的意义是:通过signapk.jar这个可执行jar包,以“testkey.x509.pem”这个公钥文件和“testkey.pk8”这个私

2、钥文件对“update.apk”进行签名,签名后的文件保存为“update_signed.apk”。对于此处所使用的私钥和公钥的生成方式,这里就不做进一步介绍了。这方面的资料大家可以找到很多。我们这里要讲的是signapk.jar到底做了什么。signapk.jar是Android源码包中的一个签名工具。由于Android是个开源项目,所以,很高兴地,我们可以直接找到signapk.jar的源码!路径为/build/tools/signapk/SignApk.java。对比一个没有签名的APK和一个签名好的APK,我们会发现,签名好的APK包中多了一个叫做META-INF的文件夹。里面有三个文

3、件,分别名为MANIFEST.MF、CERT.SF和CERT.RSA。signapk.jar就是生成了这几个文件(其他文件没有任何改变。因此我们可以很容易去掉原有签名信息)。通过阅读signapk源码,我们可以理清签名APK包的整个过程。1、 生成MANIFEST.MF文件:程序遍历update.apk包中的所有文件(entry),对非文件夹非签名文件的文件,逐个生成SHA1的数字签名信息,再用Base64进行编码。具体代码见这个方法:private static Manifest addDigestsToManifest(JarFile jar)关键代码如下:1 for (JarEntry

4、entry: byName.values() 2 String name = entry.getName();3 if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&4 !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&5 (stripPattern = null |!stripPattern.matcher(name).matches() 6 InputStream data =

5、jar.getInputStream(entry);7 while (num = data.read(buffer) > 0) 8 md.update(buffer, 0, num);9 10 Attributes attr = null;11 if (input != null) attr = input.getAttributes(name);12 attr = attr != null ? new Attributes(attr) : new Attributes();13 attr.putValue("SHA1-Digest", base64.encode(m

6、d.digest();14 output.getEntries().put(name, attr);15 16 之后将生成的签名写入MANIFEST.MF文件。关键代码如下:1 Manifest manifest = addDigestsToManifest(inputJar);2 je = new JarEntry(JarFile.MANIFEST_NAME);3 je.setTime(timestamp);4 outputJar.putNextEntry(je);5 manifest.write(outputJar);这里简单介绍下SHA1数字签名。简单地说,它就是一种安全哈希算法,类似于

7、MD5算法。它把任意长度的输入,通过散列算法变成固定长度的输出(这里我们称作“摘要信息”)。你不能仅通过这个摘要信息复原原来的信息。另外,它保证不同信息的摘要信息彼此不同。因此,如果你改变了apk包中的文件,那么在apk安装校验时,改变后的文件摘要信息与MANIFEST.MF的检验信息不同,于是程序就不能成功安装。2、 生成CERT.SF文件:对前一步生成的Manifest,使用SHA1-RSA算法,用私钥进行签名。关键代码如下:1 Signature signature = Signature.getInstance("SHA1withRSA");2 signature.

8、initSign(privateKey);3 je = new JarEntry(CERT_SF_NAME);4 je.setTime(timestamp);5 outputJar.putNextEntry(je);6 writeSignatureFile(manifest,7 new SignatureOutputStream(outputJar, signature);RSA是一种非对称加密算法。用私钥通过RSA算法对摘要信息进行加密。在安装时只能使用公钥才能解密它。解密之后,将它与未加密的摘要信息进行对比,如果相符,则表明内容没有被异常修改。3、 生成CERT.RSA文件:生成MANIF

9、EST.MF没有使用密钥信息,生成CERT.SF文件使用了私钥文件。那么我们可以很容易猜测到,CERT.RSA文件的生成肯定和公钥相关。CERT.RSA文件中保存了公钥、所采用的加密算法等信息。核心代码如下: je = new JarEntry(CERT_RSA_NAME); je.setTime(timestamp); outputJar.putNextEntry(je); writeSignatureBlock(signature, publicKey, outputJar);其中writeSignatureBlock的代码如下: private static void writeSign

10、atureBlock( Signature signature, X509Certificate publicKey, OutputStream out) throws IOException, GeneralSecurityException SignerInfo signerInfo = new SignerInfo( new X500Name(publicKey.getIssuerX500Principal().getName(), publicKey.getSerialNumber(), AlgorithmId.get("SHA1"), AlgorithmId.ge

11、t("RSA"), signature.sign(); PKCS7 pkcs7 = new PKCS7( new AlgorithmId AlgorithmId.get("SHA1") , new ContentInfo(ContentInfo.DATA_OID, null), new X509Certificate publicKey , new SignerInfo signerInfo ); pkcs7.encodeSignedData(out); 好了,分析完APK包的签名流程,我们可以清楚地意识到:1、 Android签名机制其实是对APK包完

12、整性和发布机构唯一性的一种校验机制。2、 Android签名机制不能阻止APK包被修改,但修改后的再签名无法与原先的签名保持一致。(拥有私钥的情况除外)。3、 APK包加密的公钥就打包在APK包内,且不同的私钥对应不同的公钥。换句话言之,不同的私钥签名的APK公钥也必不相同。所以我们可以根据公钥的对比,来判断私钥是否一致。APK签名比对的实现方式好了,通过Android签名机制的分析,我们从理论上证明了通过APK公钥的比对能判断一个APK的发布机构。并且这个发布机构是很难伪装的,我们暂时可以认为是不可伪装的。有了理论基础后,我们就可以开始实践了。那么如何获取到APK文件的公钥信息呢?因为And

13、roid系统安装程序肯定会获取APK信息进行比对,所以我们可以通过Android源码获得一些思路和帮助。源码中有一个隐藏的类用于APK包的解析。这个类叫PackageParser,路径为frameworksbasecorejavaandroidcontentpmPackageParser.java。当我们需要获取APK包的相关信息时,可以直接使用这个类,下面代码就是一个例子函数: private PackageInfo parsePackage(String archiveFilePath, int flags) PackageParser packageParser = new Packag

14、eParser(archiveFilePath); DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); final File sourceFile = new File(archiveFilePath); PackageParser.Package pkg = packageParser.parsePackage( sourceFile, archiveFilePath, metrics, 0); if (pkg = null) return null; packageParser.collectCer

15、tificates(pkg, 0); return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0); 其中参数archiveFilePath指定APK文件路径;flags需设置PackageManager.GET_SIGNATURES位,以保证返回证书签名信息。具体如何通过PackageParser获取签名信息在此处不做详述,具体代码请参考PackageParser中的public boolean collectCertificates(Package pkg, int flags)和private Certificate l

16、oadCertificates(JarFile jarFile, JarEntry je, byte readBuffer)方法。至于如何在Android应用开发中使用隐藏的类及方法,可以参看我的这篇文章:Android应用开发中如何使用隐藏API。紧接着,我们就可以通过packageInfo.signatures来访问到APK的签名信息。还需要说明的是 Android中Signature和Java中Certificate的对应关系。它们的关系如下面代码所示: pkg.mSignatures = new Signaturecerts.length;2 for (int i=0; i<N;

17、 i+) pkg.mSignaturesi = new Signature( certsi.getEncoded(); 也就是说signature = new Signature(certificate.getEncoded(); certificate证书中包含了公钥和证书的其他基本信息。公钥不同,证书肯定互不相同。我们可以通过certificate的getPublicKey方法获取公钥信息。所以比对签名证书本质上就是比对公钥信息。OK,获取到APK签名证书之后,就剩下比对了。这个简单,功能函数如下所示: private boolean IsSignaturesSame(Signature

18、s1, Signature s2) if (s1 = null) return false; if (s2 = null) return false; HashSet<Signature> set1 = new HashSet<Signature>(); for (Signature sig : s1) set1.add(sig); HashSet<Signature> set2 = new HashSet<Signature>(); for (Signature sig : s2) set2.add(sig); / Make sure s2 contains all

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论