APK v1 签名方案验证过程

若之前有跟过 APK 的安装过程,就会知道:无论是用户自己安装 APK 还是开机系统扫描安装预置 APK,都会在 PMS 的 addForInitLI 方法中调用 PackageParser 来解析它。只是系统预置的 APK 验证过程有些不同,因为系统分区受 AVB 保护,所以默认该分区中的应用是可信的,通常不会进行完整的验证,而是只验证 Signing Block 的数据加快安装速度。

如果你有解压过 v1 签名的 APK 就会发现在 META-INF 文件夹,包含 CERT.RSA、CERT.SF、MANIFEST.MF 三个文件。这些文件在 APK 验签过程中分别扮演什么角色呢?请跟着本文来了解一下 v1 签名方案的验证过程吧。

首先,来看看采用 v1 签名方案的 APK 的验证过程时序图:

1. PackageManagerService.addForInitLI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
// 在平台初始化期间向内部数据结构添加一个新包。添加后,该包对于系统是已知的,并且可以查询。
// 对于位于设备ROM上的软件包[例如位于 system,vendor 等的软件包中,则执行其他检查。
// 如果程序包与以前已知的程序包相同,则会进行基本验证(例如确保匹配的签名,检查版本代码等)。
// 如果软件包未通过签名检查,则将删除 data 上安装的版本。如果新软件包的版本小于/等于 data上的版本,它将被忽略。
// 无论包的位置如何,结果都将应用于内部结构,并且包可用于系统的其余部分。返回值应删除。 这是传入的包对象。
@GuardedBy({"mInstallLock", "mLock"})
private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
final String renamedPkgName;
final PackageSetting disabledPkgSetting;
final boolean isSystemPkgUpdated;
final boolean pkgAlreadyExists;
PackageSetting pkgSetting;
// installPackageLI() 具有相同的代码来设置软件包的应用信息。
// 这可能应该在调用堆栈中的较低位置进行,例如 scanPackageOnly()。但是在 scanPackageNew() 中之前验证了应用信息,因此必须尽早设置应用信息。
synchronized (mLock) {
renamedPkgName = mSettings.getRenamedPackageLPr(parsedPackage.getRealPackage());
final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
if (realPkgName != null) {
ensurePackageRenamed(parsedPackage, renamedPkgName);
}
final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
renamedPkgName);
final PackageSetting installedPkgSetting = mSettings.getPackageLPr(
parsedPackage.getPackageName());
pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
pkgAlreadyExists = pkgSetting != null;
final String disabledPkgName = pkgAlreadyExists
? pkgSetting.name : parsedPackage.getPackageName();
if (scanSystemPartition && !pkgAlreadyExists
&& mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
// 在 data apk 的数据意外丢失后, system apk 的升级数据与之不一致。为了恢复它,需启用 system apk 并将其安装为未升级的系统应用。
Slog.w(TAG, "Inconsistent package setting of updated system app for "
+ disabledPkgName + ". To recover it, enable the system app"
+ "and install it as non-updated system app.");
mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
}
disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
isSystemPkgUpdated = disabledPkgSetting != null;
if (DEBUG_INSTALL && isSystemPkgUpdated) {
Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
}
final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
? mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
: null;
if (DEBUG_PACKAGE_SCANNING
&& (parseFlags & PackageParser.PARSE_CHATTY) != 0
&& sharedUserSetting != null) {
Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ " (uid=" + sharedUserSetting.userId + "):"
+ " packages=" + sharedUserSetting.packages);
}
if (scanSystemPartition) {
// 如果 system 分区上的应用已通过 OTA 更新,但 data 上的版本扔被禁用,则遍历其所有子程序包并删除不再定义的子程序。
if (isSystemPkgUpdated) {
boolean isPlatformPackage = mPlatformPackage != null
&& Objects.equals(mPlatformPackage.getPackageName(),
parsedPackage.getPackageName());
// 对正在升级的禁用的软件包进行扫描设置。
final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
null, disabledPkgSetting /* pkgSetting */,
null /* disabledPkgSetting */, null /* originalPkgSetting */,
null, parseFlags, scanFlags, isPlatformPackage, user, null);
applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, true);
final ScanResult scanResult =
scanPackageOnlyLI(request, mInjector, mFactoryTest, -1L);
if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) {
scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting);
}
}
}
}
final boolean newPkgChangedPaths =
pkgAlreadyExists && !pkgSetting.codePathString.equals(parsedPackage.getCodePath());
final boolean newPkgVersionGreater =
pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
&& newPkgChangedPaths && newPkgVersionGreater;
if (isSystemPkgBetter) {
// 如果 system 上的应用版本大于 data 上的版本,则切到 system 上的应用。
// 假设 system 上的应用都将被正确扫描,否则将没有该应用的副本。
synchronized (mLock) {
// 只需从软件包列表中删除已加载的条目。
mPackages.remove(pkgSetting.name);
}
logCriticalInfo(Log.WARN,
"System package updated;"
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + parsedPackage.getCodePath());
final InstallArgs args = createInstallArgsForExisting(
pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(
pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
args.cleanUpResourcesLI();
synchronized (mLock) {
mSettings.enableSystemPackageLPw(pkgSetting.name);
}
}
if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
// 如果 system 分区上的应用版本小于 data 分区上的版本,则触发异常并使用 data 分区上已安装的应用。
throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+ " at " + parsedPackage.getCodePath() + " ignored: updated version "
+ pkgSetting.versionCode + " better than this "
+ parsedPackage.getLongVersionCode());
}
// 如果进行了升级,并且这是系统分区中的一个应用,或者这是升级的 priv-app,则将强制重新收集证书。
final boolean forceCollect = scanSystemPartition ? mIsUpgrade
: PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
if (DEBUG_VERIFY && forceCollect) {
Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
}
// 仅 APK 位于经过验证的分区时,或在访问时跳过强制验证时,才可以跳过完整的 APK 验证,而改为仅验证签名块中的数据。
final boolean skipVerify = scanSystemPartition
|| (forceCollect && canSkipForcedPackageVerification(parsedPackage));
collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);
// 如果更改了应用版本,则重置配置文件。
maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
// 出现了一个新的系统应用,但是之前已经安装了一个非系统的同名应用。
boolean shouldHideSystemApp = false;
if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
&& !pkgSetting.isSystem()) {
if (!parsedPackage.getSigningDetails()
.checkCapability(pkgSetting.signatures.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !pkgSetting.signatures.mSigningDetails.checkCapability(
parsedPackage.getSigningDetails(),
PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
try (@SuppressWarnings("unused") PackageFreezer freezer = freezePackage(
parsedPackage.getPackageName(),
"scanPackageInternalLI")) {
deletePackageLIF(parsedPackage.getPackageName(), null, true, null, 0, null,
false, null);
}
pkgSetting = null;
} else if (newPkgVersionGreater) {
// system 上的应用比 data 上的应用新。只需删除 data 上的应用[保留应用程序数据],然后将其替换为 system 上的版本。
logCriticalInfo(Log.WARN,
"System package enabled;"
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> "
+ parsedPackage.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> "
+ parsedPackage.getCodePath());
InstallArgs args = createInstallArgsForExisting(
pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(
pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
} else {
// system 上的应用比 data 上的应用旧。则隐藏 system 上的应用, data 上的版本将在以后扫描并像升级一样重新被添加。
shouldHideSystemApp = true;
logCriticalInfo(Log.INFO,
"System package disabled;"
+ " name: " + pkgSetting.name
+ "; old: " + pkgSetting.codePathString + " @ "
+ pkgSetting.versionCode
+ "; new: " + parsedPackage.getCodePath() + " @ "
+ parsedPackage.getCodePath());
}
}
final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user, null);
if (scanResult.success) {
synchronized (mLock) {
boolean appIdCreated = false;
try {
final String pkgName = scanResult.pkgSetting.name;
final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
mSharedLibraries,
mPackages,
Collections.singletonMap(
pkgName, getSettingsVersionForPackage(parsedPackage)),
Collections.singletonMap(pkgName,
getSharedLibLatestVersionSetting(scanResult))),
mSettings.mKeySetManagerService);
appIdCreated = optimisticallyRegisterAppId(scanResult);
commitReconciledScanResultLocked(
reconcileResult.get(pkgName), mUserManager.getUserIds());
} catch (PackageManagerException e) {
if (appIdCreated) {
cleanUpAppIdCreation(scanResult);
}
throw e;
}
}
}
if (shouldHideSystemApp) {
synchronized (mLock) {
mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
}
}
return scanResult.pkgSetting.pkg;
}

2. PackageManagerService.collectCertificatesLI

collectCertificatesLI 方法主要用于收集 APK 的签名信息,因为这里着重分析非系统 APK 的验证过程,故而 skipVerify 为 false。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
boolean forceCollect, boolean skipVerify) throws PackageManagerException {
// 若是从 pre-N MR1 升级上来的,则使用软件包目录的时间戳,否则使用 APK 文件来的时间戳。
final long lastModifiedTime = mIsPreNMR1Upgrade
? new File(parsedPackage.getCodePath()).lastModified()
: getLastModifiedTime(parsedPackage);
final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage);
if (ps != null && !forceCollect
&& ps.codePathString.equals(parsedPackage.getCodePath())
&& ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
&& !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
if (ps.signatures.mSigningDetails.signatures != null
&& ps.signatures.mSigningDetails.signatures.length != 0
&& ps.signatures.mSigningDetails.signatureSchemeVersion
!= SignatureSchemeVersion.UNKNOWN) {
// 如果程序包没有变化,则继续使用现有的缓存签名数据。
parsedPackage.setSigningDetails(
new PackageParser.SigningDetails(ps.signatures.mSigningDetails));
return;
}
Slog.w(TAG, "PackageSetting for " + ps.name
+ " is missing signatures. Collecting certs again to recover them.");
} else {
Slog.i(TAG, parsedPackage.getCodePath() + " changed; collecting certs" +
(forceCollect ? " (forced)" : ""));
}
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
parsedPackage.setSigningDetails(
ParsingPackageUtils.getSigningDetails(parsedPackage, skipVerify));
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}

3. PackageParser.setSigningDetails

setSigningDetails 方法主要用于将 getSigningDetails 方法获取到的签名信息存储起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// frameworks/base/core/java/android/content/pm/PackageParser.java
public final static class Package implements Parcelable {
...
public void setSigningDetails(@NonNull SigningDetails signingDetails) {
mSigningDetails = signingDetails;
if (childPackages != null) {
final int packageCount = childPackages.size();
for (int i = 0; i < packageCount; i++) {
childPackages.get(i).mSigningDetails = signingDetails;
}
}
}
...
}

4. ParsingPackageUtils.getSigningDetails

getSigningDetails 方法会收集证书、安装路径、 sdk 等信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// frameworks/base/core/java/android/content/pm/parsing/ParsingPackageUtils.java
// 从指定的 APK 中收集证书,判断所有 APK 内容是否已被正确签名。
// 如果被请求,则在原始解析调用期间取消此操作以收集证书。将其保留为调用方的可选方法意味着必须构造一个虚拟 ParseInput。
@CheckResult
public static SigningDetails getSigningDetails(ParsingPackageRead pkg, boolean skipVerify)
throws PackageParserException {
SigningDetails signingDetails = SigningDetails.UNKNOWN;
ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
ParseResult<SigningDetails> result = getSigningDetails(
input,
pkg.getBaseCodePath(),
skipVerify,
pkg.isStaticSharedLibrary(),
signingDetails,
pkg.getTargetSdkVersion()
);
if (result.isError()) {
throw new PackageParser.PackageParserException(result.getErrorCode(),
result.getErrorMessage(), result.getException());
}
signingDetails = result.getResult();
String[] splitCodePaths = pkg.getSplitCodePaths();
if (!ArrayUtils.isEmpty(splitCodePaths)) {
for (int i = 0; i < splitCodePaths.length; i++) {
result = getSigningDetails(
input,
splitCodePaths[i],
skipVerify,
pkg.isStaticSharedLibrary(),
signingDetails,
pkg.getTargetSdkVersion()
);
if (result.isError()) {
throw new PackageParser.PackageParserException(result.getErrorCode(),
result.getErrorMessage(), result.getException());
}
signingDetails = result.getResult();
}
}
return signingDetails;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@CheckResult
public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
String baseCodePath, boolean skipVerify, boolean isStaticSharedLibrary,
@NonNull SigningDetails existingSigningDetails, int targetSdk) {
// android r及以上版本系统最低支持的 apk 签名是v2,android r以下系统支持最低的 apk 签名是v1。
int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
targetSdk);
// 静态共享库必须使用v2版本签名。
if (isStaticSharedLibrary) {
minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
}
SigningDetails verified;
try {
if (skipVerify) {
// 仅限系统分区的 APK,因为默认系统分区 APK 已获得信任,无需再验证以节省时间。
// 因为这些应用签名没有验证,且部分系统应用的 v2 签名可以被移除,因为支持从 jar 签名(v1)获取证书。
verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
baseCodePath, SigningDetails.SignatureSchemeVersion.JAR);
} else {
// 验证 APK 的签名返回证书。
verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
}
} catch (PackageParserException e) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed collecting certificates for " + baseCodePath, e);
}
// 验证条目是否与遇到的第一个 APK 包签名一致。证书可能已经在基 APK 的较早解析期间被填充了。
if (existingSigningDetails == SigningDetails.UNKNOWN) {
return input.success(verified);
} else {
if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
baseCodePath + " has mismatched certificates");
}
return input.success(existingSigningDetails);
}
}

5. ApkSignatureVerifier.verify

1
2
3
4
5
6
7
// frameworks/base/core/java/android/util/apk/ApkSignatureVerifier.java
// 验证 APK 并返回与每个签名者关联的证书。
public static PackageParser.SigningDetails verify(String apkPath,
@SignatureSchemeVersion int minSignatureSchemeVersion)
throws PackageParserException {
return verifySignatures(apkPath, minSignatureSchemeVersion, true);
}

6. ApkSignatureVerifier.verifySignatures

verifySignatures 方法会判断该 APK 所采用的 签名方案。其中 7.0 开始支持 v2, 9.0 开始支持 v3, 11.0 开始支持 v4.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// frameworks/base/core/java/android/util/apk/ApkSignatureVerifier.java
private static PackageParser.SigningDetails verifySignatures(String apkPath,
@SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
throws PackageParserException {
// 最高支持 v4 版本签名。
if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + apkPath);
}
// 验证v4版本的签名的 APK。
try {
return verifyV4Signature(apkPath, minSignatureSchemeVersion, verifyFull);
} catch (SignatureNotFoundException e) {
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V4) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v4 signature in package " + apkPath, e);
}
}
if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + apkPath);
}
// 验证v3及更低版本签名的 APK。
return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull);
}

7. ApkSignatureVerifier.verifyV3AndBelowSignatures

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// frameworks/base/core/java/android/util/apk/ApkSignatureVerifier.java
// 验证 APK 的签名并返回与每个签名者关联的证书。
public static PackageParser.SigningDetails verify(String apkPath,
@SignatureSchemeVersion int minSignatureSchemeVersion)
throws PackageParserException {
// 验证v3版本签名的 APK。
try {
return verifyV3Signature(apkPath, verifyFull);
} catch (SignatureNotFoundException e) {
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v3 signature in package " + apkPath, e);
}
}
if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + apkPath);
}
// 验证v2版本签名的 APK。
try {
return verifyV2Signature(apkPath, verifyFull);
} catch (SignatureNotFoundException e) {
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v2 signature in package " + apkPath, e);
}
}
if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + apkPath);
}
// 验证v1版本签名的 APK。
return verifyV1Signature(apkPath, true);
}

8. ApkSignatureVerifier.verifyV1Signature

下面开始 v1 签名的验证之路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// frameworks/base/core/java/android/util/apk/ApkSignatureVerifier.java
// 验证v1版本签名的 APK 并返回签名相关联的证书信息。
private static PackageParser.SigningDetails verifyV1Signature(
String apkPath, boolean verifyFull)
throws PackageParserException {
StrictJarFile[] jarFile = null;
try {
final Certificate[][] lastCerts;
final Signature[] lastSigs;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
// 即使不检查整个 jar 包,仍将 verify=true 传给 ctor 来收集证书。
jarFile[i] = new StrictJarFile(
apkPath,
true, // 是否收集证书
verifyFull); // 是否拒绝带有v2签名的 APK
final List<ZipEntry> toVerify = new ArrayList<>();
// 从 AndroidManifest.xml 收集证书,每个 APK 都必须具有该证书,当 verifyFUll==false 时不需要验证整个 APK。
final ZipEntry manifestEntry = jarFile[0].findEntry(
PackageParser.ANDROID_MANIFEST_FILENAME);
if (manifestEntry == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Package " + apkPath + " has no manifest");
}
lastCerts = loadCertificates(jarFile[0], manifestEntry);
if (ArrayUtils.isEmpty(lastCerts)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package "
+ apkPath + " has no certificates at entry "
+ PackageParser.ANDROID_MANIFEST_FILENAME);
}
lastSigs = convertToSignatures(lastCerts);
// 完全验证所有内容,除了 AndroidManifest.xml 和 META-INF 目录文件。
if (verifyFull) {
final Iterator<ZipEntry> i = jarFile[0].iterator();
while (i.hasNext()) {
final ZipEntry entry = i.next();
if (entry.isDirectory()) continue;
final String entryName = entry.getName();
if (entryName.startsWith("META-INF/")) continue;
if (entryName.equals(PackageParser.ANDROID_MANIFEST_FILENAME)) continue;
toVerify.add(entry);
}
for (ZipEntry entry : toVerify) {
final Certificate[][] entryCerts = loadCertificates(tempJarFile, entry);
if (ArrayUtils.isEmpty(entryCerts)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Package " + apkPath + " has no certificates at entry "
+ entry.getName());
}
// 确保所有条目都使用相同的签名证书。
final Signature[] entrySigs = convertToSignatures(entryCerts);
if (!Signature.areExactMatch(lastSigs, entrySigs)) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
"Package " + apkPath + " has mismatched certificates at entry "
+ entry.getName());
}
}
}
return new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR);
} 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 {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
closeQuietly(jarFile[i]);
}
}

9. StrictJarFile

由于 v1 是基于 jar 的签名,所以它会构造 StrictJarFile 来解析 Manifest 和 META-INF 目录下的文件获取签名相关信息,然后传给 StrictJarVerifier 进行验证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// frameworks/base/core/java/android/util/jar/StrictJarFile.java
public StrictJarFile(String fileName,
boolean verify,
boolean signatureSchemeRollbackProtectionsEnforced)
throws IOException, SecurityException {
this(fileName, IoBridge.open(fileName, OsConstants.O_RDONLY),
verify, signatureSchemeRollbackProtectionsEnforced);
}
// verify:是否验证文件的 JAR 签名并收集相应的签名者证书。
// signatureSchemeRollbackProtectionsEnforced=true:强制实施保护措施,以防止从文件中删除较新的签名方案(例如APK签名方案v2);
// signatureSchemeRollbackProtectionsEnforced=false:忽略任何此类保护。
private StrictJarFile(String name,
FileDescriptor fd,
boolean verify,
boolean signatureSchemeRollbackProtectionsEnforced)
throws IOException, SecurityException {
this.nativeHandle = nativeOpenJarFile(name, fd.getInt$());
this.fd = fd;
try {
// 先读取manifest清单和签名文件,然后尝试解析它们。
if (verify) {
HashMap<String, byte[]> metaEntries = getMetaEntries();
this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
this.verifier =
new StrictJarVerifier(
name,
manifest,
metaEntries,
signatureSchemeRollbackProtectionsEnforced);
Set<String> files = manifest.getEntries().keySet();
for (String file : files) {
if (findEntry(file) == null) {
throw new SecurityException("File " + file + " in manifest does not exist");
}
}
isSigned = verifier.readCertificates() && verifier.isSignedJar();
} else {
isSigned = false;
this.manifest = null;
this.verifier = null;
}
} catch (IOException | SecurityException e) {
nativeClose(this.nativeHandle);
IoUtils.closeQuietly(fd);
closed = true;
throw e;
}
guard.open("close");
}

10. StrictJarVerifier.readCertificates

readCertificates 方法主要用于从 META-INF 中获取证书信息。v1 只支持 RSA、DSA、EC 三种加密算法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// frameworks/base/core/java/android/util/jar/StrictJarVerifier.java
// 如果关联的 JAR 文件已签名,需检查所有已知签名的有效性。
synchronized boolean readCertificates() {
if (metaEntries.isEmpty()) {
return false;
}
Iterator<String> it = metaEntries.keySet().iterator();
while (it.hasNext()) {
String key = it.next();
// 支持 DSA/RSA/EC。
if (key.endsWith(".DSA") || key.endsWith(".RSA") || key.endsWith(".EC")) {
verifyCertificate(key);
it.remove();
}
}
return true;
}

11. StrictJarVerifier.verifyCertificate

verifyCertificate 方法会验证 META-INF 的 .SF 的数字签名,manifest哈希、摘要、签名方案对应的 ID 等信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// frameworks/base/core/java/android/util/jar/StrictJarVerifier.java
private void verifyCertificate(String certFile) {
// 从 .SF 中读取数字签名。
String signatureFile = certFile.substring(0, certFile.lastIndexOf('.')) + ".SF";
byte[] sfBytes = metaEntries.get(signatureFile);
if (sfBytes == null) {
return;
}
byte[] manifestBytes = metaEntries.get(JarFile.MANIFEST_NAME);
// 进行任何验证都需要输入 manifest 清单。
if (manifestBytes == null) {
return;
}
byte[] sBlockBytes = metaEntries.get(certFile);
try {
Certificate[] signerCertChain = verifyBytes(sBlockBytes, sfBytes);
if (signerCertChain != null) {
certificates.put(signatureFile, signerCertChain);
}
} catch (GeneralSecurityException e) {
throw failedVerification(jarName, signatureFile, e);
}
// 验证 .SF 文件中 manifest 哈希。
Attributes attributes = new Attributes();
HashMap<String, Attributes> entries = new HashMap<String, Attributes>();
try {
StrictJarManifestReader im = new StrictJarManifestReader(sfBytes, attributes);
im.readEntries(entries, null);
} catch (IOException e) {
return;
}
// 检查新的 APK 签名结构是否被删除了。
if (signatureSchemeRollbackProtectionsEnforced) {
String apkSignatureSchemeIdList =
attributes.getValue(SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME);
if (apkSignatureSchemeIdList != null) {
// 该字段包含逗号分隔的 APK 签名方案 ID 列表,这些 ID 用于签名此 APK
// 如果知道一个 ID,则意味着该方案的签名已从 APK 中删除,否则将不再依赖使用 JAR 签名方案来验证 APK。
boolean v2SignatureGenerated = false;
boolean v3SignatureGenerated = false;
StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ",");
while (tokenizer.hasMoreTokens()) {
String idText = tokenizer.nextToken().trim();
if (idText.isEmpty()) {
continue;
}
int id;
try {
id = Integer.parseInt(idText);
} catch (Exception ignored) {
continue;
}
if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
// 该 APK 应该使用了v2签名,但未找到此类签名。
v2SignatureGenerated = true;
break;
}
if (id == ApkSignatureSchemeV3Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
// 该 APK 应该使用了v3签名,但未找到此类签名。
v3SignatureGenerated = true;
break;
}
}
if (v2SignatureGenerated) {
throw new SecurityException(signatureFile + " indicates " + jarName
+ " is signed using APK Signature Scheme v2, but no such signature was"
+ " found. Signature stripped?");
}
if (v3SignatureGenerated) {
throw new SecurityException(signatureFile + " indicates " + jarName
+ " is signed using APK Signature Scheme v3, but no such signature was"
+ " found. Signature stripped?");
}
}
}
// 签名版本为空。
if (attributes.get(Attributes.Name.SIGNATURE_VERSION) == null) {
return;
}
boolean createdBySigntool = false;
String createdBy = attributes.getValue("Created-By");
if (createdBy != null) {
createdBySigntool = createdBy.indexOf("signtool") != -1;
}
// 使用 .SF 验证清单的 mainAttributes。
// 如果 .SF 文件中没有 -Digest-Manifest-Main-Attributes 条目,则忽略此类验证。
if (mainAttributesEnd > 0 && !createdBySigntool) {
String digestAttribute = "-Digest-Manifest-Main-Attributes";
if (!verify(attributes, digestAttribute, manifestBytes, 0, mainAttributesEnd, false, true)) {
throw failedVerification(jarName, signatureFile);
}
}
// // 使用 .SF 验证整个 manifest 清单。
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();
StrictJarManifest.Chunk chunk = manifest.getChunk(entry.getKey());
if (chunk == null) {
return;
}
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);
}

12. StrictJarVerifier.verifyBytes

verifyBytes 方法用于验证 .SF 中 PKCS7 块证书签名(X509证书)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// frameworks/base/core/java/android/util/jar/StrictJarVerifier.java
// 验证从 sfBytes 计算出的签名是否与 blockBytes(PKCS7块)中指定的签名匹配。
// 返回 PKCS7 块中列出的证书。如果验证期间出了点问题,则抛出 GeneralSecurityException 异常。
static Certificate[] verifyBytes(byte[] blockBytes, byte[] sfBytes)
throws GeneralSecurityException {
Object obj = null;
try {
obj = Providers.startJarVerification();
PKCS7 block = new PKCS7(blockBytes);
SignerInfo[] verifiedSignerInfos = block.verify(sfBytes);
if ((verifiedSignerInfos == null) || (verifiedSignerInfos.length == 0)) {
throw new GeneralSecurityException(
"Failed to verify signature: no verified SignerInfos");
}
// 忽略除第一个以外的任何 SignerInfo,以与已经使用了多年的旧版 Android 平台兼容。
// 见:libcore/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
SignerInfo verifiedSignerInfo = verifiedSignerInfos[0];
List<X509Certificate> verifiedSignerCertChain =
verifiedSignerInfo.getCertificateChain(block);
if (verifiedSignerCertChain == null) {
throw new GeneralSecurityException(
"Failed to find verified SignerInfo certificate chain");
} else if (verifiedSignerCertChain.isEmpty()) {
throw new GeneralSecurityException(
"Verified SignerInfo certificate chain is emtpy");
}
return verifiedSignerCertChain.toArray(
new X509Certificate[verifiedSignerCertChain.size()]);
} catch (IOException e) {
throw new GeneralSecurityException("IO exception verifying jar cert", e);
} finally {
Providers.stopJarVerification(obj);
}
}

13. StrictJarVerifier.verify

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// frameworks/base/core/java/android/util/jar/StrictJarVerifier.java
private boolean verify(Attributes attributes, String entry, byte[] data,
int start, int end, boolean ignoreSecondEndline, boolean ignorable) {
// "SHA-512","SHA-384","SHA-256","SHA1",
for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) {
String algorithm = DIGEST_ALGORITHMS[i];
String hash = attributes.getValue(algorithm + entry);
if (hash == null) {
continue;
}
MessageDigest md;
try {
md = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
continue;
}
if (ignoreSecondEndline && data[end - 1] == '\n' && data[end - 2] == '\n') {
md.update(data, start, end - 1 - start);
} else {
md.update(data, start, end - start);
}
byte[] b = md.digest();
byte[] encodedHashBytes = hash.getBytes(StandardCharsets.ISO_8859_1);
return verifyMessageDigest(b, encodedHashBytes);
}
return ignorable;
}

14. StrictJarVerifier.verifyMessageDigest

verifyMessageDigest 方法用于验证摘要安全性。

1
2
3
4
5
6
7
8
9
10
// frameworks/base/core/java/android/util/jar/StrictJarVerifier.java
private static boolean verifyMessageDigest(byte[] expected, byte[] encodedActual) {
byte[] actual;
try {
actual = java.util.Base64.getDecoder().decode(encodedActual);
} catch (IllegalArgumentException e) {
return false;
}
return MessageDigest.isEqual(expected, actual);
}

15. ApkSignatureVerifier.loadCertificates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// frameworks/base/core/java/android/util/apk/ApkSignatureVerifier.java
private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry)
throws PackageParserException {
InputStream is = null;
try {
// 必须读取 JarEntry 流以检索其证书。
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);
}
}

16. StrictJarFile.getInputStream

1
2
3
4
5
6
7
8
9
10
11
12
// frameworks/base/core/java/android/util/jar/StrictJarFile.java
public InputStream getInputStream(ZipEntry ze) {
final InputStream is = getZipInputStream(ze);
if (isSigned) {
StrictJarVerifier.VerifierEntry entry = verifier.initEntry(ze.getName());
if (entry == null) {
return is;
}
return new JarFileInputStream(is, ze.getSize(), entry);
}
return is;
}

17. StrictJarVerifier.initEntry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// frameworks/base/core/java/android/util/jar/StrictJarVerifier.java
// 从输入流中为每个新的 JAR 条目调用读操作。此方法构造并返回一个新的 VerifierEntry,其中包含用于签名该条目的证书及其以 JAR MANIFEST 格式指定的哈希值。
VerifierEntry initEntry(String name) {
// 如果在找到条目之前没有清单,则无法进行验证。如果未找到签名文件,则不进行验证。
if (manifest == null || signatures.isEmpty()) {
return null;
}
Attributes attributes = manifest.getAttributes(name);
// 没有摘要
if (attributes == null) {
return null;
}
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) {
// 从 .SF 文件中找到条目名称。
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.length; i++) {
final String algorithm = DIGEST_ALGORITHMS[i];
final String hash = attributes.getValue(algorithm + "-Digest");
if (hash == null) {
continue;
}
byte[] hashBytes = hash.getBytes(StandardCharsets.ISO_8859_1);
try {
return new VerifierEntry(name, MessageDigest.getInstance(algorithm), hashBytes,
certChainsArray, verifiedEntries);
} catch (NoSuchAlgorithmException ignored) {
}
}
return null;
}

18. ApkSignatureVerifier.readFullyIgnoringContents

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// frameworks/base/core/java/android/util/apk/ApkSignatureVerifier.java
private static void 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) {
count += n;
}
sBuffer.set(buffer);
return;
}

19. ApkSignatureVerifier.convertToSignatures

1
2
3
4
5
6
7
8
9
10
// frameworks/base/core/java/android/util/apk/ApkSignatureVerifier.java
// 将证书链数组转换为 PackageManager 使用的等效签名。
private static Signature[] convertToSignatures(Certificate[][] certs)
throws CertificateEncodingException {
final Signature[] res = new Signature[certs.length];
for (int i = 0; i < certs.length; i++) {
res[i] = new Signature(certs[i]);
}
return res;
}
1
2
3
4
5
6
7
8
// frameworks/base/core/java/android/content/pm/Signature.java
// 从证书链创建签名。用于向后兼容。
public Signature(Certificate[] certificateChain) throws CertificateEncodingException {
mSignature = certificateChain[0].getEncoded();
if (certificateChain.length > 1) {
mCertificateChain = Arrays.copyOfRange(certificateChain, 1, certificateChain.length);
}
}

20. ApkSignatureSchemeV2Verifier.unsafeGetCertsWithoutVerification

1
2
3
4
5
6
// frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
public static X509Certificate[][] unsafeGetCertsWithoutVerification(String apkFile)
throws SignatureNotFoundException, SecurityException, IOException {
VerifiedSigner vSigner = verify(apkFile, false);
return vSigner.certs;
}