APK v3 签名方案验证过程

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

首先,来看看采用 v3 签名方案的 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.verifyV3Signature

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

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/apk/ApkSignatureVerifier.java
// 验证V3签名版本的APK的签名
private static PackageParser.SigningDetails verifyV3Signature(String apkPath,
boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV3" : "certsOnlyV3");
try {
ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
verifyFull ? ApkSignatureSchemeV3Verifier.verify(apkPath)
: ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(
apkPath);
Certificate[][] signerCerts = new Certificate[][]{vSigner.certs};
Signature[] signerSigs = convertToSignatures(signerCerts);
Signature[] pastSignerSigs = null;
if (vSigner.por != null) {
// 填充旋转证明proof-of-rotation信息
pastSignerSigs = new Signature[vSigner.por.certs.size()];
for (int i = 0; i < pastSignerSigs.length; i++) {
pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
}
}
return new PackageParser.SigningDetails(signerSigs,
SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs);
} catch (SignatureNotFoundException e) {
throw e;
} catch (Exception e) {
// // 找到v3版本APK签名方案,但未验证。
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath
+ " using APK Signature Scheme v3", e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}

9. ApkSignatureSchemeV3Verifier.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
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
// frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
// 验证提供的 V3 版本签名的APK的签名,并返回与每个签名者关联的证书。
public static VerifiedSigner verify(String apkFile)
throws SignatureNotFoundException, SecurityException, IOException {
return verify(apkFile, true);
}
private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
return verify(apk, verifyIntegrity);
}
}
private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
SignatureInfo signatureInfo = findSignature(apk);
return verify(apk, signatureInfo, verifyIntegrity);
}
private static VerifiedSigner verify(
RandomAccessFile apk,
SignatureInfo signatureInfo,
boolean doVerifyIntegrity) throws SecurityException, IOException {
int signerCount = 0;
Map<Integer, byte[]> contentDigests = new ArrayMap<>();
VerifiedSigner result = null;
CertificateFactory certFactory;
try {
certFactory = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
}
ByteBuffer signers;
try {
signers = getLengthPrefixedSlice(signatureInfo.signatureBlock);
} catch (IOException e) {
throw new SecurityException("Failed to read list of signers", e);
}
while (signers.hasRemaining()) {
try {
ByteBuffer signer = getLengthPrefixedSlice(signers);
result = verifySigner(signer, contentDigests, certFactory);
signerCount++;
} catch (PlatformNotSupportedException e) {
// 该签名者用于其他平台,忽略它。
continue;
} catch (IOException | BufferUnderflowException | SecurityException e) {
throw new SecurityException(
"Failed to parse/verify signer #" + signerCount + " block",
e);
}
}
if (signerCount < 1 || result == null) {
throw new SecurityException("No signers found");
}
if (signerCount != 1) {
throw new SecurityException("APK Signature Scheme V3 only supports one signer: "
+ "multiple signers found.");
}
if (contentDigests.isEmpty()) {
throw new SecurityException("No content digests found");
}
if (doVerifyIntegrity) {
ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
result.verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
verityDigest, apk.length(), signatureInfo);
}
result.digest = pickBestDigestForV4(contentDigests);
return result;
}

10. ApkSignatureSchemeV3Verifier.findSignature

1
2
3
4
5
6
// frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
// 返回包含在提供的v3签名的 APK 文件中签名块以及与根据文件验证该块有关的其他信息。
private static SignatureInfo findSignature(RandomAccessFile apk)
throws IOException, SignatureNotFoundException {
return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V3_BLOCK_ID);
}

11. ApkSigningBlockUtils.findSignature

findSignature 方法主要用于获取签名块相关信息。

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/ApkSigningBlockUtils.java
// 返回包含在提供的 APK 文件中的 APK 签名方案块以及与根据文件验证该块有关的其他信息。
static SignatureInfo findSignature(RandomAccessFile apk, int blockId)
throws IOException, SignatureNotFoundException {
// 中央目录记录的 ZIP 结尾 EoCD。
Pair<ByteBuffer, Long> eocdAndOffsetInFile = getEocd(apk);
ByteBuffer eocd = eocdAndOffsetInFile.first;
long eocdOffset = eocdAndOffsetInFile.second;
if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apk, eocdOffset)) {
throw new SignatureNotFoundException("ZIP64 APK not supported");
}
// 找到 APK 签名块。该块紧接在中央目录之前。
long centralDirOffset = getCentralDirOffset(eocd, eocdOffset);
Pair<ByteBuffer, Long> apkSigningBlockAndOffsetInFile =
findApkSigningBlock(apk, centralDirOffset);
ByteBuffer apkSigningBlock = apkSigningBlockAndOffsetInFile.first;
long apkSigningBlockOffset = apkSigningBlockAndOffsetInFile.second;
// 在 APK 签名块中找到 APK 签名块。
ByteBuffer apkSignatureSchemeBlock = findApkSignatureSchemeBlock(apkSigningBlock,
blockId);
return new SignatureInfo(
apkSignatureSchemeBlock,
apkSigningBlockOffset,
centralDirOffset,
eocdOffset,
eocd);
}

12. ApkSigningBlockUtils.getEocd

1
2
3
4
5
6
7
8
9
10
11
12
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
// 返回中央目录记录的 ZIP 结尾 EoCD 和它在文件中的偏移量 offset。
static Pair<ByteBuffer, Long> getEocd(RandomAccessFile apk)
throws IOException, SignatureNotFoundException {
Pair<ByteBuffer, Long> eocdAndOffsetInFile =
ZipUtils.findZipEndOfCentralDirectoryRecord(apk);
if (eocdAndOffsetInFile == null) {
throw new SignatureNotFoundException(
"Not an APK file: ZIP End of Central Directory record not found");
}
return eocdAndOffsetInFile;
}

13. ZipUtils.findZipEndOfCentralDirectoryRecord

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
// frameworks/base/core/java/android/util/apk/ZipUtils.java
// 返回提供的 ZIP 文件的中央目录记录的 ZIP 结尾。
static Pair<ByteBuffer, Long> findZipEndOfCentralDirectoryRecord(RandomAccessFile zip)
throws IOException {
// 中央目录记录的 ZIP 末尾 EOCD 位于 ZIP 文件的末尾。可以通过位于记录开头的4字节签名/魔术 magic 来识别记录。
// 复杂的是,由于注释字段,记录的长度是可变的。查找 ZIP EOCD 记录的算法如下:
// 从缓冲区的末尾向后搜索 EOCD 记录签名。每当找到签名时,都会检查候选记录的注释长度,以确保记录的其余部分恰好占用缓冲区中的剩余字节。
// 由于注释字段的最大大小为65535字节,因为该字段是无符号的16位数字,因此搜索受到限制。
long fileSize = zip.length();
// ZIP_EOCD_REC_MIN_SIZE=22
if (fileSize < ZIP_EOCD_REC_MIN_SIZE) {
return null;
}
// 99.99%的 APK 在 EoCD 记录中具有一个零长度的注释字段,因此 EoCD 记录偏移量是预先已知的。首先尝试该偏移量,以避免不必要的读取更多数据。
Pair<ByteBuffer, Long> result = findZipEndOfCentralDirectoryRecord(zip, 0);
if (result != null) {
return result;
}
// EoCD 不在期望的起始位置。也许它包含一个非空的注释字段。EoCD 中注释字段的最大大小为65535,因为注释长度字段是16位无符号数字。
// UINT16_MAX_VALUE=0xffff(65535)
return findZipEndOfCentralDirectoryRecord(zip, UINT16_MAX_VALUE);
}
// 返回提供的 ZIP 文件的中央目录记录的 ZIP 结尾。

private static Pair<ByteBuffer, Long> findZipEndOfCentralDirectoryRecord(
RandomAccessFile zip, int maxCommentSize) throws IOException {
if ((maxCommentSize < 0) || (maxCommentSize > UINT16_MAX_VALUE)) {
throw new IllegalArgumentException("maxCommentSize: " + maxCommentSize);
}
long fileSize = zip.length();
if (fileSize < ZIP_EOCD_REC_MIN_SIZE) {
// 文件中的 EoCD 记录没有空间。
return null;
}
// 如果文件太小,则降低 maxCommentSize。
maxCommentSize = (int) Math.min(maxCommentSize, fileSize - ZIP_EOCD_REC_MIN_SIZE);
ByteBuffer buf = ByteBuffer.allocate(ZIP_EOCD_REC_MIN_SIZE + maxCommentSize);
// LITTLE_ENDIAN:小端字节排序
buf.order(ByteOrder.LITTLE_ENDIAN);
long bufOffsetInFile = fileSize - buf.capacity();
zip.seek(bufOffsetInFile);
zip.readFully(buf.array(), buf.arrayOffset(), buf.capacity());
int eocdOffsetInBuf = findZipEndOfCentralDirectoryRecord(buf);
if (eocdOffsetInBuf == -1) {
// 缓存区找不到 EoCD。
return null;
}
// 找到 EoCD
buf.position(eocdOffsetInBuf);
ByteBuffer eocd = buf.slice();
eocd.order(ByteOrder.LITTLE_ENDIAN);
return Pair.create(eocd, bufOffsetInFile + eocdOffsetInBuf);
}

// 返回中央目录记录的 ZIP 末尾在提供的缓冲区中开始的位置;如果记录不存在,则返回-1。
private static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) {
assertByteOrderLittleEndian(zipContents);
int archiveSize = zipContents.capacity();
if (archiveSize < ZIP_EOCD_REC_MIN_SIZE) {
return -1;
}
int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT16_MAX_VALUE);
int eocdWithEmptyCommentStartPosition = archiveSize - ZIP_EOCD_REC_MIN_SIZE;
for (int expectedCommentLength = 0; expectedCommentLength <= maxCommentLength;
expectedCommentLength++) {
int eocdStartPos = eocdWithEmptyCommentStartPosition - expectedCommentLength;
// ZIP_EOCD_REC_SIG=0x06054b50
if (zipContents.getInt(eocdStartPos) == ZIP_EOCD_REC_SIG) {
// ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET=20
int actualCommentLength =
getUnsignedInt16(
zipContents, eocdStartPos + ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET);
if (actualCommentLength == expectedCommentLength) {
return eocdStartPos;
}
}
}
return -1;
}

14. ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// frameworks/base/core/java/android/util/apk/ZipUtils.java
// 如果提供的文件包含中央目录定位器的 ZIP64 结尾,则返回true。
public static final boolean isZip64EndOfCentralDirectoryLocatorPresent(
RandomAccessFile zip, long zipEndOfCentralDirectoryPosition) throws IOException {
// 中央目录定位器的 ZIP64 末尾紧邻中央目录记录的 ZIP 末尾。
// ZIP64_EOCD_LOCATOR_SIZE=20
long locatorPosition = zipEndOfCentralDirectoryPosition - ZIP64_EOCD_LOCATOR_SIZE;
if (locatorPosition < 0) {
return false;
}
zip.seek(locatorPosition);
// RandomAccessFile.readInt 假定字节序为大尾数,但 ZIP 格式使用字节序。
// ZIP64_EOCD_LOCATOR_SIG_REVERSE_BYTE_ORDER=0x504b0607
return zip.readInt() == ZIP64_EOCD_LOCATOR_SIG_REVERSE_BYTE_ORDER;
}

15. ApkSigningBlockUtils.getCentralDirOffset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
static long getCentralDirOffset(ByteBuffer eocd, long eocdOffset)
throws SignatureNotFoundException {
// 查找 ZIP 中央目录的偏移量。
long centralDirOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocd);
if (centralDirOffset > eocdOffset) {
throw new SignatureNotFoundException(
"ZIP Central Directory offset out of range: " + centralDirOffset
+ ". ZIP End of Central Directory offset: " + eocdOffset);
}
long centralDirSize = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd);
if (centralDirOffset + centralDirSize != eocdOffset) {
throw new SignatureNotFoundException(
"ZIP Central Directory is not immediately followed by End of Central"
+ " Directory");
}
return centralDirOffset;
}

16. ZipUtils.getZipEocdCentralDirectoryOffset

1
2
3
4
5
6
7
8
9
// frameworks/base/core/java/android/util/apk/ZipUtils.java
// 返回 ZIP 中央目录开始的偏移量。
public static long getZipEocdCentralDirectoryOffset(ByteBuffer zipEndOfCentralDirectory) {
assertByteOrderLittleEndian(zipEndOfCentralDirectory);
// ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET=16
return getUnsignedInt32(
zipEndOfCentralDirectory,
zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET);
}

17. ZipUtils.getZipEocdCentralDirectorySizeBytes

1
2
3
4
5
6
7
8
9
// frameworks/base/core/java/android/util/apk/ZipUtils.java
// 返回 ZIP 中央目录的大小(以字节为单位)。
public static long getZipEocdCentralDirectorySizeBytes(ByteBuffer zipEndOfCentralDirectory) {
assertByteOrderLittleEndian(zipEndOfCentralDirectory);
// ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET=12
return getUnsignedInt32(
zipEndOfCentralDirectory,
zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET);
}

18. ApkSigningBlockUtils.findApkSigningBlock

findApkSigningBlock 方法会获取签名块内容,包括magic、offset等信息。

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
// // frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
static Pair<ByteBuffer, Long> findApkSigningBlock(
RandomAccessFile apk, long centralDirOffset)
throws IOException, SignatureNotFoundException {
// OFFSET DATA TYPE DESCRIPTION
// 0 bytes uint64: 大小(以字节为单位)(不包括此字段)
// 8 bytes payload
// -24 bytes uint64: 字节大小(与上面的相同)
// -16 bytes uint128: 魔数magic
// APK_SIG_BLOCK_MIN_SIZE=32
if (centralDirOffset < APK_SIG_BLOCK_MIN_SIZE) {
throw new SignatureNotFoundException(
"APK too small for APK Signing Block. ZIP Central Directory offset: "
+ centralDirOffset);
}
// 从块的页脚部分读取魔术magic和偏移量offset
// uint64: 块大小
// 16 bytes: magic
ByteBuffer footer = ByteBuffer.allocate(24);
footer.order(ByteOrder.LITTLE_ENDIAN);
apk.seek(centralDirOffset - footer.capacity());
apk.readFully(footer.array(), footer.arrayOffset(), footer.capacity());
// APK_SIG_BLOCK_MAGIC_LO=0x20676953204b5041L
// APK_SIG_BLOCK_MAGIC_HI=0x3234206b636f6c42L
if ((footer.getLong(8) != APK_SIG_BLOCK_MAGIC_LO)
|| (footer.getLong(16) != APK_SIG_BLOCK_MAGIC_HI)) {
throw new SignatureNotFoundException(
"No APK Signing Block before ZIP Central Directory");
}
// 读取并比较字段大小。
long apkSigBlockSizeInFooter = footer.getLong(0);
if ((apkSigBlockSizeInFooter < footer.capacity())
|| (apkSigBlockSizeInFooter > Integer.MAX_VALUE - 8)) {
throw new SignatureNotFoundException(
"APK Signing Block size out of range: " + apkSigBlockSizeInFooter);
}
int totalSize = (int) (apkSigBlockSizeInFooter + 8);
long apkSigBlockOffset = centralDirOffset - totalSize;
if (apkSigBlockOffset < 0) {
throw new SignatureNotFoundException(
"APK Signing Block offset out of range: " + apkSigBlockOffset);
}
ByteBuffer apkSigBlock = ByteBuffer.allocate(totalSize);
apkSigBlock.order(ByteOrder.LITTLE_ENDIAN);
apk.seek(apkSigBlockOffset);
apk.readFully(apkSigBlock.array(), apkSigBlock.arrayOffset(), apkSigBlock.capacity());
long apkSigBlockSizeInHeader = apkSigBlock.getLong(0);
if (apkSigBlockSizeInHeader != apkSigBlockSizeInFooter) {
throw new SignatureNotFoundException(
"APK Signing Block sizes in header and footer do not match: "
+ apkSigBlockSizeInHeader + " vs " + apkSigBlockSizeInFooter);
}
return Pair.create(apkSigBlock, apkSigBlockOffset);
}

19. ApkSigningBlockUtils.findApkSignatureSchemeBlock

findApkSignatureSchemeBlock 方法会从签名块中拿到 v3 签名方案块内容,包括 v3 签名方案对应的 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
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
static ByteBuffer findApkSignatureSchemeBlock(ByteBuffer apkSigningBlock, int blockId)
throws SignatureNotFoundException {
checkByteOrderLittleEndian(apkSigningBlock);
// FORMAT:
// OFFSET DATA TYPE DESCRIPTION
// * @+0 bytes uint64: size in bytes (excluding this field)
// * @+8 bytes pairs
// * @-24 bytes uint64: size in bytes (same as the one above)
// * @-16 bytes uint128: magic
ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24);
int entryCount = 0;
while (pairs.hasRemaining()) {
entryCount++;
if (pairs.remaining() < 8) {
throw new SignatureNotFoundException(
"Insufficient data to read size of APK Signing Block entry #" + entryCount);
}
long lenLong = pairs.getLong();
if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) {
throw new SignatureNotFoundException(
"APK Signing Block entry #" + entryCount
+ " size out of range: " + lenLong);
}
int len = (int) lenLong;
int nextEntryPos = pairs.position() + len;
if (len > pairs.remaining()) {
throw new SignatureNotFoundException(
"APK Signing Block entry #" + entryCount + " size out of range: " + len
+ ", available: " + pairs.remaining());
}
int id = pairs.getInt();
if (id == blockId) {
return getByteBuffer(pairs, len - 4);
}
pairs.position(nextEntryPos);
}
throw new SignatureNotFoundException(
"No block with ID " + blockId + " in APK Signing Block.");
}

20. ApkSigningBlockUtils.sliceFromTo

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
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
// 返回新的字节缓冲区,其内容是此缓冲区的内容在指定的开始位置(包含)和结束位置(不包含)之间的共享子序列。
// 与 ByteBuffer#slice() 相反,返回的缓冲区的字节顺序与源缓冲区的字节顺序相同。
static ByteBuffer sliceFromTo(ByteBuffer source, int start, int end) {
if (start < 0) {
throw new IllegalArgumentException("start: " + start);
}
if (end < start) {
throw new IllegalArgumentException("end < start: " + end + " < " + start);
}
int capacity = source.capacity();
if (end > source.capacity()) {
throw new IllegalArgumentException("end > capacity: " + end + " > " + capacity);
}
int originalLimit = source.limit();
int originalPosition = source.position();
try {
source.position(0);
source.limit(end);
source.position(start);
ByteBuffer result = source.slice();
result.order(source.order());
return result;
} finally {
source.position(0);
source.limit(originalLimit);
source.position(originalPosition);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
   // frameworks/base/core/java/android/util/apk/SignatureInfo.java
SignatureInfo(ByteBuffer signatureBlock, long apkSigningBlockOffset, long centralDirOffset,
long eocdOffset, ByteBuffer eocd) {
// 签名数据块。
this.signatureBlock = signatureBlock;
// 签名数据块偏移量。
this.apkSigningBlockOffset = apkSigningBlockOffset;
// 中央目录偏移。
this.centralDirOffset = centralDirOffset;
// 中央目录结束块偏移。
this.eocdOffset = eocdOffset;
// 中央目录结束块数据。
this.eocd = eocd;
}

21. ApkSignatureSchemeV3Verifier.verifySigner

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
// frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
private static VerifiedSigner verifySigner(
ByteBuffer signerBlock,
Map<Integer, byte[]> contentDigests,
CertificateFactory certFactory)
throws SecurityException, IOException, PlatformNotSupportedException {
ByteBuffer signedData = getLengthPrefixedSlice(signerBlock);
int minSdkVersion = signerBlock.getInt();
int maxSdkVersion = signerBlock.getInt();
if (Build.VERSION.SDK_INT < minSdkVersion || Build.VERSION.SDK_INT > maxSdkVersion) {
// 使用非该平台的签名,跳过。
throw new PlatformNotSupportedException(
"Signer not supported by this platform "
+ "version. This platform: " + Build.VERSION.SDK_INT
+ ", signer minSdkVersion: " + minSdkVersion
+ ", maxSdkVersion: " + maxSdkVersion);
}
ByteBuffer signatures = getLengthPrefixedSlice(signerBlock);
byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock);
int signatureCount = 0;
int bestSigAlgorithm = -1;
byte[] bestSigAlgorithmSignatureBytes = null;
List<Integer> signaturesSigAlgorithms = new ArrayList<>();
while (signatures.hasRemaining()) {
signatureCount++;
try {
ByteBuffer signature = getLengthPrefixedSlice(signatures);
if (signature.remaining() < 8) {
throw new SecurityException("Signature record too short");
}
int sigAlgorithm = signature.getInt();
signaturesSigAlgorithms.add(sigAlgorithm);
if (!isSupportedSignatureAlgorithm(sigAlgorithm)) {
continue;
}
if ((bestSigAlgorithm == -1)
|| (compareSignatureAlgorithm(sigAlgorithm, bestSigAlgorithm) > 0)) {
bestSigAlgorithm = sigAlgorithm;
bestSigAlgorithmSignatureBytes = readLengthPrefixedByteArray(signature);
}
} catch (IOException | BufferUnderflowException e) {
throw new SecurityException(
"Failed to parse signature record #" + signatureCount,
e);
}
}
if (bestSigAlgorithm == -1) {
if (signatureCount == 0) {
throw new SecurityException("No signatures found");
} else {
throw new SecurityException("No supported signatures found");
}
}
String keyAlgorithm = getSignatureAlgorithmJcaKeyAlgorithm(bestSigAlgorithm);
Pair<String, ? extends AlgorithmParameterSpec> signatureAlgorithmParams =
getSignatureAlgorithmJcaSignatureAlgorithm(bestSigAlgorithm);
String jcaSignatureAlgorithm = signatureAlgorithmParams.first;
AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithmParams.second;
boolean sigVerified;
try {
PublicKey publicKey =
KeyFactory.getInstance(keyAlgorithm)
.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
sig.initVerify(publicKey);
if (jcaSignatureAlgorithmParams != null) {
sig.setParameter(jcaSignatureAlgorithmParams);
}
sig.update(signedData);
sigVerified = sig.verify(bestSigAlgorithmSignatureBytes);
} catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException
| InvalidAlgorithmParameterException | SignatureException e) {
throw new SecurityException(
"Failed to verify " + jcaSignatureAlgorithm + " signature", e);
}
if (!sigVerified) {
throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify");
}
// 签名数据已验证。
byte[] contentDigest = null;
signedData.clear();
ByteBuffer digests = getLengthPrefixedSlice(signedData);
List<Integer> digestsSigAlgorithms = new ArrayList<>();
int digestCount = 0;
while (digests.hasRemaining()) {
digestCount++;
try {
ByteBuffer digest = getLengthPrefixedSlice(digests);
if (digest.remaining() < 8) {
throw new IOException("Record too short");
}
int sigAlgorithm = digest.getInt();
digestsSigAlgorithms.add(sigAlgorithm);
if (sigAlgorithm == bestSigAlgorithm) {
contentDigest = readLengthPrefixedByteArray(digest);
}
} catch (IOException | BufferUnderflowException e) {
throw new IOException("Failed to parse digest record #" + digestCount, e);
}
}
if (!signaturesSigAlgorithms.equals(digestsSigAlgorithms)) {
throw new SecurityException(
"Signature algorithms don't match between digests and signatures records");
}
int digestAlgorithm = getSignatureAlgorithmContentDigestAlgorithm(bestSigAlgorithm);
byte[] previousSignerDigest = contentDigests.put(digestAlgorithm, contentDigest);
if ((previousSignerDigest != null)
&& (!MessageDigest.isEqual(previousSignerDigest, contentDigest))) {
throw new SecurityException(
getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
+ " contents digest does not match the digest specified by a preceding signer");
}
ByteBuffer certificates = getLengthPrefixedSlice(signedData);
List<X509Certificate> certs = new ArrayList<>();
int certificateCount = 0;
while (certificates.hasRemaining()) {
certificateCount++;
byte[] encodedCert = readLengthPrefixedByteArray(certificates);
X509Certificate certificate;
try {
certificate = (X509Certificate)
certFactory.generateCertificate(new ByteArrayInputStream(encodedCert));
} catch (CertificateException e) {
throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
}
certificate = new VerbatimX509Certificate(
certificate, encodedCert);
certs.add(certificate);
}
if (certs.isEmpty()) {
throw new SecurityException("No certificates listed");
}
X509Certificate mainCertificate = certs.get(0);
byte[] certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
throw new SecurityException(
"Public key mismatch between certificate and signature record");
}
int signedMinSDK = signedData.getInt();
if (signedMinSDK != minSdkVersion) {
throw new SecurityException(
"minSdkVersion mismatch between signed and unsigned in v3 signer block.");
}
int signedMaxSDK = signedData.getInt();
if (signedMaxSDK != maxSdkVersion) {
throw new SecurityException(
"maxSdkVersion mismatch between signed and unsigned in v3 signer block.");
}
ByteBuffer additionalAttrs = getLengthPrefixedSlice(signedData);
return verifyAdditionalAttributes(additionalAttrs, certs, certFactory);
}

22. ApkSignatureSchemeV2Verifier.isSupportedSignatureAlgorithm

isSupportedSignatureAlgorithm 方法中列出了当前所支持的签名算法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
switch (sigAlgorithm) {
case SIGNATURE_RSA_PSS_WITH_SHA256:
case SIGNATURE_RSA_PSS_WITH_SHA512:
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
case SIGNATURE_ECDSA_WITH_SHA256:
case SIGNATURE_ECDSA_WITH_SHA512:
case SIGNATURE_DSA_WITH_SHA256:
case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256:
case SIGNATURE_VERITY_ECDSA_WITH_SHA256:
case SIGNATURE_VERITY_DSA_WITH_SHA256:
return true;
default:
return false;
}
}

23. ApkSigningBlockUtils.compareSignatureAlgorithm

1
2
3
4
5
6
7
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
// 进行签名算法比对。
static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) {
int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1);
int digestAlgorithm2 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm2);
return compareContentDigestAlgorithm(digestAlgorithm1, digestAlgorithm2);
}

24. ApkSigningBlockUtils.getSignatureAlgorithmContentDigestAlgorithm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
// 当前支持的内容摘要算法。
static int getSignatureAlgorithmContentDigestAlgorithm(int sigAlgorithm) {
switch (sigAlgorithm) {
case SIGNATURE_RSA_PSS_WITH_SHA256:
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
case SIGNATURE_ECDSA_WITH_SHA256:
case SIGNATURE_DSA_WITH_SHA256:
return CONTENT_DIGEST_CHUNKED_SHA256;
case SIGNATURE_RSA_PSS_WITH_SHA512:
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
case SIGNATURE_ECDSA_WITH_SHA512:
return CONTENT_DIGEST_CHUNKED_SHA512;
case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256:
case SIGNATURE_VERITY_ECDSA_WITH_SHA256:
case SIGNATURE_VERITY_DSA_WITH_SHA256:
return CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
default:
throw new IllegalArgumentException(
"Unknown signature algorithm: 0x"
+ Long.toHexString(sigAlgorithm & 0xffffffff));
}
}

25. ApkSigningBlockUtils.compareContentDigestAlgorithm

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
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
// 比对内容摘要算法。
private static int compareContentDigestAlgorithm(int digestAlgorithm1, int digestAlgorithm2) {
switch (digestAlgorithm1) {
case CONTENT_DIGEST_CHUNKED_SHA256:
switch (digestAlgorithm2) {
case CONTENT_DIGEST_CHUNKED_SHA256:
return 0;
case CONTENT_DIGEST_CHUNKED_SHA512:
case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
return -1;
default:
throw new IllegalArgumentException(
"Unknown digestAlgorithm2: " + digestAlgorithm2);
}
case CONTENT_DIGEST_CHUNKED_SHA512:
switch (digestAlgorithm2) {
case CONTENT_DIGEST_CHUNKED_SHA256:
case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
return 1;
case CONTENT_DIGEST_CHUNKED_SHA512:
return 0;
default:
throw new IllegalArgumentException(
"Unknown digestAlgorithm2: " + digestAlgorithm2);
}
case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
switch (digestAlgorithm2) {
case CONTENT_DIGEST_CHUNKED_SHA512:
return -1;
case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
return 0;
case CONTENT_DIGEST_CHUNKED_SHA256:
return 1;
default:
throw new IllegalArgumentException(
"Unknown digestAlgorithm2: " + digestAlgorithm2);
}
default:
throw new IllegalArgumentException("Unknown digestAlgorithm1: " + digestAlgorithm1);
}
}

26. ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
// 当前支持的 java 加密算法。
static String getSignatureAlgorithmJcaKeyAlgorithm(int sigAlgorithm) {
switch (sigAlgorithm) {
case SIGNATURE_RSA_PSS_WITH_SHA256:
case SIGNATURE_RSA_PSS_WITH_SHA512:
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256:
return "RSA";
case SIGNATURE_ECDSA_WITH_SHA256:
case SIGNATURE_ECDSA_WITH_SHA512:
case SIGNATURE_VERITY_ECDSA_WITH_SHA256:
return "EC";
case SIGNATURE_DSA_WITH_SHA256:
case SIGNATURE_VERITY_DSA_WITH_SHA256:
return "DSA";
default:
throw new IllegalArgumentException(
"Unknown signature algorithm: 0x"
+ Long.toHexString(sigAlgorithm & 0xffffffff));
}
}

27. ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm

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/apk/ApkSigningBlockUtils.java
// 当前支持的 java 签名算法。
static Pair<String, ? extends AlgorithmParameterSpec>
getSignatureAlgorithmJcaSignatureAlgorithm(int sigAlgorithm) {
switch (sigAlgorithm) {
case SIGNATURE_RSA_PSS_WITH_SHA256:
return Pair.create(
"SHA256withRSA/PSS",
new PSSParameterSpec(
"SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 256 / 8, 1));
case SIGNATURE_RSA_PSS_WITH_SHA512:
return Pair.create(
"SHA512withRSA/PSS",
new PSSParameterSpec(
"SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1));
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256:
return Pair.create("SHA256withRSA", null);
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
return Pair.create("SHA512withRSA", null);
case SIGNATURE_ECDSA_WITH_SHA256:
case SIGNATURE_VERITY_ECDSA_WITH_SHA256:
return Pair.create("SHA256withECDSA", null);
case SIGNATURE_ECDSA_WITH_SHA512:
return Pair.create("SHA512withECDSA", null);
case SIGNATURE_DSA_WITH_SHA256:
case SIGNATURE_VERITY_DSA_WITH_SHA256:
return Pair.create("SHA256withDSA", null);
default:
throw new IllegalArgumentException(
"Unknown signature algorithm: 0x"
+ Long.toHexString(sigAlgorithm & 0xffffffff));
}
}

28. ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
// 当前支持的 java 摘要算法。
static String getContentDigestAlgorithmJcaDigestAlgorithm(int digestAlgorithm) {
switch (digestAlgorithm) {
case CONTENT_DIGEST_CHUNKED_SHA256:
case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
return "SHA-256";
case CONTENT_DIGEST_CHUNKED_SHA512:
return "SHA-512";
default:
throw new IllegalArgumentException(
"Unknown content digest algorthm: " + digestAlgorithm);
}
}

29. ApkSignatureSchemeV3Verifier.verifyAdditionalAttributes

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
// frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
// 验证其它附加数据。
private static VerifiedSigner verifyAdditionalAttributes(ByteBuffer attrs,
List<X509Certificate> certs, CertificateFactory certFactory) throws IOException {
X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]);
VerifiedProofOfRotation por = null;
while (attrs.hasRemaining()) {
ByteBuffer attr = getLengthPrefixedSlice(attrs);
if (attr.remaining() < 4) {
throw new IOException("Remaining buffer too short to contain additional attribute "
+ "ID. Remaining: " + attr.remaining());
}
int id = attr.getInt();
switch(id) {
case PROOF_OF_ROTATION_ATTR_ID:
if (por != null) {
throw new SecurityException("Encountered multiple Proof-of-rotation records"
+ " when verifying APK Signature Scheme v3 signature");
}
por = verifyProofOfRotationStruct(attr, certFactory);
// 确保旋转证明Proof-of-rotation记录中的最后一个证书与用于对此 APK 签名的证书相匹配。
try {
if (por.certs.size() > 0
&& !Arrays.equals(por.certs.get(por.certs.size() - 1).getEncoded(),
certChain[0].getEncoded())) {
throw new SecurityException("Terminal certificate in Proof-of-rotation"
+ " record does not match APK signing certificate");
}
} catch (CertificateEncodingException e) {
throw new SecurityException("Failed to encode certificate when comparing"
+ " Proof-of-rotation record and signing certificate", e);
}

break;
default:
break;
}
}
return new VerifiedSigner(certChain, por);
}

30. ApkSignatureSchemeV3Verifier.verifyProofOfRotationStruct

verifyProofOfRotationStruct 方法主要用于验证 v3 签名新增功能:轮换签名。

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
// frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
private static VerifiedProofOfRotation verifyProofOfRotationStruct(
ByteBuffer porBuf,
CertificateFactory certFactory)
throws SecurityException, IOException {
int levelCount = 0;
int lastSigAlgorithm = -1;
X509Certificate lastCert = null;
List<X509Certificate> certs = new ArrayList<>();
List<Integer> flagsList = new ArrayList<>();
// 旋转证明结构Proof-of-rotation:
// 一个uint32版本代码,后面基本上是一个单链接的节点列表,在这里称为级别,每个级别都具有以下结构:
// 整个级别的长度前缀:
// (1)长度前缀的签名数据(如果存在上一级别):
// a.带前缀的X509证书;
// b.uint32签名算法ID,用于描述该签名数据的签名方式。
// (2)uint32标志,描述如何处理此级别中包含的证书。
// (3)uint32签名算法ID,用于验证下一级的签名。此处的算法必须与下一级的签名数据部分中的算法匹配。
// (4)在此级别中对签名数据进行长度前缀签名。此处的签名使用上一级的证书进行验证。
// 该链接由每个级别的证书提供,该证书用于签署下一个证书。
try {
// 获取版本代码,但不执行任何操作:创建者知道所有的标志
porBuf.getInt();
HashSet<X509Certificate> certHistorySet = new HashSet<>();
while (porBuf.hasRemaining()) {
levelCount++;
ByteBuffer level = getLengthPrefixedSlice(porBuf);
ByteBuffer signedData = getLengthPrefixedSlice(level);
int flags = level.getInt();
int sigAlgorithm = level.getInt();
byte[] signature = readLengthPrefixedByteArray(level);
if (lastCert != null) {
// 使用以前的级别证书来验证当前级别。
Pair<String, ? extends AlgorithmParameterSpec> sigAlgParams =
getSignatureAlgorithmJcaSignatureAlgorithm(lastSigAlgorithm);
PublicKey publicKey = lastCert.getPublicKey();
Signature sig = Signature.getInstance(sigAlgParams.first);
sig.initVerify(publicKey);
if (sigAlgParams.second != null) {
sig.setParameter(sigAlgParams.second);
}
sig.update(signedData);
if (!sig.verify(signature)) {
throw new SecurityException("Unable to verify signature of certificate #"
+ levelCount + " using " + sigAlgParams.first + " when verifying"
+ " Proof-of-rotation record");
}
}
signedData.rewind();
byte[] encodedCert = readLengthPrefixedByteArray(signedData);
int signedSigAlgorithm = signedData.getInt();
if (lastCert != null && lastSigAlgorithm != signedSigAlgorithm) {
throw new SecurityException("Signing algorithm ID mismatch for certificate #"
+ levelCount + " when verifying Proof-of-rotation record");
}
lastCert = (X509Certificate)
certFactory.generateCertificate(new ByteArrayInputStream(encodedCert));
lastCert = new VerbatimX509Certificate(lastCert, encodedCert);

lastSigAlgorithm = sigAlgorithm;
if (certHistorySet.contains(lastCert)) {
throw new SecurityException("Encountered duplicate entries in "
+ "Proof-of-rotation record at certificate #" + levelCount + ". All "
+ "signing certificates should be unique");
}
certHistorySet.add(lastCert);
certs.add(lastCert);
flagsList.add(flags);
}
} catch (IOException | BufferUnderflowException e) {
throw new IOException("Failed to parse Proof-of-rotation record", e);
} catch (NoSuchAlgorithmException | InvalidKeyException
| InvalidAlgorithmParameterException | SignatureException e) {
throw new SecurityException(
"Failed to verify signature over signed data for certificate #"
+ levelCount + " when verifying Proof-of-rotation record", e);
} catch (CertificateException e) {
throw new SecurityException("Failed to decode certificate #" + levelCount
+ " when verifying Proof-of-rotation record", e);
}
return new VerifiedProofOfRotation(certs, flagsList);
}

31. ApkSignatureSchemeV3Verifier.VerifiedProofOfRotation

1
2
3
4
5
6
7
8
9
10
// frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
// proof-of-rotation验证过程。
public static class VerifiedProofOfRotation {
public final List<X509Certificate> certs;
public final List<Integer> flagsList;
public VerifiedProofOfRotation(List<X509Certificate> certs, List<Integer> flagsList) {
this.certs = certs;
this.flagsList = flagsList;
}
}

32. ApkSignatureSchemeV3Verifier.VerifiedSigner

1
2
3
4
5
6
7
8
9
10
11
12
// frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
// 经过验证的APK签名方案v3签名者,包括旋转证明结构proof-of-rotation。
public static class VerifiedSigner {
public final X509Certificate[] certs;
public final VerifiedProofOfRotation por;
public byte[] verityRootHash;
public byte[] digest;
public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
this.certs = certs;
this.por = por;
}
}

33. ApkSigningBlockUtils.verifyIntegrity

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/ApkSigningBlockUtils.java
static void verifyIntegrity(
Map<Integer, byte[]> expectedDigests,
RandomAccessFile apk,
SignatureInfo signatureInfo) throws SecurityException {
if (expectedDigests.isEmpty()) {
throw new SecurityException("No digests provided");
}
boolean neverVerified = true;
Map<Integer, byte[]> expected1MbChunkDigests = new ArrayMap<>();
if (expectedDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA256)) {
expected1MbChunkDigests.put(CONTENT_DIGEST_CHUNKED_SHA256,
expectedDigests.get(CONTENT_DIGEST_CHUNKED_SHA256));
}
if (expectedDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA512)) {
expected1MbChunkDigests.put(CONTENT_DIGEST_CHUNKED_SHA512,
expectedDigests.get(CONTENT_DIGEST_CHUNKED_SHA512));
}
if (!expected1MbChunkDigests.isEmpty()) {
try {
verifyIntegrityFor1MbChunkBasedAlgorithm(expected1MbChunkDigests, apk.getFD(),
signatureInfo);
neverVerified = false;
} catch (IOException e) {
throw new SecurityException("Cannot get FD", e);
}
}
if (expectedDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
verifyIntegrityForVerityBasedAlgorithm(
expectedDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256), apk, signatureInfo);
neverVerified = false;
}
if (neverVerified) {
throw new SecurityException("No known digest exists for integrity check");
}
}

34. ApkSigningBlockUtils.verifyIntegrityFor1MbChunkBasedAlgorithm

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
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
private static void verifyIntegrityFor1MbChunkBasedAlgorithm(
Map<Integer, byte[]> expectedDigests,
FileDescriptor apkFileDescriptor,
SignatureInfo signatureInfo) throws SecurityException {
// 需要验证文件以下三个部分的完整性:
// 1.直到 APK 签名块开始的所有内容。
// 2.ZIP 中央目录。
// 3.中央目录(EoCD)的 ZIP 结尾。
// 每个部分都在下面以单独的DataSource实例来呈现。
// 要处理大型APK,应使用内存映射的 I/O 以 1MB 的块读取这些部分,以避免浪费物理内存。
// 在大多数 APK 验证方案中,APK 的内容已经存在于操作系统的页面缓存中,因此 mmap 不会使用额外的物理内存。
DataSource beforeApkSigningBlock =
new MemoryMappedFileDataSource(apkFileDescriptor, 0,
signatureInfo.apkSigningBlockOffset);
DataSource centralDir =
new MemoryMappedFileDataSource(
apkFileDescriptor, signatureInfo.centralDirOffset,
signatureInfo.eocdOffset - signatureInfo.centralDirOffset);
// 为了进行完整性验证,“中央目录的ZIP结尾”字段的“中央目录的起始”必须被考虑,以确定 APK 签名块的偏移量。
ByteBuffer eocdBuf = signatureInfo.eocd.duplicate();
eocdBuf.order(ByteOrder.LITTLE_ENDIAN);
ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, signatureInfo.apkSigningBlockOffset);
DataSource eocd = new ByteBufferDataSource(eocdBuf);
int[] digestAlgorithms = new int[expectedDigests.size()];
int digestAlgorithmCount = 0;
for (int digestAlgorithm : expectedDigests.keySet()) {
digestAlgorithms[digestAlgorithmCount] = digestAlgorithm;
digestAlgorithmCount++;
}
byte[][] actualDigests;
try {
actualDigests =
computeContentDigestsPer1MbChunk(
digestAlgorithms,
new DataSource[] {beforeApkSigningBlock, centralDir, eocd});
} catch (DigestException e) {
throw new SecurityException("Failed to compute digest(s) of contents", e);
}
for (int i = 0; i < digestAlgorithms.length; i++) {
int digestAlgorithm = digestAlgorithms[i];
byte[] expectedDigest = expectedDigests.get(digestAlgorithm);
byte[] actualDigest = actualDigests[i];
if (!MessageDigest.isEqual(expectedDigest, actualDigest)) {
throw new SecurityException(
getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
+ " digest of contents did not verify");
}
}
}

35. ApkSigningBlockUtils.computeContentDigestsPer1MbChunk

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
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
private static byte[][] computeContentDigestsPer1MbChunk(
int[] digestAlgorithms,
DataSource[] contents) throws DigestException {
// 对于每种摘要算法,结果的计算如下:
// 1.每个内容段都被分成大小为 1MB的连续块。如果段的长度不是 1MB的倍数,则最后一个块会更短。空(零长度)段不会产生任何块。
// 2.每个块的摘要是通过字节 0xa5 的串联,块的长度(以字节为单位)(uint32 小端)和块的内容来计算的。
// 3.输出的摘要是按字节 0x5a 的串联,块的数量(uint32 小端)和所有段的块的摘要的串联进行计算的。
long totalChunkCountLong = 0;
for (DataSource input : contents) {
totalChunkCountLong += getChunkCount(input.size());
}
if (totalChunkCountLong >= Integer.MAX_VALUE / 1024) {
throw new DigestException("Too many chunks: " + totalChunkCountLong);
}
int totalChunkCount = (int) totalChunkCountLong;
byte[][] digestsOfChunks = new byte[digestAlgorithms.length][];
for (int i = 0; i < digestAlgorithms.length; i++) {
int digestAlgorithm = digestAlgorithms[i];
int digestOutputSizeBytes = getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm);
byte[] concatenationOfChunkCountAndChunkDigests =
new byte[5 + totalChunkCount * digestOutputSizeBytes];
concatenationOfChunkCountAndChunkDigests[0] = 0x5a;
setUnsignedInt32LittleEndian(
totalChunkCount,
concatenationOfChunkCountAndChunkDigests,
1);
digestsOfChunks[i] = concatenationOfChunkCountAndChunkDigests;
}
byte[] chunkContentPrefix = new byte[5];
chunkContentPrefix[0] = (byte) 0xa5;
int chunkIndex = 0;
MessageDigest[] mds = new MessageDigest[digestAlgorithms.length];
for (int i = 0; i < digestAlgorithms.length; i++) {
String jcaAlgorithmName =
getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithms[i]);
try {
mds[i] = MessageDigest.getInstance(jcaAlgorithmName);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(jcaAlgorithmName + " digest not supported", e);
}
}
// 并行计算块摘要。需要对如何基于硬件能力并行化以及基于基于输入的大小进行并行化(如果有的话)进行一些研究。
DataDigester digester = new MultipleDigestDataDigester(mds);
int dataSourceIndex = 0;
for (DataSource input : contents) {
long inputOffset = 0;
long inputRemaining = input.size();
while (inputRemaining > 0) {
int chunkSize = (int) Math.min(inputRemaining, CHUNK_SIZE_BYTES);
setUnsignedInt32LittleEndian(chunkSize, chunkContentPrefix, 1);
for (int i = 0; i < mds.length; i++) {
mds[i].update(chunkContentPrefix);
}
try {
input.feedIntoDataDigester(digester, inputOffset, chunkSize);
} catch (IOException e) {
throw new DigestException(
"Failed to digest chunk #" + chunkIndex + " of section #"
+ dataSourceIndex,
e);
}
for (int i = 0; i < digestAlgorithms.length; i++) {
int digestAlgorithm = digestAlgorithms[i];
byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i];
int expectedDigestSizeBytes =
getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm);
MessageDigest md = mds[i];
int actualDigestSizeBytes =
md.digest(
concatenationOfChunkCountAndChunkDigests,
5 + chunkIndex * expectedDigestSizeBytes,
expectedDigestSizeBytes);
if (actualDigestSizeBytes != expectedDigestSizeBytes) {
throw new RuntimeException(
"Unexpected output size of " + md.getAlgorithm() + " digest: "
+ actualDigestSizeBytes);
}
}
inputOffset += chunkSize;
inputRemaining -= chunkSize;
chunkIndex++;
}
dataSourceIndex++;
}
byte[][] result = new byte[digestAlgorithms.length][];
for (int i = 0; i < digestAlgorithms.length; i++) {
int digestAlgorithm = digestAlgorithms[i];
byte[] input = digestsOfChunks[i];
String jcaAlgorithmName = getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm);
MessageDigest md;
try {
md = MessageDigest.getInstance(jcaAlgorithmName);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(jcaAlgorithmName + " digest not supported", e);
}
byte[] output = md.digest(input);
result[i] = output;
}
return result;
}

36. ApkSigningBlockUtils.getContentDigestAlgorithmOutputSizeBytes

1
2
3
4
5
6
7
8
9
10
11
12
13
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
private static int getContentDigestAlgorithmOutputSizeBytes(int digestAlgorithm) {
switch (digestAlgorithm) {
case CONTENT_DIGEST_CHUNKED_SHA256:
case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
return 256 / 8;
case CONTENT_DIGEST_CHUNKED_SHA512:
return 512 / 8;
default:
throw new IllegalArgumentException(
"Unknown content digest algorthm: " + digestAlgorithm);
}
}

37. ApkSigningBlockUtils.setUnsignedInt32LittleEndian

1
2
3
4
5
6
7
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
static void setUnsignedInt32LittleEndian(int value, byte[] result, int offset) {
result[offset] = (byte) (value & 0xff);
result[offset + 1] = (byte) ((value >>> 8) & 0xff);
result[offset + 2] = (byte) ((value >>> 16) & 0xff);
result[offset + 3] = (byte) ((value >>> 24) & 0xff);
}

38. ApkSigningBlockUtils.MultipleDigestDataDigester

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
// DataDigester,只要有数据输入,它就会更新多个 MessageDigest。
private static class MultipleDigestDataDigester implements DataDigester {
private final MessageDigest[] mMds;
MultipleDigestDataDigester(MessageDigest[] mds) {
mMds = mds;
}
@Override
public void consume(ByteBuffer buffer) {
buffer = buffer.slice();
for (MessageDigest md : mMds) {
buffer.position(0);
md.update(buffer);
}
}
}

39. ApkSigningBlockUtils.verifyIntegrityForVerityBasedAlgorithm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
private static void verifyIntegrityForVerityBasedAlgorithm(
byte[] expectedDigest,
RandomAccessFile apk,
SignatureInfo signatureInfo) throws SecurityException {
try {
byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
apk.length(), signatureInfo);
VerityBuilder.VerityResult verity = VerityBuilder.generateApkVerityTree(apk,
signatureInfo, new ByteBufferFactory() {
@Override
public ByteBuffer create(int capacity) {
return ByteBuffer.allocate(capacity);
}
});
if (!Arrays.equals(expectedRootHash, verity.rootHash)) {
throw new SecurityException("APK verity digest of contents did not verify");
}
} catch (DigestException | IOException | NoSuchAlgorithmException e) {
throw new SecurityException("Error during verification", e);
}
}

40. ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// frameworks/base/core/java/android/util/apk/ApkSigningBlockUtils.java
// 仅当摘要内容的长度看起来正确时才返回 verity 摘要。生成真实摘要时,在散列之前,将最后一个不完整的4k块填充为0。
// 这意味着最后两个不同的0几乎相同的APK具有相同的验证摘要。为避免此问题,将源内容的长度(不包括签名块)附加到 verity 摘要中,并且仅在长度与当前APK一致时才返回摘要。
static byte[] parseVerityDigestAndVerifySourceLength(
byte[] data, long fileSize, SignatureInfo signatureInfo) throws SecurityException {
// FORMAT:
// OFFSET DATA TYPE DESCRIPTION
// +0 bytes uint8[32] SHA-256的merkle根哈希
// +32 bytes int64 源数据长度
int kRootHashSize = 32;
int kSourceLengthSize = 8;
if (data.length != kRootHashSize + kSourceLengthSize) {
throw new SecurityException("Verity digest size is wrong: " + data.length);
}
ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
buffer.position(kRootHashSize);
long expectedSourceLength = buffer.getLong();
long signingBlockSize = signatureInfo.centralDirOffset
- signatureInfo.apkSigningBlockOffset;
if (expectedSourceLength != fileSize - signingBlockSize) {
throw new SecurityException("APK content size did not verify");
}
return Arrays.copyOfRange(data, 0, kRootHashSize);
}

41. 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;
}