android7.0及以上版本签名校验过程详解

 

    对于新的签名方案APK Signature Scheme v2,在这篇文章中已经有详细的介绍http://www.tuicool.com/articles/bURRVrj。从这篇文章中可以知道,新的签名方案与旧的签名方案之间的对比是:

                                             

                                                                                                 图1

 

新的签名方案生成与旧的签名方案相比,在zip文件中新增了一个APK Signing Block区块。使用新的签名方案以后,在apk下所有的文件中所做的修改,都会导致在android7.0系统上安装失败。那么android7.0上的签名校验过程是怎样的呢?为什么apk下任意文件修改都会导致签名校验失败呢?网上很多文章都对7.0之前的签名校验过程进行了讲解,想了解7.0之前的签名校验过程可以参考文章http://blog.csdn.net/roland_sun/article/details/42029019 但是很少有文章对7.0及更高版本上的签名校验过程进行分析,所以在这篇文章中,主要针对7.0及以上的签名校验过程进行分析,通过分析7.0系统源码的方式,来向大家描述一下这一过程。
(1) Android平台上所有应用程序安装都是由PackageManangerService(代码位于frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java)来管理的,apk的安装流程与签名验证相关的步骤位于installPackageLI函数中:
    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {  
    ……  
    PackageParser pp = new PackageParser();  
    ……  
    try {  
        pp.collectCertificates(pkg, parseFlags);  
        pp.collectManifestDigest(pkg);  
    } catch (PackageParserException e) {  
        res.setError("Failed collect during installPackageLI", e);  
        return;  
    }  
    ……  
(2)  在这个函数中,会用到PackageParser这个类 (代码位于 frameworks\base\core\java\android\content\pm\PackageParser.java ,编译后存在于 framework.jar 文件中)是一个apk包的解析器,接下来我们来看其 collectCertificates 函数的实现:
     public static void collectCertificates(Package pkg, int parseFlags)
            throws PackageParserException {
        collectCertificatesInternal(pkg, parseFlags);
        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
        for (int i = 0; i < childCount; i++) {
            Package childPkg = pkg.childPackages.get(i);
            childPkg.mCertificates = pkg.mCertificates;
            childPkg.mSignatures = pkg.mSignatures;
            childPkg.mSigningKeys = pkg.mSigningKeys;
        }
    }
(3)  collectCertificates函数中,调用了   collectCertificatesInternal函数。所以接下来我们看看   collectCertificatesInternal的函数实现:
    private static void collectCertificatesInternal(Package pkg, int parseFlags)
            throws PackageParserException {
        pkg.mCertificates = null;
        pkg.mSignatures = null;
        pkg.mSigningKeys = null;

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
        try {
            collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);

            if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
                for (int i = 0; i < pkg.splitCodePaths.length; i++) {
                    collectCertificates(pkg, new File(pkg.splitCodePaths[i]), parseFlags);
                }
            }
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
(4)在函数 collectCertificatesInternal中调用了 collectCertificates函数的重载函数,在7.0之前,是在 collectCertificates函数中直接调用其重载函数,中间是没有 collectCertificatesInternal函数的。 collectCertificates的重载函数是一个很重要的函数,接下来我们看看在这个函数中做了什么操作:
            private static void collectCertificates(Package pkg, File apkFile, int parseFlags)
1            throws PackageParserException {
1151        final String apkPath = apkFile.getAbsolutePath();
1152
1153        // Try to verify the APK using APK Signature Scheme v2.
1154        boolean verified = false;
1155        {
1156            Certificate[][] allSignersCerts = null;
1157            Signature[] signatures = null;
1158            try {
1159                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
1160                allSignersCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
1161                signatures = convertToSignatures(allSignersCerts);
1162                // APK verified using APK Signature Scheme v2.
1163                verified = true;
1164            } catch (ApkSignatureSchemeV2Verifier.SignatureNotFoundException e) {
1165                // No APK Signature Scheme v2 signature found
1166            } catch (Exception e) {
1167                // APK Signature Scheme v2 signature was found but did not verify
1168                throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
1169                        "Failed to collect certificates from " + apkPath
1170                                + " using APK Signature Scheme v2",
1171                        e);
1172            } finally {
1173                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
1174            }
                   ……
在这个函数中,首先对签名apk做了v2方式的签名校验(代码从1154-1174)。也就是说首先用针对v2方式的签名方式来做签名校验,如果校验成功 verified   = true。如果在校验的过程中抛出了异常,那么有两种可能:1.apk没有用v2签名方式进行签名;2.apk用了v2签名方式进行签名,但是签名检验没有成功。
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android 7.0 及以上版本中,系统加强了对应用间数据隐私的保护,如果一个应用试图通过 Intent.getData() 方法获取另一个应用私有目录下的文件,就会抛出 "exposed beyond app through Intent.getData()" 异常。 为了解决这个问题,你可以使用 FileProvider 类来共享文件,具体步骤如下: 1. 在 AndroidManifest.xml 文件中添加如下代码: ``` <manifest> <application> ... <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> ... </application> </manifest> ``` 2. 在 res/xml 目录下创建 file_paths.xml 文件,添加如下代码: ``` <paths> <external-path name="external_files" path="." /> <files-path name="files" path="." /> </paths> ``` 这里的 external-path 和 files-path 分别对应外部存储和内部存储的文件路径,如果你的文件存储在其他路径,需要相应地修改。 3. 在你需要共享文件的地方,使用如下代码获取文件 Uri: ``` File file = new File("文件路径"); Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file); ``` 其中,context 是你的上下文对象,file 是你要共享的文件路径。 4. 将 Uri 添加到 Intent 中,并添加 FLAG_GRANT_READ_URI_PERMISSION 标记: ``` Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(uri, "application/pdf"); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(intent); ``` 这里以打开 PDF 文件为例,你需要根据实际情况修改 MIME 类型。FLAG_GRANT_READ_URI_PERMISSION 标记会授予目标应用访问该 Uri 的权限。 通过以上步骤,你就可以在 Android 7.0 及以上版本中共享文件了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值