Android相同包名不同签名的apk安装失败问题分析

在app安装时,系统会采集app的签名进行保存,不了解的请看https://blog.csdn.net/Cailand/article/details/103870784

手机签名的用处就是方尺防止安装的apk是非法来源的,那么系统是怎么进行校验的呢,下面我们通过Android9.0的源码进行分析

查看installPackageLI方法如下

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
    ..........................
    ..........................
    PackageParser pp = new PackageParser();
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setDisplayMetrics(mMetrics);
    pp.setCallback(mPackageParserCallback);
    ..............................
    ..............................
    //解析apk中的签名文件
    try {
        // either use what we've been given or parse directly from the APK
        if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
            pkg.setSigningDetails(args.signingDetails);
        } else {
            PackageParser.collectCertificates(pkg, false /* skipVerify */);
        }
    } catch (PackageParserException e) {
        res.setError("Failed collect during installPackageLI", e);
        return;
    }
    .............................
    .............................
    //获取已安装的信息
    PackageSetting ps = mSettings.mPackages.get(pkgName);
    if (ps != null) {
        if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);

        // Static shared libs have same package with different versions where
        // we internally use a synthetic package name to allow multiple versions
        // of the same package, therefore we need to compare signatures against
        // the package setting for the latest library version.
        PackageSetting signatureCheckPs = ps;
        if (pkg.applicationInfo.isStaticSharedLibrary()) {
            SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
            if (libraryEntry != null) {
                signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
            }
        }

        // Quick sanity check that we're signed correctly if updating;
        // we'll check this again later when scanning, but we want to
        // bail early here before tripping over redefined permissions.
        final KeySetManagerService ksms = mSettings.mKeySetManagerService;
        if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
            if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
                res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                        + pkg.packageName + " upgrade keys do not match the "
                        + "previously installed version");
                return;
            }
        } else {
            try {
                final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
                final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
                // We don't care about disabledPkgSetting on install for now.
                //此处去比较两个apk的签名文件
                final boolean compatMatch = verifySignatures( signatureCheckPs, null, pkg.mSigningDetails, compareCompat, compareRecover);
                // The new KeySets will be re-added later in the scanning process.
                if (compatMatch) {
                    synchronized (mPackages) {
                        ksms.removeAppKeySetDataLPw(pkg.packageName);
                    }
                }
            } catch (PackageManagerException e) {
                res.setError(e.error, e.getMessage());
                return;
             
            }   
        }

        oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
        if (ps.pkg != null && ps.pkg.applicationInfo != null) {
            systemApp = (ps.pkg.applicationInfo.flags &
            ApplicationInfo.FLAG_SYSTEM) != 0;
        }
        res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
    }    
    ............
    ...................
}

接下来看verifySignatures方法

public static boolean verifySignatures(PackageSetting pkgSetting,
            PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
            boolean compareCompat, boolean compareRecover)
            throws PackageManagerException {
        final String packageName = pkgSetting.name;
        boolean compatMatch = false;
        if (pkgSetting.signatures.mSigningDetails.signatures != null) {

            // Already existing package. Make sure signatures match
            boolean match = parsedSignatures.checkCapability(
                    pkgSetting.signatures.mSigningDetails,
                    PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
                            || pkgSetting.signatures.mSigningDetails.checkCapability(
                                    parsedSignatures,
                                    PackageParser.SigningDetails.CertCapabilities.ROLLBACK);

            if (!match && compareCompat) {
                match = matchSignaturesCompat(packageName, pkgSetting.signatures,
                        parsedSignatures);
                compatMatch = match;
            }
            if (!match && compareRecover) {
                match = matchSignaturesRecover(
                        packageName,
                        pkgSetting.signatures.mSigningDetails,
                        parsedSignatures,
                        PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
                                || matchSignaturesRecover(
                                        packageName,
                                        parsedSignatures,
                                        pkgSetting.signatures.mSigningDetails,
                                        PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
            }
            if (!match && isApkVerificationForced(disabledPkgSetting)) {
                match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);
            }
            if (!match) {
                throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                        "Package " + packageName +
                        " signatures do not match previously installed version; ignoring!");
            }
        }
        // Check for shared user signatures
        if (pkgSetting.sharedUser != null
                && pkgSetting.sharedUser.signatures.mSigningDetails
                        != PackageParser.SigningDetails.UNKNOWN) {

            // Already existing package. Make sure signatures match.  In case of signing certificate
            // rotation, the packages with newer certs need to be ok with being sharedUserId with
            // the older ones.  We check to see if either the new package is signed by an older cert
            // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok
            // with being sharedUser with the existing signing cert.
            boolean match =
                    parsedSignatures.checkCapability(
                            pkgSetting.sharedUser.signatures.mSigningDetails,
                            PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
                    || pkgSetting.sharedUser.signatures.mSigningDetails.checkCapability(
                            parsedSignatures,
                            PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
            if (!match && compareCompat) {
                match = matchSignaturesCompat(
                        packageName, pkgSetting.sharedUser.signatures, parsedSignatures);
            }
            if (!match && compareRecover) {
                match =
                        matchSignaturesRecover(packageName,
                                pkgSetting.sharedUser.signatures.mSigningDetails,
                                parsedSignatures,
                                PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
                        || matchSignaturesRecover(packageName,
                                parsedSignatures,
                                pkgSetting.sharedUser.signatures.mSigningDetails,
                                PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
                compatMatch |= match;
            }
            if (!match) {
                throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
                        "Package " + packageName
                        + " has no signatures that match those in shared user "
                        + pkgSetting.sharedUser.name + "; ignoring!");
            }
        }
        return compatMatch;
    }

调用了SigningDetails的checkCapability的方法,继续深入

public boolean signaturesMatchExactly(SigningDetails other) {
    return Signature.areExactMatch(this.signatures, other.signatures);
}

查看Signature.areExactMatch

public static boolean areExactMatch(Signature[] a, Signature[] b) {
    return (a.length == b.length) && ArrayUtils.containsAll(a, b)
                && ArrayUtils.containsAll(b, a);
}

从这里可以看到,只有传入的两个签名数据(Certificate)完全一样才返回true,下面的判断同理,而最后如果match还是false就会抛出异常.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Studio中,APK打包失败可能是由于多种因素引起的。以下是一些常见的问题和解决方法: 1. Gradle配置问题:请确保你的Gradle配置文件没有错误,并且正确地引用了所需的依赖项。可以尝试重新同步Gradle文件来修复配置问题。在菜单栏中选择"File",然后选择"Invalidate Caches / Restart",最后选择"Invalidate and Restart"。 2. 编译错误:在Android Studio中,如果你的代码中存在语法错误或其他编译错误,APK打包将会失败。请检查你的代码,修复所有的编译错误,并重新编译项目。 3. 内存问题:如果你的项目非常庞大,可能会导致APK打包失败。这种情况下,你可以尝试增加分配给Gradle的内存。在项目根目录下的`gradle.properties`文件中,添加以下行:`org.gradle.jvmargs=-Xmx4096m`,然后重新编译项目。 4. 依赖冲突:如果你的项目中存在依赖冲突,也可能导致APK打包失败。你可以使用Gradle的依赖排除功能,排除引起冲突的依赖项。例如,如果`libraryA`和`libraryB`产生冲突,你可以在`build.gradle`文件的`dependencies`块中添加以下代码来排除冲突项: ``` implementation ('libraryA') { exclude group: 'com.example.libraryB', module: 'libraryB' } ``` 5. 缓存问题:有时候,Android Studio的缓存可能会导致APK打包失败。你可以尝试清理缓存来解决该问题。在菜单栏中选择"File",然后选择"Invalidate Caches / Restart",最后选择"Invalidates and Restart"。 这些解决方法中的一种或多种可能会解决你在Android Studio中遇到的APK打包失败问题。在尝试这些解决方法之前,建议备份你的项目以防万一。如果问题仍然存在,你可以查看Android Studio的日志文件以获取更多详细信息,并尝试在Stack Overflow等开发者社区中寻求帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值