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就会抛出异常.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值