1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.apksig; 18 19 import static com.android.apksig.ApkVerifier.Result.V3SchemeSignerInfo; 20 import static com.android.apksig.ApkVerifierTest.assertVerificationWarning; 21 import static com.android.apksig.SigningCertificateLineage.SignerCapabilities; 22 import static com.android.apksig.SigningCertificateLineageTest.assertLineageContainsExpectedSigners; 23 import static com.android.apksig.SigningCertificateLineageTest.assertLineageContainsExpectedSignersWithCapabilities; 24 import static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME; 25 import static com.android.apksig.apk.ApkUtils.findZipSections; 26 import static com.android.apksig.internal.util.Resources.EC_P256_2_SIGNER_RESOURCE_NAME; 27 import static com.android.apksig.internal.util.Resources.EC_P256_SIGNER_RESOURCE_NAME; 28 import static com.android.apksig.internal.util.Resources.FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS; 29 import static com.android.apksig.internal.util.Resources.FIRST_RSA_2048_SIGNER_RESOURCE_NAME; 30 import static com.android.apksig.internal.util.Resources.FIRST_RSA_4096_SIGNER_RESOURCE_NAME; 31 import static com.android.apksig.internal.util.Resources.LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME; 32 import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_2_SIGNERS_2_3_RESOURCE_NAME; 33 import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME; 34 import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_3_SIGNERS_1_NO_CAPS_RESOURCE_NAME; 35 import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME; 36 import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_TO_RSA_4096_RESOURCE_NAME; 37 import static com.android.apksig.internal.util.Resources.SECOND_RSA_2048_SIGNER_RESOURCE_NAME; 38 // BEGIN-AOSP 39 import static com.android.apksig.internal.util.Resources.TEST_GCP_KEY_RING; 40 // END-AOSP 41 import static com.android.apksig.internal.util.Resources.THIRD_RSA_2048_SIGNER_RESOURCE_NAME; 42 43 import static org.junit.Assert.assertArrayEquals; 44 import static org.junit.Assert.assertEquals; 45 import static org.junit.Assert.assertFalse; 46 import static org.junit.Assert.assertNotNull; 47 import static org.junit.Assert.assertNull; 48 import static org.junit.Assert.assertThrows; 49 import static org.junit.Assert.assertTrue; 50 import static org.junit.Assert.fail; 51 import static org.junit.Assume.assumeNoException; 52 import static org.junit.Assume.assumeTrue; 53 54 import com.android.apksig.ApkVerifier.Issue; 55 import com.android.apksig.apk.ApkFormatException; 56 import com.android.apksig.apk.ApkUtils; 57 import com.android.apksig.internal.apk.ApkSigningBlockUtils; 58 import com.android.apksig.internal.apk.SignatureInfo; 59 import com.android.apksig.internal.apk.stamp.SourceStampConstants; 60 import com.android.apksig.internal.apk.v1.V1SchemeVerifier; 61 import com.android.apksig.internal.apk.v2.V2SchemeConstants; 62 import com.android.apksig.internal.apk.v3.V3SchemeConstants; 63 import com.android.apksig.internal.asn1.Asn1BerParser; 64 import com.android.apksig.internal.util.AndroidSdkVersion; 65 import com.android.apksig.internal.util.Pair; 66 import com.android.apksig.internal.util.Resources; 67 import com.android.apksig.internal.x509.RSAPublicKey; 68 import com.android.apksig.internal.x509.SubjectPublicKeyInfo; 69 import com.android.apksig.internal.zip.CentralDirectoryRecord; 70 import com.android.apksig.internal.zip.LocalFileRecord; 71 // BEGIN-AOSP 72 import com.android.apksig.kms.aws.AwsSignerConfigGenerator; 73 import com.android.apksig.kms.aws.KeyAliasClient; 74 import com.android.apksig.kms.gcp.GcpSignerConfigGenerator; 75 import com.android.apksig.kms.gcp.KeyRingClient; 76 // END-AOSP 77 import com.android.apksig.util.DataSource; 78 import com.android.apksig.util.DataSources; 79 import com.android.apksig.zip.ZipFormatException; 80 81 import org.bouncycastle.jce.provider.BouncyCastleProvider; 82 import org.junit.Rule; 83 import org.junit.Test; 84 import org.junit.rules.TemporaryFolder; 85 import org.junit.runner.RunWith; 86 import org.junit.runners.JUnit4; 87 88 import java.io.File; 89 import java.io.FileInputStream; 90 import java.io.IOException; 91 import java.io.RandomAccessFile; 92 import java.math.BigInteger; 93 import java.nio.ByteBuffer; 94 import java.nio.file.Files; 95 import java.nio.file.Paths; 96 import java.security.MessageDigest; 97 import java.security.NoSuchAlgorithmException; 98 import java.security.PrivateKey; 99 import java.security.Security; 100 import java.security.Signature; 101 import java.security.SignatureException; 102 import java.security.cert.X509Certificate; 103 import java.util.ArrayList; 104 import java.util.Arrays; 105 import java.util.Collection; 106 import java.util.Collections; 107 import java.util.HashSet; 108 import java.util.List; 109 import java.util.Set; 110 import java.util.zip.ZipEntry; 111 import java.util.zip.ZipInputStream; 112 113 @RunWith(JUnit4.class) 114 public class ApkSignerTest { 115 116 /** 117 * Whether to preserve, as files, outputs of failed tests. This is useful for investigating test 118 * failures. 119 */ 120 private static final boolean KEEP_FAILING_OUTPUT_AS_FILES = false; 121 122 private static final SignerCapabilities DEFAULT_CAPABILITIES = 123 new SignerCapabilities.Builder().build(); 124 private static final SignerCapabilities NO_CAPABILITIES = new SignerCapabilities.Builder( 125 0).build(); 126 127 // These are the ID and value of an extra signature block within the APK signing block that 128 // can be preserved through the setOtherSignersSignaturesPreserved API. 129 private final int EXTRA_BLOCK_ID = 0x7e57c0de; 130 private final byte[] EXTRA_BLOCK_VALUE = {0, 1, 2, 3, 4, 5, 6, 7}; 131 132 @Rule 133 public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); 134 main(String[] params)135 public static void main(String[] params) throws Exception { 136 File outDir = (params.length > 0) ? new File(params[0]) : new File("."); 137 generateGoldenFiles(outDir); 138 } 139 generateGoldenFiles(File outDir)140 private static void generateGoldenFiles(File outDir) throws Exception { 141 System.out.println( 142 "Generating golden files " 143 + ApkSignerTest.class.getSimpleName() 144 + " into " 145 + outDir); 146 if (!outDir.exists()) { 147 if (!outDir.mkdirs()) { 148 throw new IOException("Failed to create directory: " + outDir); 149 } 150 } 151 List<ApkSigner.SignerConfig> rsa2048SignerConfig = 152 Collections.singletonList( 153 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 154 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 155 Arrays.asList( 156 rsa2048SignerConfig.get(0), 157 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 158 SigningCertificateLineage lineage = 159 Resources.toSigningCertificateLineage( 160 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 161 162 signGolden( 163 "golden-unaligned-in.apk", 164 new File(outDir, "golden-unaligned-out.apk"), 165 new ApkSigner.Builder(rsa2048SignerConfig)); 166 signGolden( 167 "golden-legacy-aligned-in.apk", 168 new File(outDir, "golden-legacy-aligned-out.apk"), 169 new ApkSigner.Builder(rsa2048SignerConfig)); 170 signGolden( 171 "golden-aligned-in.apk", 172 new File(outDir, "golden-aligned-out.apk"), 173 new ApkSigner.Builder(rsa2048SignerConfig)); 174 175 signGolden( 176 "golden-unaligned-in.apk", 177 new File(outDir, "golden-unaligned-v1-out.apk"), 178 new ApkSigner.Builder(rsa2048SignerConfig) 179 .setV1SigningEnabled(true) 180 .setV2SigningEnabled(false) 181 .setV3SigningEnabled(false) 182 .setV4SigningEnabled(false)); 183 signGolden( 184 "golden-legacy-aligned-in.apk", 185 new File(outDir, "golden-legacy-aligned-v1-out.apk"), 186 new ApkSigner.Builder(rsa2048SignerConfig) 187 .setV1SigningEnabled(true) 188 .setV2SigningEnabled(false) 189 .setV3SigningEnabled(false) 190 .setV4SigningEnabled(false)); 191 signGolden( 192 "golden-aligned-in.apk", 193 new File(outDir, "golden-aligned-v1-out.apk"), 194 new ApkSigner.Builder(rsa2048SignerConfig) 195 .setV1SigningEnabled(true) 196 .setV2SigningEnabled(false) 197 .setV3SigningEnabled(false) 198 .setV4SigningEnabled(false)); 199 200 signGolden( 201 "golden-unaligned-in.apk", 202 new File(outDir, "golden-unaligned-v2-out.apk"), 203 new ApkSigner.Builder(rsa2048SignerConfig) 204 .setV1SigningEnabled(false) 205 .setV2SigningEnabled(true) 206 .setV3SigningEnabled(false)); 207 signGolden( 208 "golden-legacy-aligned-in.apk", 209 new File(outDir, "golden-legacy-aligned-v2-out.apk"), 210 new ApkSigner.Builder(rsa2048SignerConfig) 211 .setV1SigningEnabled(false) 212 .setV2SigningEnabled(true) 213 .setV3SigningEnabled(false)); 214 signGolden( 215 "golden-aligned-in.apk", 216 new File(outDir, "golden-aligned-v2-out.apk"), 217 new ApkSigner.Builder(rsa2048SignerConfig) 218 .setV1SigningEnabled(false) 219 .setV2SigningEnabled(true) 220 .setV3SigningEnabled(false)); 221 222 signGolden( 223 "golden-unaligned-in.apk", 224 new File(outDir, "golden-unaligned-v3-out.apk"), 225 new ApkSigner.Builder(rsa2048SignerConfig) 226 .setV1SigningEnabled(false) 227 .setV2SigningEnabled(false) 228 .setV3SigningEnabled(true)); 229 signGolden( 230 "golden-legacy-aligned-in.apk", 231 new File(outDir, "golden-legacy-aligned-v3-out.apk"), 232 new ApkSigner.Builder(rsa2048SignerConfig) 233 .setV1SigningEnabled(false) 234 .setV2SigningEnabled(false) 235 .setV3SigningEnabled(true)); 236 signGolden( 237 "golden-aligned-in.apk", 238 new File(outDir, "golden-aligned-v3-out.apk"), 239 new ApkSigner.Builder(rsa2048SignerConfig) 240 .setV1SigningEnabled(false) 241 .setV2SigningEnabled(false) 242 .setV3SigningEnabled(true)); 243 244 signGolden( 245 "golden-unaligned-in.apk", 246 new File(outDir, "golden-unaligned-v3-lineage-out.apk"), 247 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 248 .setV1SigningEnabled(false) 249 .setV2SigningEnabled(false) 250 .setV3SigningEnabled(true) 251 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 252 .setSigningCertificateLineage(lineage)); 253 254 signGolden( 255 "golden-legacy-aligned-in.apk", 256 new File(outDir, "golden-legacy-aligned-v3-lineage-out.apk"), 257 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 258 .setV1SigningEnabled(false) 259 .setV2SigningEnabled(false) 260 .setV3SigningEnabled(true) 261 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 262 .setSigningCertificateLineage(lineage)); 263 signGolden( 264 "golden-aligned-in.apk", 265 new File(outDir, "golden-aligned-v3-lineage-out.apk"), 266 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 267 .setV1SigningEnabled(false) 268 .setV2SigningEnabled(false) 269 .setV3SigningEnabled(true) 270 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 271 .setSigningCertificateLineage(lineage)); 272 273 signGolden( 274 "golden-unaligned-in.apk", 275 new File(outDir, "golden-unaligned-v1v2-out.apk"), 276 new ApkSigner.Builder(rsa2048SignerConfig) 277 .setV1SigningEnabled(true) 278 .setV2SigningEnabled(true) 279 .setV3SigningEnabled(false)); 280 signGolden( 281 "golden-legacy-aligned-in.apk", 282 new File(outDir, "golden-legacy-aligned-v1v2-out.apk"), 283 new ApkSigner.Builder(rsa2048SignerConfig) 284 .setV1SigningEnabled(true) 285 .setV2SigningEnabled(true) 286 .setV3SigningEnabled(false)); 287 signGolden( 288 "golden-aligned-in.apk", 289 new File(outDir, "golden-aligned-v1v2-out.apk"), 290 new ApkSigner.Builder(rsa2048SignerConfig) 291 .setV1SigningEnabled(true) 292 .setV2SigningEnabled(true) 293 .setV3SigningEnabled(false)); 294 295 signGolden( 296 "golden-unaligned-in.apk", 297 new File(outDir, "golden-unaligned-v2v3-out.apk"), 298 new ApkSigner.Builder(rsa2048SignerConfig) 299 .setV1SigningEnabled(false) 300 .setV2SigningEnabled(true) 301 .setV3SigningEnabled(true)); 302 signGolden( 303 "golden-legacy-aligned-in.apk", 304 new File(outDir, "golden-legacy-aligned-v2v3-out.apk"), 305 new ApkSigner.Builder(rsa2048SignerConfig) 306 .setV1SigningEnabled(false) 307 .setV2SigningEnabled(true) 308 .setV3SigningEnabled(true)); 309 signGolden( 310 "golden-aligned-in.apk", 311 new File(outDir, "golden-aligned-v2v3-out.apk"), 312 new ApkSigner.Builder(rsa2048SignerConfig) 313 .setV1SigningEnabled(false) 314 .setV2SigningEnabled(true) 315 .setV3SigningEnabled(true)); 316 signGolden( 317 "golden-unaligned-in.apk", 318 new File(outDir, "golden-unaligned-v2v3-lineage-out.apk"), 319 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 320 .setV1SigningEnabled(false) 321 .setV2SigningEnabled(true) 322 .setV3SigningEnabled(true) 323 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 324 .setSigningCertificateLineage(lineage)); 325 signGolden( 326 "golden-legacy-aligned-in.apk", 327 new File(outDir, "golden-legacy-aligned-v2v3-lineage-out.apk"), 328 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 329 .setV1SigningEnabled(false) 330 .setV2SigningEnabled(true) 331 .setV3SigningEnabled(true) 332 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 333 .setSigningCertificateLineage(lineage)); 334 signGolden( 335 "golden-aligned-in.apk", 336 new File(outDir, "golden-aligned-v2v3-lineage-out.apk"), 337 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 338 .setV1SigningEnabled(false) 339 .setV2SigningEnabled(true) 340 .setV3SigningEnabled(true) 341 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 342 .setSigningCertificateLineage(lineage)); 343 344 signGolden( 345 "golden-unaligned-in.apk", 346 new File(outDir, "golden-unaligned-v1v2v3-out.apk"), 347 new ApkSigner.Builder(rsa2048SignerConfig) 348 .setV1SigningEnabled(true) 349 .setV2SigningEnabled(true) 350 .setV3SigningEnabled(true)); 351 signGolden( 352 "golden-legacy-aligned-in.apk", 353 new File(outDir, "golden-legacy-aligned-v1v2v3-out.apk"), 354 new ApkSigner.Builder(rsa2048SignerConfig) 355 .setV1SigningEnabled(true) 356 .setV2SigningEnabled(true) 357 .setV3SigningEnabled(true)); 358 signGolden( 359 "golden-aligned-in.apk", 360 new File(outDir, "golden-aligned-v1v2v3-out.apk"), 361 new ApkSigner.Builder(rsa2048SignerConfig) 362 .setV1SigningEnabled(true) 363 .setV2SigningEnabled(true) 364 .setV3SigningEnabled(true)); 365 signGolden( 366 "golden-unaligned-in.apk", 367 new File(outDir, "golden-unaligned-v1v2v3-lineage-out.apk"), 368 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 369 .setV1SigningEnabled(true) 370 .setV2SigningEnabled(true) 371 .setV3SigningEnabled(true) 372 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 373 .setSigningCertificateLineage(lineage)); 374 signGolden( 375 "golden-legacy-aligned-in.apk", 376 new File(outDir, "golden-legacy-aligned-v1v2v3-lineage-out.apk"), 377 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 378 .setV1SigningEnabled(true) 379 .setV2SigningEnabled(true) 380 .setV3SigningEnabled(true) 381 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 382 .setSigningCertificateLineage(lineage)); 383 signGolden( 384 "golden-aligned-in.apk", 385 new File(outDir, "golden-aligned-v1v2v3-lineage-out.apk"), 386 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 387 .setV1SigningEnabled(true) 388 .setV2SigningEnabled(true) 389 .setV3SigningEnabled(true) 390 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 391 .setSigningCertificateLineage(lineage)); 392 393 signGolden( 394 "original.apk", 395 new File(outDir, "golden-rsa-out.apk"), 396 new ApkSigner.Builder(rsa2048SignerConfig)); 397 signGolden( 398 "original.apk", 399 new File(outDir, "golden-rsa-minSdkVersion-1-out.apk"), 400 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(1)); 401 signGolden( 402 "original.apk", 403 new File(outDir, "golden-rsa-minSdkVersion-18-out.apk"), 404 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(18)); 405 signGolden( 406 "original.apk", 407 new File(outDir, "golden-rsa-minSdkVersion-24-out.apk"), 408 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(24)); 409 signGolden( 410 "original.apk", 411 new File(outDir, "golden-rsa-verity-out.apk"), 412 new ApkSigner.Builder(rsa2048SignerConfig) 413 .setV1SigningEnabled(true) 414 .setV2SigningEnabled(true) 415 .setV3SigningEnabled(true) 416 .setVerityEnabled(true)); 417 signGolden( 418 "original.apk", 419 new File(outDir, "golden-file-size-aligned.apk"), 420 new ApkSigner.Builder(rsa2048SignerConfig) 421 .setAlignFileSize(true)); 422 signGolden( 423 "pinsapp-unsigned.apk", 424 new File(outDir, "golden-pinsapp-signed.apk"), 425 new ApkSigner.Builder(rsa2048SignerConfig) 426 .setV1SigningEnabled(true) 427 .setV2SigningEnabled(true) 428 .setV3SigningEnabled(true) 429 .setVerityEnabled(true)); 430 } 431 signGolden( String inResourceName, File outFile, ApkSigner.Builder apkSignerBuilder)432 private static void signGolden( 433 String inResourceName, File outFile, ApkSigner.Builder apkSignerBuilder) 434 throws Exception { 435 DataSource in = 436 DataSources.asDataSource( 437 ByteBuffer.wrap(Resources.toByteArray(ApkSigner.class, inResourceName))); 438 apkSignerBuilder.setInputApk(in).setOutputApk(outFile); 439 440 File outFileIdSig = new File(outFile.getCanonicalPath() + ".idsig"); 441 apkSignerBuilder.setV4SignatureOutputFile(outFileIdSig); 442 apkSignerBuilder.setV4ErrorReportingEnabled(true); 443 444 apkSignerBuilder.build().sign(); 445 } 446 447 @Test testAlignmentPreserved_Golden()448 public void testAlignmentPreserved_Golden() throws Exception { 449 // Regression tests for preserving (mis)alignment of ZIP Local File Header data 450 // NOTE: Expected output files can be re-generated by running the "main" method. 451 452 List<ApkSigner.SignerConfig> rsa2048SignerConfig = 453 Collections.singletonList( 454 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 455 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 456 Arrays.asList( 457 rsa2048SignerConfig.get(0), 458 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 459 SigningCertificateLineage lineage = 460 Resources.toSigningCertificateLineage( 461 getClass(), LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 462 // Uncompressed entries in this input file are not aligned -- the file was created using 463 // the jar utility. temp4.txt entry was then manually added into the archive. This entry's 464 // ZIP Local File Header "extra" field declares that the entry's data must be aligned to 465 // 4 kB boundary, but the data isn't actually aligned in the file. 466 assertGolden( 467 "golden-unaligned-in.apk", 468 "golden-unaligned-out.apk", 469 new ApkSigner.Builder(rsa2048SignerConfig) 470 .setAlignmentPreserved(true)); 471 assertGolden( 472 "golden-unaligned-in.apk", 473 "golden-unaligned-v1-out.apk", 474 new ApkSigner.Builder(rsa2048SignerConfig) 475 .setV1SigningEnabled(true) 476 .setV2SigningEnabled(false) 477 .setV3SigningEnabled(false) 478 .setV4SigningEnabled(false) 479 .setAlignmentPreserved(true)); 480 assertGolden( 481 "golden-unaligned-in.apk", 482 "golden-unaligned-v2-out.apk", 483 new ApkSigner.Builder(rsa2048SignerConfig) 484 .setV1SigningEnabled(false) 485 .setV2SigningEnabled(true) 486 .setV3SigningEnabled(false) 487 .setAlignmentPreserved(true)); 488 assertGolden( 489 "golden-unaligned-in.apk", 490 "golden-unaligned-v3-out.apk", 491 new ApkSigner.Builder(rsa2048SignerConfig) 492 .setV1SigningEnabled(false) 493 .setV2SigningEnabled(false) 494 .setV3SigningEnabled(true) 495 .setAlignmentPreserved(true)); 496 assertGolden( 497 "golden-unaligned-in.apk", 498 "golden-unaligned-v3-lineage-out.apk", 499 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 500 .setV1SigningEnabled(false) 501 .setV2SigningEnabled(false) 502 .setV3SigningEnabled(true) 503 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 504 .setSigningCertificateLineage(lineage) 505 .setAlignmentPreserved(true)); 506 assertGolden( 507 "golden-unaligned-in.apk", 508 "golden-unaligned-v1v2-out.apk", 509 new ApkSigner.Builder(rsa2048SignerConfig) 510 .setV1SigningEnabled(true) 511 .setV2SigningEnabled(true) 512 .setV3SigningEnabled(false) 513 .setAlignmentPreserved(true)); 514 assertGolden( 515 "golden-unaligned-in.apk", 516 "golden-unaligned-v2v3-out.apk", 517 new ApkSigner.Builder(rsa2048SignerConfig) 518 .setV1SigningEnabled(false) 519 .setV2SigningEnabled(true) 520 .setV3SigningEnabled(true) 521 .setAlignmentPreserved(true)); 522 assertGolden( 523 "golden-unaligned-in.apk", 524 "golden-unaligned-v2v3-lineage-out.apk", 525 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 526 .setV1SigningEnabled(false) 527 .setV2SigningEnabled(true) 528 .setV3SigningEnabled(true) 529 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 530 .setSigningCertificateLineage(lineage) 531 .setAlignmentPreserved(true)); 532 assertGolden( 533 "golden-unaligned-in.apk", 534 "golden-unaligned-v1v2v3-out.apk", 535 new ApkSigner.Builder(rsa2048SignerConfig) 536 .setV1SigningEnabled(true) 537 .setV2SigningEnabled(true) 538 .setV3SigningEnabled(true) 539 .setAlignmentPreserved(true)); 540 assertGolden( 541 "golden-unaligned-in.apk", 542 "golden-unaligned-v1v2v3-lineage-out.apk", 543 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 544 .setV1SigningEnabled(true) 545 .setV2SigningEnabled(true) 546 .setV3SigningEnabled(true) 547 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 548 .setSigningCertificateLineage(lineage) 549 .setAlignmentPreserved(true)); 550 551 // Uncompressed entries in this input file are aligned by zero-padding the "extra" field, as 552 // performed by zipalign at the time of writing. This padding technique produces ZIP 553 // archives whose "extra" field are not compliant with APPNOTE.TXT. Hence, this technique 554 // was deprecated. 555 assertGolden( 556 "golden-legacy-aligned-in.apk", 557 "golden-legacy-aligned-out.apk", 558 new ApkSigner.Builder(rsa2048SignerConfig) 559 .setLibraryPageAlignmentBytes(4096) 560 .setAlignmentPreserved(true)); 561 assertGolden( 562 "golden-legacy-aligned-in.apk", 563 "golden-legacy-aligned-v1-out.apk", 564 new ApkSigner.Builder(rsa2048SignerConfig) 565 .setV1SigningEnabled(true) 566 .setV2SigningEnabled(false) 567 .setV3SigningEnabled(false) 568 .setV4SigningEnabled(false) 569 .setLibraryPageAlignmentBytes(4096) 570 .setAlignmentPreserved(true)); 571 assertGolden( 572 "golden-legacy-aligned-in.apk", 573 "golden-legacy-aligned-v2-out.apk", 574 new ApkSigner.Builder(rsa2048SignerConfig) 575 .setV1SigningEnabled(false) 576 .setV2SigningEnabled(true) 577 .setV3SigningEnabled(false) 578 .setLibraryPageAlignmentBytes(4096) 579 .setAlignmentPreserved(true)); 580 assertGolden( 581 "golden-legacy-aligned-in.apk", 582 "golden-legacy-aligned-v3-out.apk", 583 new ApkSigner.Builder(rsa2048SignerConfig) 584 .setV1SigningEnabled(false) 585 .setV2SigningEnabled(false) 586 .setV3SigningEnabled(true) 587 .setLibraryPageAlignmentBytes(4096) 588 .setAlignmentPreserved(true)); 589 assertGolden( 590 "golden-legacy-aligned-in.apk", 591 "golden-legacy-aligned-v3-lineage-out.apk", 592 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 593 .setV1SigningEnabled(false) 594 .setV2SigningEnabled(false) 595 .setV3SigningEnabled(true) 596 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 597 .setSigningCertificateLineage(lineage) 598 .setLibraryPageAlignmentBytes(4096) 599 .setAlignmentPreserved(true)); 600 assertGolden( 601 "golden-legacy-aligned-in.apk", 602 "golden-legacy-aligned-v1v2-out.apk", 603 new ApkSigner.Builder(rsa2048SignerConfig) 604 .setV1SigningEnabled(true) 605 .setV2SigningEnabled(true) 606 .setV3SigningEnabled(false) 607 .setLibraryPageAlignmentBytes(4096) 608 .setAlignmentPreserved(true)); 609 assertGolden( 610 "golden-legacy-aligned-in.apk", 611 "golden-legacy-aligned-v2v3-out.apk", 612 new ApkSigner.Builder(rsa2048SignerConfig) 613 .setV1SigningEnabled(false) 614 .setV2SigningEnabled(true) 615 .setV3SigningEnabled(true) 616 .setLibraryPageAlignmentBytes(4096) 617 .setAlignmentPreserved(true)); 618 assertGolden( 619 "golden-legacy-aligned-in.apk", 620 "golden-legacy-aligned-v2v3-lineage-out.apk", 621 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 622 .setV1SigningEnabled(false) 623 .setV2SigningEnabled(true) 624 .setV3SigningEnabled(true) 625 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 626 .setSigningCertificateLineage(lineage) 627 .setLibraryPageAlignmentBytes(4096) 628 .setAlignmentPreserved(true)); 629 assertGolden( 630 "golden-legacy-aligned-in.apk", 631 "golden-legacy-aligned-v1v2v3-out.apk", 632 new ApkSigner.Builder(rsa2048SignerConfig) 633 .setV1SigningEnabled(true) 634 .setV2SigningEnabled(true) 635 .setV3SigningEnabled(true) 636 .setLibraryPageAlignmentBytes(4096) 637 .setAlignmentPreserved(true)); 638 assertGolden( 639 "golden-legacy-aligned-in.apk", 640 "golden-legacy-aligned-v1v2v3-lineage-out.apk", 641 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 642 .setV1SigningEnabled(true) 643 .setV2SigningEnabled(true) 644 .setV3SigningEnabled(true) 645 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 646 .setSigningCertificateLineage(lineage) 647 .setLibraryPageAlignmentBytes(4096) 648 .setAlignmentPreserved(true)); 649 650 // Uncompressed entries in this input file are aligned by padding the "extra" field, as 651 // generated by signapk and apksigner. This padding technique produces "extra" fields which 652 // are compliant with APPNOTE.TXT. 653 assertGolden( 654 "golden-aligned-in.apk", 655 "golden-aligned-out.apk", 656 new ApkSigner.Builder(rsa2048SignerConfig) 657 .setAlignmentPreserved(true)); 658 assertGolden( 659 "golden-aligned-in.apk", 660 "golden-aligned-v1-out.apk", 661 new ApkSigner.Builder(rsa2048SignerConfig) 662 .setV1SigningEnabled(true) 663 .setV2SigningEnabled(false) 664 .setV3SigningEnabled(false) 665 .setV4SigningEnabled(false) 666 .setAlignmentPreserved(true)); 667 assertGolden( 668 "golden-aligned-in.apk", 669 "golden-aligned-v2-out.apk", 670 new ApkSigner.Builder(rsa2048SignerConfig) 671 .setV1SigningEnabled(false) 672 .setV2SigningEnabled(true) 673 .setV3SigningEnabled(false) 674 .setAlignmentPreserved(true)); 675 assertGolden( 676 "golden-aligned-in.apk", 677 "golden-aligned-v3-out.apk", 678 new ApkSigner.Builder(rsa2048SignerConfig) 679 .setV1SigningEnabled(false) 680 .setV2SigningEnabled(false) 681 .setV3SigningEnabled(true) 682 .setAlignmentPreserved(true)); 683 assertGolden( 684 "golden-aligned-in.apk", 685 "golden-aligned-v3-lineage-out.apk", 686 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 687 .setV1SigningEnabled(false) 688 .setV2SigningEnabled(false) 689 .setV3SigningEnabled(true) 690 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 691 .setSigningCertificateLineage(lineage) 692 .setAlignmentPreserved(true)); 693 assertGolden( 694 "golden-aligned-in.apk", 695 "golden-aligned-v1v2-out.apk", 696 new ApkSigner.Builder(rsa2048SignerConfig) 697 .setV1SigningEnabled(true) 698 .setV2SigningEnabled(true) 699 .setV3SigningEnabled(false) 700 .setAlignmentPreserved(true)); 701 assertGolden( 702 "golden-aligned-in.apk", 703 "golden-aligned-v2v3-out.apk", 704 new ApkSigner.Builder(rsa2048SignerConfig) 705 .setV1SigningEnabled(false) 706 .setV2SigningEnabled(true) 707 .setV3SigningEnabled(true) 708 .setAlignmentPreserved(true)); 709 assertGolden( 710 "golden-aligned-in.apk", 711 "golden-aligned-v2v3-lineage-out.apk", 712 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 713 .setV1SigningEnabled(false) 714 .setV2SigningEnabled(true) 715 .setV3SigningEnabled(true) 716 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 717 .setSigningCertificateLineage(lineage) 718 .setAlignmentPreserved(true)); 719 assertGolden( 720 "golden-aligned-in.apk", 721 "golden-aligned-v1v2v3-out.apk", 722 new ApkSigner.Builder(rsa2048SignerConfig) 723 .setV1SigningEnabled(true) 724 .setV2SigningEnabled(true) 725 .setV3SigningEnabled(true) 726 .setAlignmentPreserved(true)); 727 assertGolden( 728 "golden-aligned-in.apk", 729 "golden-aligned-v1v2v3-lineage-out.apk", 730 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 731 .setV1SigningEnabled(true) 732 .setV2SigningEnabled(true) 733 .setV3SigningEnabled(true) 734 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 735 .setSigningCertificateLineage(lineage) 736 .setAlignmentPreserved(true)); 737 } 738 739 // BEGIN-AOSP 740 @Test testAws_Golden()741 public void testAws_Golden() throws Exception { 742 try (KeyAliasClient client = new KeyAliasClient()) { 743 client.listKeyAliases(); 744 } catch (Exception e) { 745 assumeNoException("Test cannot run without access to test data in AWS", e); 746 } 747 final List<ApkSigner.SignerConfig> rsa2048SignerConfig = 748 Collections.singletonList( 749 AwsSignerConfigGenerator.getSignerConfigFromResources( 750 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, false)); 751 752 assertGolden( 753 "golden-aligned-in.apk", 754 "golden-aligned-out.apk", 755 new ApkSigner.Builder(rsa2048SignerConfig).setAlignmentPreserved(true)); 756 assertGolden( 757 "golden-aligned-in.apk", 758 "golden-aligned-v1-out.apk", 759 new ApkSigner.Builder(rsa2048SignerConfig) 760 .setV1SigningEnabled(true) 761 .setV2SigningEnabled(false) 762 .setV3SigningEnabled(false) 763 .setV4SigningEnabled(false) 764 .setAlignmentPreserved(true)); 765 assertGolden( 766 "golden-aligned-in.apk", 767 "golden-aligned-v2-out.apk", 768 new ApkSigner.Builder(rsa2048SignerConfig) 769 .setV1SigningEnabled(false) 770 .setV2SigningEnabled(true) 771 .setV3SigningEnabled(false) 772 .setAlignmentPreserved(true)); 773 assertGolden( 774 "golden-aligned-in.apk", 775 "golden-aligned-v3-out.apk", 776 new ApkSigner.Builder(rsa2048SignerConfig) 777 .setV1SigningEnabled(false) 778 .setV2SigningEnabled(false) 779 .setV3SigningEnabled(true) 780 .setAlignmentPreserved(true)); 781 assertGolden( 782 "golden-aligned-in.apk", 783 "golden-aligned-v1v2-out.apk", 784 new ApkSigner.Builder(rsa2048SignerConfig) 785 .setV1SigningEnabled(true) 786 .setV2SigningEnabled(true) 787 .setV3SigningEnabled(false) 788 .setAlignmentPreserved(true)); 789 assertGolden( 790 "golden-aligned-in.apk", 791 "golden-aligned-v2v3-out.apk", 792 new ApkSigner.Builder(rsa2048SignerConfig) 793 .setV1SigningEnabled(false) 794 .setV2SigningEnabled(true) 795 .setV3SigningEnabled(true) 796 .setAlignmentPreserved(true)); 797 assertGolden( 798 "golden-aligned-in.apk", 799 "golden-aligned-v1v2v3-out.apk", 800 new ApkSigner.Builder(rsa2048SignerConfig) 801 .setV1SigningEnabled(true) 802 .setV2SigningEnabled(true) 803 .setV3SigningEnabled(true) 804 .setAlignmentPreserved(true)); 805 806 // In the first set of lineage tests, the lineage starts with an AWS KMS key config, and 807 // then 808 // rotates to JCA. 809 final List<ApkSigner.SignerConfig> rsa2048SignerConfigWithAwsAtStartOfLineage = 810 Arrays.asList( 811 rsa2048SignerConfig.get(0), 812 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 813 final SigningCertificateLineage lineage = 814 Resources.toSigningCertificateLineage( 815 getClass(), LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 816 817 assertGolden( 818 "golden-aligned-in.apk", 819 "golden-aligned-v3-lineage-out.apk", 820 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtStartOfLineage) 821 .setV1SigningEnabled(false) 822 .setV2SigningEnabled(false) 823 .setV3SigningEnabled(true) 824 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 825 .setSigningCertificateLineage(lineage) 826 .setAlignmentPreserved(true)); 827 assertGolden( 828 "golden-aligned-in.apk", 829 "golden-aligned-v2v3-lineage-out.apk", 830 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtStartOfLineage) 831 .setV1SigningEnabled(false) 832 .setV2SigningEnabled(true) 833 .setV3SigningEnabled(true) 834 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 835 .setSigningCertificateLineage(lineage) 836 .setAlignmentPreserved(true)); 837 assertGolden( 838 "golden-aligned-in.apk", 839 "golden-aligned-v1v2v3-lineage-out.apk", 840 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtStartOfLineage) 841 .setV1SigningEnabled(true) 842 .setV2SigningEnabled(true) 843 .setV3SigningEnabled(true) 844 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 845 .setSigningCertificateLineage(lineage) 846 .setAlignmentPreserved(true)); 847 848 // In the this set of lineage tests, the lineage starts with a local JCA key config, and 849 // then rotates to AWS KMS. 850 final List<ApkSigner.SignerConfig> rsa2048SignerConfigWithAwsAtEndOfLineage = 851 Arrays.asList( 852 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 853 AwsSignerConfigGenerator.getSignerConfigFromResources( 854 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false)); 855 856 assertGolden( 857 "golden-aligned-in.apk", 858 "golden-aligned-v3-lineage-out.apk", 859 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtEndOfLineage) 860 .setV1SigningEnabled(false) 861 .setV2SigningEnabled(false) 862 .setV3SigningEnabled(true) 863 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 864 .setSigningCertificateLineage(lineage) 865 .setAlignmentPreserved(true)); 866 867 assertGolden( 868 "golden-aligned-in.apk", 869 "golden-aligned-v2v3-lineage-out.apk", 870 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtEndOfLineage) 871 .setV1SigningEnabled(false) 872 .setV2SigningEnabled(true) 873 .setV3SigningEnabled(true) 874 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 875 .setSigningCertificateLineage(lineage) 876 .setAlignmentPreserved(true)); 877 878 assertGolden( 879 "golden-aligned-in.apk", 880 "golden-aligned-v1v2v3-lineage-out.apk", 881 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtEndOfLineage) 882 .setV1SigningEnabled(true) 883 .setV2SigningEnabled(true) 884 .setV3SigningEnabled(true) 885 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 886 .setSigningCertificateLineage(lineage) 887 .setAlignmentPreserved(true)); 888 } 889 // END-AOSP 890 891 // BEGIN-AOSP 892 @Test testGcp_Golden()893 public void testGcp_Golden() throws Exception { 894 try (KeyRingClient keyRingClient = new KeyRingClient(TEST_GCP_KEY_RING)) { 895 keyRingClient.getKeyRing(); 896 } catch (Exception e) { 897 assumeNoException("Test cannot run without access to test data in GCP", e); 898 } 899 final List<ApkSigner.SignerConfig> rsa2048SignerConfig = 900 Collections.singletonList( 901 GcpSignerConfigGenerator.getSignerConfigFromResources( 902 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, false)); 903 904 assertGolden( 905 "golden-aligned-in.apk", 906 "golden-aligned-out.apk", 907 new ApkSigner.Builder(rsa2048SignerConfig).setAlignmentPreserved(true)); 908 assertGolden( 909 "golden-aligned-in.apk", 910 "golden-aligned-v1-out.apk", 911 new ApkSigner.Builder(rsa2048SignerConfig) 912 .setV1SigningEnabled(true) 913 .setV2SigningEnabled(false) 914 .setV3SigningEnabled(false) 915 .setV4SigningEnabled(false) 916 .setAlignmentPreserved(true)); 917 assertGolden( 918 "golden-aligned-in.apk", 919 "golden-aligned-v2-out.apk", 920 new ApkSigner.Builder(rsa2048SignerConfig) 921 .setV1SigningEnabled(false) 922 .setV2SigningEnabled(true) 923 .setV3SigningEnabled(false) 924 .setAlignmentPreserved(true)); 925 assertGolden( 926 "golden-aligned-in.apk", 927 "golden-aligned-v3-out.apk", 928 new ApkSigner.Builder(rsa2048SignerConfig) 929 .setV1SigningEnabled(false) 930 .setV2SigningEnabled(false) 931 .setV3SigningEnabled(true) 932 .setAlignmentPreserved(true)); 933 assertGolden( 934 "golden-aligned-in.apk", 935 "golden-aligned-v1v2-out.apk", 936 new ApkSigner.Builder(rsa2048SignerConfig) 937 .setV1SigningEnabled(true) 938 .setV2SigningEnabled(true) 939 .setV3SigningEnabled(false) 940 .setAlignmentPreserved(true)); 941 assertGolden( 942 "golden-aligned-in.apk", 943 "golden-aligned-v2v3-out.apk", 944 new ApkSigner.Builder(rsa2048SignerConfig) 945 .setV1SigningEnabled(false) 946 .setV2SigningEnabled(true) 947 .setV3SigningEnabled(true) 948 .setAlignmentPreserved(true)); 949 assertGolden( 950 "golden-aligned-in.apk", 951 "golden-aligned-v1v2v3-out.apk", 952 new ApkSigner.Builder(rsa2048SignerConfig) 953 .setV1SigningEnabled(true) 954 .setV2SigningEnabled(true) 955 .setV3SigningEnabled(true) 956 .setAlignmentPreserved(true)); 957 958 // In the first set of lineage tests, the lineage starts with a GCP KMS key config, and then 959 // rotates to JCA. 960 final List<ApkSigner.SignerConfig> rsa2048SignerConfigWithGcpAtStartOfLineage = 961 Arrays.asList( 962 rsa2048SignerConfig.get(0), 963 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 964 final SigningCertificateLineage lineage = 965 Resources.toSigningCertificateLineage( 966 getClass(), LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 967 968 assertGolden( 969 "golden-aligned-in.apk", 970 "golden-aligned-v3-lineage-out.apk", 971 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtStartOfLineage) 972 .setV1SigningEnabled(false) 973 .setV2SigningEnabled(false) 974 .setV3SigningEnabled(true) 975 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 976 .setSigningCertificateLineage(lineage) 977 .setAlignmentPreserved(true)); 978 assertGolden( 979 "golden-aligned-in.apk", 980 "golden-aligned-v2v3-lineage-out.apk", 981 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtStartOfLineage) 982 .setV1SigningEnabled(false) 983 .setV2SigningEnabled(true) 984 .setV3SigningEnabled(true) 985 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 986 .setSigningCertificateLineage(lineage) 987 .setAlignmentPreserved(true)); 988 assertGolden( 989 "golden-aligned-in.apk", 990 "golden-aligned-v1v2v3-lineage-out.apk", 991 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtStartOfLineage) 992 .setV1SigningEnabled(true) 993 .setV2SigningEnabled(true) 994 .setV3SigningEnabled(true) 995 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 996 .setSigningCertificateLineage(lineage) 997 .setAlignmentPreserved(true)); 998 999 // In the this set of lineage tests, the lineage starts with a local JCA key config, and 1000 // then rotates to GCP KMS. 1001 final List<ApkSigner.SignerConfig> rsa2048SignerConfigWithGcpAtEndOfLineage = 1002 Arrays.asList( 1003 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 1004 GcpSignerConfigGenerator.getSignerConfigFromResources( 1005 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false)); 1006 1007 assertGolden( 1008 "golden-aligned-in.apk", 1009 "golden-aligned-v3-lineage-out.apk", 1010 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtEndOfLineage) 1011 .setV1SigningEnabled(false) 1012 .setV2SigningEnabled(false) 1013 .setV3SigningEnabled(true) 1014 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 1015 .setSigningCertificateLineage(lineage) 1016 .setAlignmentPreserved(true)); 1017 1018 assertGolden( 1019 "golden-aligned-in.apk", 1020 "golden-aligned-v2v3-lineage-out.apk", 1021 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtEndOfLineage) 1022 .setV1SigningEnabled(false) 1023 .setV2SigningEnabled(true) 1024 .setV3SigningEnabled(true) 1025 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 1026 .setSigningCertificateLineage(lineage) 1027 .setAlignmentPreserved(true)); 1028 1029 assertGolden( 1030 "golden-aligned-in.apk", 1031 "golden-aligned-v1v2v3-lineage-out.apk", 1032 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtEndOfLineage) 1033 .setV1SigningEnabled(true) 1034 .setV2SigningEnabled(true) 1035 .setV3SigningEnabled(true) 1036 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 1037 .setSigningCertificateLineage(lineage) 1038 .setAlignmentPreserved(true)); 1039 } 1040 // END-AOSP 1041 1042 @Test testMinSdkVersion_Golden()1043 public void testMinSdkVersion_Golden() throws Exception { 1044 // Regression tests for minSdkVersion-based signature/digest algorithm selection 1045 // NOTE: Expected output files can be re-generated by running the "main" method. 1046 1047 List<ApkSigner.SignerConfig> rsaSignerConfig = 1048 Collections.singletonList( 1049 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1050 assertGolden( 1051 "original.apk", 1052 "golden-rsa-out.apk", 1053 new ApkSigner.Builder(rsaSignerConfig).setAlignmentPreserved(true)); 1054 assertGolden( 1055 "original.apk", 1056 "golden-rsa-minSdkVersion-1-out.apk", 1057 new ApkSigner.Builder(rsaSignerConfig) 1058 .setMinSdkVersion(1) 1059 .setAlignmentPreserved(true)); 1060 assertGolden( 1061 "original.apk", 1062 "golden-rsa-minSdkVersion-18-out.apk", 1063 new ApkSigner.Builder(rsaSignerConfig) 1064 .setMinSdkVersion(18) 1065 .setAlignmentPreserved(true)); 1066 assertGolden( 1067 "original.apk", 1068 "golden-rsa-minSdkVersion-24-out.apk", 1069 new ApkSigner.Builder(rsaSignerConfig).setMinSdkVersion(24) 1070 .setAlignmentPreserved(true)); 1071 1072 // TODO: Add tests for DSA and ECDSA. This is non-trivial because the default 1073 // implementations of these signature algorithms are non-deterministic which means output 1074 // files always differ from golden files. 1075 } 1076 1077 @Test testVerityEnabled_Golden()1078 public void testVerityEnabled_Golden() throws Exception { 1079 List<ApkSigner.SignerConfig> rsaSignerConfig = 1080 Collections.singletonList( 1081 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1082 1083 assertGolden( 1084 "original.apk", 1085 "golden-rsa-verity-out.apk", 1086 new ApkSigner.Builder(rsaSignerConfig) 1087 .setV1SigningEnabled(true) 1088 .setV2SigningEnabled(true) 1089 .setV3SigningEnabled(true) 1090 .setVerityEnabled(true) 1091 .setAlignmentPreserved(true)); 1092 } 1093 1094 @Test testAlignFileSize_Golden()1095 public void testAlignFileSize_Golden() throws Exception { 1096 List<ApkSigner.SignerConfig> rsaSignerConfig = 1097 Collections.singletonList( 1098 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1099 String goldenOutput = "golden-file-size-aligned.apk"; 1100 assertGolden( 1101 "original.apk", 1102 goldenOutput, 1103 new ApkSigner.Builder(rsaSignerConfig) 1104 .setAlignFileSize(true) 1105 .setAlignmentPreserved(true)); 1106 assertTrue(Resources.toByteArray(getClass(), goldenOutput).length % 4096 == 0); 1107 } 1108 1109 @Test testRsaSignedVerifies()1110 public void testRsaSignedVerifies() throws Exception { 1111 List<ApkSigner.SignerConfig> signers = 1112 Collections.singletonList( 1113 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1114 String in = "original.apk"; 1115 1116 // Sign so that the APK is guaranteed to verify on API Level 1+ 1117 File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1)); 1118 assertVerified(verifyForMinSdkVersion(out, 1)); 1119 1120 // Sign so that the APK is guaranteed to verify on API Level 18+ 1121 out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(18)); 1122 assertVerified(verifyForMinSdkVersion(out, 18)); 1123 // Does not verify on API Level 17 because RSA with SHA-256 not supported 1124 assertVerificationFailure( 1125 verifyForMinSdkVersion(out, 17), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG); 1126 } 1127 1128 @Test testDsaSignedVerifies()1129 public void testDsaSignedVerifies() throws Exception { 1130 List<ApkSigner.SignerConfig> signers = 1131 Collections.singletonList(getDefaultSignerConfigFromResources("dsa-1024")); 1132 String in = "original.apk"; 1133 1134 // Sign so that the APK is guaranteed to verify on API Level 1+ 1135 File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1)); 1136 assertVerified(verifyForMinSdkVersion(out, 1)); 1137 1138 // Sign so that the APK is guaranteed to verify on API Level 21+ 1139 out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(21)); 1140 assertVerified(verifyForMinSdkVersion(out, 21)); 1141 // Does not verify on API Level 20 because DSA with SHA-256 not supported 1142 assertVerificationFailure( 1143 verifyForMinSdkVersion(out, 20), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG); 1144 } 1145 1146 @Test testDeterministicDsaSignedVerifies()1147 public void testDeterministicDsaSignedVerifies() throws Exception { 1148 Security.addProvider(new BouncyCastleProvider()); 1149 try { 1150 // TODO(b/319494004) see if external/bouncycastle can support this algorithm 1151 assumeSHA1withDetDSAIsSupported(); 1152 List<ApkSigner.SignerConfig> signers = 1153 Collections.singletonList( 1154 getDeterministicDsaSignerConfigFromResources("dsa-2048")); 1155 String in = "original.apk"; 1156 1157 // Sign so that the APK is guaranteed to verify on API Level 1+ 1158 File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1)); 1159 assertVerified(verifyForMinSdkVersion(out, 1)); 1160 1161 // Sign so that the APK is guaranteed to verify on API Level 21+ 1162 out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(21)); 1163 assertVerified(verifyForMinSdkVersion(out, 21)); 1164 // Does not verify on API Level 20 because DSA with SHA-256 not supported 1165 assertVerificationFailure( 1166 verifyForMinSdkVersion(out, 20), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG); 1167 } finally { 1168 Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); 1169 } 1170 } 1171 1172 @Test testDeterministicDsaSigningIsDeterministic()1173 public void testDeterministicDsaSigningIsDeterministic() throws Exception { 1174 Security.addProvider(new BouncyCastleProvider()); 1175 try { 1176 // TODO(b/319494004) see if external/bouncycastle can support this algorithm 1177 assumeSHA1withDetDSAIsSupported(); 1178 List<ApkSigner.SignerConfig> signers = 1179 Collections.singletonList( 1180 getDeterministicDsaSignerConfigFromResources("dsa-2048")); 1181 String in = "original.apk"; 1182 1183 ApkSigner.Builder apkSignerBuilder = new ApkSigner.Builder(signers).setMinSdkVersion(1); 1184 File first = sign(in, apkSignerBuilder); 1185 File second = sign(in, apkSignerBuilder); 1186 1187 assertFileContentsEqual(first, second); 1188 } finally { 1189 Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); 1190 } 1191 } 1192 assumeSHA1withDetDSAIsSupported()1193 private void assumeSHA1withDetDSAIsSupported() { 1194 try { 1195 Signature.getInstance("SHA1withDetDSA"); 1196 } catch (NoSuchAlgorithmException e) { 1197 assumeNoException( 1198 "We should be running with a provider that supports SHA1withDetDSA", 1199 e); 1200 } 1201 } 1202 1203 @Test testEcSignedVerifies()1204 public void testEcSignedVerifies() throws Exception { 1205 List<ApkSigner.SignerConfig> signers = 1206 Collections.singletonList( 1207 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); 1208 String in = "original.apk"; 1209 1210 // NOTE: EC APK signatures are not supported prior to API Level 18 1211 // Sign so that the APK is guaranteed to verify on API Level 18+ 1212 File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(18)); 1213 assertVerified(verifyForMinSdkVersion(out, 18)); 1214 // Does not verify on API Level 17 because EC not supported 1215 assertVerificationFailure( 1216 verifyForMinSdkVersion(out, 17), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG); 1217 } 1218 1219 @Test testV1SigningRejectsInvalidZipEntryNames()1220 public void testV1SigningRejectsInvalidZipEntryNames() throws Exception { 1221 // ZIP/JAR entry name cannot contain CR, LF, or NUL characters when the APK is being 1222 // JAR-signed. 1223 List<ApkSigner.SignerConfig> signers = 1224 Collections.singletonList( 1225 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1226 1227 assertThrows( 1228 ApkFormatException.class, 1229 () -> 1230 sign( 1231 "v1-only-with-cr-in-entry-name.apk", 1232 new ApkSigner.Builder(signers).setV1SigningEnabled(true))); 1233 assertThrows( 1234 ApkFormatException.class, 1235 () -> 1236 sign( 1237 "v1-only-with-lf-in-entry-name.apk", 1238 new ApkSigner.Builder(signers).setV1SigningEnabled(true))); 1239 assertThrows( 1240 ApkFormatException.class, 1241 () -> 1242 sign( 1243 "v1-only-with-nul-in-entry-name.apk", 1244 new ApkSigner.Builder(signers).setV1SigningEnabled(true))); 1245 } 1246 1247 @Test testV1SigningAllowedWithMaximumNumberOfSigners()1248 public void testV1SigningAllowedWithMaximumNumberOfSigners() throws Exception { 1249 // The APK Signature Scheme v1 supports a maximum of 10 signers; this test verifies a 1250 // signing config with the maximum number of signers is allowed to sign the APK. 1251 List<ApkSigner.SignerConfig> signers = List.of( 1252 getDefaultSignerConfigFromResources("dsa-1024"), 1253 getDefaultSignerConfigFromResources("dsa-2048"), 1254 getDefaultSignerConfigFromResources("dsa-3072"), 1255 getDefaultSignerConfigFromResources("rsa-1024"), 1256 getDefaultSignerConfigFromResources("rsa-2048"), 1257 getDefaultSignerConfigFromResources("rsa-3072"), 1258 getDefaultSignerConfigFromResources("rsa-4096"), 1259 getDefaultSignerConfigFromResources("rsa-8192"), 1260 getDefaultSignerConfigFromResources("ec-p256"), 1261 getDefaultSignerConfigFromResources("ec-p384") 1262 ); 1263 sign("original.apk", 1264 new ApkSigner.Builder(signers) 1265 .setV1SigningEnabled(true) 1266 .setV2SigningEnabled(false) 1267 .setV3SigningEnabled(false) 1268 .setV4SigningEnabled(false)); 1269 } 1270 1271 @Test testV1SigningRejectedWithMoreThanMaximumNumberOfSigners()1272 public void testV1SigningRejectedWithMoreThanMaximumNumberOfSigners() throws Exception { 1273 // This test ensures a v1 signing config with more than the maximum supported number 1274 // of signers will fail to sign. 1275 List<ApkSigner.SignerConfig> signers = List.of( 1276 getDefaultSignerConfigFromResources("dsa-1024"), 1277 getDefaultSignerConfigFromResources("dsa-2048"), 1278 getDefaultSignerConfigFromResources("dsa-3072"), 1279 getDefaultSignerConfigFromResources("rsa-1024"), 1280 getDefaultSignerConfigFromResources("rsa-2048"), 1281 getDefaultSignerConfigFromResources("rsa-3072"), 1282 getDefaultSignerConfigFromResources("rsa-4096"), 1283 getDefaultSignerConfigFromResources("rsa-8192"), 1284 getDefaultSignerConfigFromResources("ec-p256"), 1285 getDefaultSignerConfigFromResources("ec-p384"), 1286 getDefaultSignerConfigFromResources("ec-p521") 1287 ); 1288 assertThrows(IllegalArgumentException.class, () -> 1289 sign("original.apk", 1290 new ApkSigner.Builder(signers) 1291 .setV1SigningEnabled(true) 1292 .setV2SigningEnabled(false) 1293 .setV3SigningEnabled(false) 1294 .setV4SigningEnabled(false))); 1295 } 1296 1297 @Test testV2SigningAllowedWithMaximumNumberOfSigners()1298 public void testV2SigningAllowedWithMaximumNumberOfSigners() throws Exception { 1299 // The APK Signature Scheme v2 supports a maximum of 10 signers; this test verifies a 1300 // signing config with the maximum number of signers is allowed to sign the APK. 1301 List<ApkSigner.SignerConfig> signers = List.of( 1302 getDefaultSignerConfigFromResources("dsa-1024"), 1303 getDefaultSignerConfigFromResources("dsa-2048"), 1304 getDefaultSignerConfigFromResources("dsa-3072"), 1305 getDefaultSignerConfigFromResources("rsa-1024"), 1306 getDefaultSignerConfigFromResources("rsa-2048"), 1307 getDefaultSignerConfigFromResources("rsa-3072"), 1308 getDefaultSignerConfigFromResources("rsa-4096"), 1309 getDefaultSignerConfigFromResources("rsa-8192"), 1310 getDefaultSignerConfigFromResources("ec-p256"), 1311 getDefaultSignerConfigFromResources("ec-p384") 1312 ); 1313 sign("original.apk", 1314 new ApkSigner.Builder(signers) 1315 .setV1SigningEnabled(false) 1316 .setV2SigningEnabled(true) 1317 .setV3SigningEnabled(false) 1318 .setV4SigningEnabled(false)); 1319 } 1320 1321 @Test testV2SigningRejectedWithMoreThanMaximumNumberOfSigners()1322 public void testV2SigningRejectedWithMoreThanMaximumNumberOfSigners() throws Exception { 1323 // This test ensures a v2 signing config with more than the maximum supported number 1324 // of signers will fail to sign. 1325 List<ApkSigner.SignerConfig> signers = List.of( 1326 getDefaultSignerConfigFromResources("dsa-1024"), 1327 getDefaultSignerConfigFromResources("dsa-2048"), 1328 getDefaultSignerConfigFromResources("dsa-3072"), 1329 getDefaultSignerConfigFromResources("rsa-1024"), 1330 getDefaultSignerConfigFromResources("rsa-2048"), 1331 getDefaultSignerConfigFromResources("rsa-3072"), 1332 getDefaultSignerConfigFromResources("rsa-4096"), 1333 getDefaultSignerConfigFromResources("rsa-8192"), 1334 getDefaultSignerConfigFromResources("ec-p256"), 1335 getDefaultSignerConfigFromResources("ec-p384"), 1336 getDefaultSignerConfigFromResources("ec-p521") 1337 ); 1338 assertThrows(IllegalArgumentException.class, () -> 1339 sign("original.apk", 1340 new ApkSigner.Builder(signers) 1341 .setV1SigningEnabled(false) 1342 .setV2SigningEnabled(true) 1343 .setV3SigningEnabled(false) 1344 .setV4SigningEnabled(false))); 1345 } 1346 1347 @Test testWeirdZipCompressionMethod()1348 public void testWeirdZipCompressionMethod() throws Exception { 1349 // Any ZIP compression method other than STORED is treated as DEFLATED by Android. 1350 // This APK declares compression method 21 (neither STORED nor DEFLATED) for CERT.RSA entry, 1351 // but the entry is actually Deflate-compressed. 1352 List<ApkSigner.SignerConfig> signers = 1353 Collections.singletonList( 1354 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1355 sign("weird-compression-method.apk", new ApkSigner.Builder(signers)); 1356 } 1357 1358 @Test testZipCompressionMethodMismatchBetweenLfhAndCd()1359 public void testZipCompressionMethodMismatchBetweenLfhAndCd() throws Exception { 1360 // Android Package Manager ignores compressionMethod field in Local File Header and always 1361 // uses the compressionMethod from Central Directory instead. 1362 // In this APK, compression method of CERT.RSA is declared as STORED in Local File Header 1363 // and as DEFLATED in Central Directory. The entry is actually Deflate-compressed. 1364 List<ApkSigner.SignerConfig> signers = 1365 Collections.singletonList( 1366 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1367 sign("mismatched-compression-method.apk", new ApkSigner.Builder(signers)); 1368 } 1369 1370 @Test testDebuggableApk()1371 public void testDebuggableApk() throws Exception { 1372 // APK which uses a boolean value "true" in its android:debuggable 1373 final String debuggableBooleanApk = "debuggable-boolean.apk"; 1374 List<ApkSigner.SignerConfig> signers = 1375 Collections.singletonList( 1376 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1377 // Signing debuggable APKs is permitted by default 1378 sign(debuggableBooleanApk, new ApkSigner.Builder(signers)); 1379 // Signing debuggable APK succeeds when explicitly requested 1380 sign(debuggableBooleanApk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(true)); 1381 1382 // Signing debuggable APK fails when requested 1383 assertThrows( 1384 SignatureException.class, 1385 () -> 1386 sign( 1387 debuggableBooleanApk, 1388 new ApkSigner.Builder(signers).setDebuggableApkPermitted(false))); 1389 1390 // APK which uses a reference value, pointing to boolean "false", in its android:debuggable 1391 final String debuggableResourceApk = "debuggable-resource.apk"; 1392 // When we permit signing regardless of whether the APK is debuggable, the value of 1393 // android:debuggable should be ignored. 1394 sign(debuggableResourceApk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(true)); 1395 1396 // When we disallow signing debuggable APKs, APKs with android:debuggable being a resource 1397 // reference must be rejected, because there's no easy way to establish whether the resolved 1398 // boolean value is the same for all resource configurations. 1399 assertThrows( 1400 SignatureException.class, 1401 () -> 1402 sign( 1403 debuggableResourceApk, 1404 new ApkSigner.Builder(signers).setDebuggableApkPermitted(false))); 1405 } 1406 1407 @Test testV3SigningWithSignersNotInLineageFails()1408 public void testV3SigningWithSignersNotInLineageFails() throws Exception { 1409 // APKs signed with the v3 scheme after a key rotation must specify the lineage containing 1410 // the proof of rotation. This test verifies that the signing will fail if the provided 1411 // signers are not in the specified lineage. 1412 List<ApkSigner.SignerConfig> signers = 1413 Arrays.asList( 1414 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 1415 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 1416 SigningCertificateLineage lineage = 1417 Resources.toSigningCertificateLineage(getClass(), "rsa-1024-lineage-2-signers"); 1418 assertThrows( 1419 IllegalStateException.class, 1420 () -> 1421 sign( 1422 "original.apk", 1423 new ApkSigner.Builder(signers) 1424 .setSigningCertificateLineage(lineage))); 1425 } 1426 1427 @Test testSigningWithLineageRequiresOldestSignerForV1AndV2()1428 public void testSigningWithLineageRequiresOldestSignerForV1AndV2() throws Exception { 1429 // After a key rotation the oldest signer must still be specified for v1 and v2 signing. 1430 // The lineage contains the proof of rotation and will be used to determine the oldest 1431 // signer. 1432 ApkSigner.SignerConfig firstSigner = 1433 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 1434 ApkSigner.SignerConfig secondSigner = 1435 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1436 ApkSigner.SignerConfig thirdSigner = 1437 getDefaultSignerConfigFromResources(THIRD_RSA_2048_SIGNER_RESOURCE_NAME); 1438 SigningCertificateLineage lineage = 1439 Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-3-signers"); 1440 1441 // Verifies that the v1 signing scheme requires the oldest signer after a key rotation. 1442 List<ApkSigner.SignerConfig> signers = Collections.singletonList(thirdSigner); 1443 try { 1444 sign( 1445 "original.apk", 1446 new ApkSigner.Builder(signers) 1447 .setV1SigningEnabled(true) 1448 .setV2SigningEnabled(false) 1449 .setV3SigningEnabled(true) 1450 .setSigningCertificateLineage(lineage)); 1451 fail( 1452 "The signing should have failed due to the oldest signer in the lineage not" 1453 + " being provided for v1 signing"); 1454 } catch (IllegalArgumentException expected) { 1455 } 1456 1457 // Verifies that the v2 signing scheme requires the oldest signer after a key rotation. 1458 try { 1459 sign( 1460 "original.apk", 1461 new ApkSigner.Builder(signers) 1462 .setV1SigningEnabled(false) 1463 .setV2SigningEnabled(true) 1464 .setV3SigningEnabled(true) 1465 .setSigningCertificateLineage(lineage)); 1466 fail( 1467 "The signing should have failed due to the oldest signer in the lineage not" 1468 + " being provided for v2 signing"); 1469 } catch (IllegalArgumentException expected) { 1470 } 1471 1472 // Verifies that when only the v3 signing scheme is requested the oldest signer does not 1473 // need to be provided. 1474 sign( 1475 "original.apk", 1476 new ApkSigner.Builder(signers) 1477 .setV1SigningEnabled(false) 1478 .setV2SigningEnabled(false) 1479 .setV3SigningEnabled(true) 1480 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 1481 .setSigningCertificateLineage(lineage)); 1482 1483 // Verifies that an intermediate signer in the lineage is not sufficient to satisfy the 1484 // requirement that the oldest signer be provided for v1 and v2 signing. 1485 signers = Arrays.asList(secondSigner, thirdSigner); 1486 try { 1487 sign( 1488 "original.apk", 1489 new ApkSigner.Builder(signers) 1490 .setV1SigningEnabled(true) 1491 .setV2SigningEnabled(true) 1492 .setV3SigningEnabled(true) 1493 .setSigningCertificateLineage(lineage)); 1494 fail( 1495 "The signing should have failed due to the oldest signer in the lineage not" 1496 + " being provided for v1/v2 signing"); 1497 } catch (IllegalArgumentException expected) { 1498 } 1499 1500 // Verifies that the signing is successful when the oldest and newest signers are provided 1501 // and that intermediate signers are not required. 1502 signers = Arrays.asList(firstSigner, thirdSigner); 1503 sign( 1504 "original.apk", 1505 new ApkSigner.Builder(signers) 1506 .setV1SigningEnabled(true) 1507 .setV2SigningEnabled(true) 1508 .setV3SigningEnabled(true) 1509 .setSigningCertificateLineage(lineage)); 1510 } 1511 1512 @Test testV3SigningWithMultipleSignersAndNoLineageFails()1513 public void testV3SigningWithMultipleSignersAndNoLineageFails() throws Exception { 1514 // The v3 signing scheme does not support multiple signers; if multiple signers are provided 1515 // it is assumed these signers are part of the lineage. This test verifies v3 signing 1516 // fails if multiple signers are provided without a lineage. 1517 ApkSigner.SignerConfig firstSigner = 1518 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 1519 ApkSigner.SignerConfig secondSigner = 1520 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1521 List<ApkSigner.SignerConfig> signers = Arrays.asList(firstSigner, secondSigner); 1522 assertThrows( 1523 IllegalStateException.class, 1524 () -> 1525 sign( 1526 "original.apk", 1527 new ApkSigner.Builder(signers) 1528 .setV1SigningEnabled(true) 1529 .setV2SigningEnabled(true) 1530 .setV3SigningEnabled(true))); 1531 } 1532 1533 @Test testLineageCanBeReadAfterV3Signing()1534 public void testLineageCanBeReadAfterV3Signing() throws Exception { 1535 SigningCertificateLineage.SignerConfig firstSigner = 1536 Resources.toLineageSignerConfig(getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 1537 SigningCertificateLineage.SignerConfig secondSigner = 1538 Resources.toLineageSignerConfig(getClass(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1539 SigningCertificateLineage lineage = 1540 new SigningCertificateLineage.Builder(firstSigner, secondSigner).build(); 1541 List<ApkSigner.SignerConfig> signerConfigs = 1542 Arrays.asList( 1543 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 1544 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 1545 File out = 1546 sign( 1547 "original.apk", 1548 new ApkSigner.Builder(signerConfigs) 1549 .setV3SigningEnabled(true) 1550 .setSigningCertificateLineage(lineage)); 1551 SigningCertificateLineage lineageFromApk = SigningCertificateLineage.readFromApkFile(out); 1552 assertTrue( 1553 "The first signer was not in the lineage from the signed APK", 1554 lineageFromApk.isSignerInLineage((firstSigner))); 1555 assertTrue( 1556 "The second signer was not in the lineage from the signed APK", 1557 lineageFromApk.isSignerInLineage((secondSigner))); 1558 } 1559 1560 @Test testPublicKeyHasPositiveModulusAfterSigning()1561 public void testPublicKeyHasPositiveModulusAfterSigning() throws Exception { 1562 // The V2 and V3 signature schemes include the public key from the certificate in the 1563 // signing block. If a certificate with an RSAPublicKey is improperly encoded with a 1564 // negative modulus this was previously written to the signing block as is and failed on 1565 // device verification since on device the public key in the certificate was reencoded with 1566 // the correct encoding for the modulus. This test uses an improperly encoded certificate to 1567 // sign an APK and verifies that the public key in the signing block is corrected with a 1568 // positive modulus to allow on device installs / updates. 1569 List<ApkSigner.SignerConfig> signersList = 1570 Collections.singletonList( 1571 getDefaultSignerConfigFromResources( 1572 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 1573 FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS)); 1574 File signedApk = 1575 sign( 1576 "original.apk", 1577 new ApkSigner.Builder(signersList) 1578 .setV1SigningEnabled(true) 1579 .setV2SigningEnabled(true) 1580 .setV3SigningEnabled(true)); 1581 RSAPublicKey v2PublicKey = 1582 getRSAPublicKeyFromSigningBlock( 1583 signedApk, ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); 1584 assertTrue( 1585 "The modulus in the public key in the V2 signing block must not be negative", 1586 v2PublicKey.modulus.compareTo(BigInteger.ZERO) > 0); 1587 RSAPublicKey v3PublicKey = 1588 getRSAPublicKeyFromSigningBlock( 1589 signedApk, ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); 1590 assertTrue( 1591 "The modulus in the public key in the V3 signing block must not be negative", 1592 v3PublicKey.modulus.compareTo(BigInteger.ZERO) > 0); 1593 } 1594 1595 @Test testV4State_disableV2V3EnableV4_fails()1596 public void testV4State_disableV2V3EnableV4_fails() throws Exception { 1597 ApkSigner.SignerConfig signer = 1598 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 1599 1600 assertThrows( 1601 IllegalStateException.class, 1602 () -> 1603 sign( 1604 "original.apk", 1605 new ApkSigner.Builder(Collections.singletonList(signer)) 1606 .setV1SigningEnabled(true) 1607 .setV2SigningEnabled(false) 1608 .setV3SigningEnabled(false) 1609 .setV4SigningEnabled(true))); 1610 } 1611 1612 @Test testSignApk_stampFile()1613 public void testSignApk_stampFile() throws Exception { 1614 List<ApkSigner.SignerConfig> signers = 1615 Collections.singletonList( 1616 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1617 ApkSigner.SignerConfig sourceStampSigner = 1618 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1619 MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); 1620 messageDigest.update(sourceStampSigner.getCertificates().get(0).getEncoded()); 1621 byte[] expectedStampCertificateDigest = messageDigest.digest(); 1622 1623 File signedApkFile = 1624 sign( 1625 "original.apk", 1626 new ApkSigner.Builder(signers) 1627 .setV1SigningEnabled(true) 1628 .setSourceStampSignerConfig(sourceStampSigner)); 1629 1630 try (RandomAccessFile f = new RandomAccessFile(signedApkFile, "r")) { 1631 DataSource signedApk = DataSources.asDataSource(f, 0, f.length()); 1632 1633 ApkUtils.ZipSections zipSections = findZipSections(signedApk); 1634 List<CentralDirectoryRecord> cdRecords = 1635 V1SchemeVerifier.parseZipCentralDirectory(signedApk, zipSections); 1636 CentralDirectoryRecord stampCdRecord = null; 1637 for (CentralDirectoryRecord cdRecord : cdRecords) { 1638 if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(cdRecord.getName())) { 1639 stampCdRecord = cdRecord; 1640 break; 1641 } 1642 } 1643 assertNotNull(stampCdRecord); 1644 byte[] actualStampCertificateDigest = 1645 LocalFileRecord.getUncompressedData( 1646 signedApk, stampCdRecord, zipSections.getZipCentralDirectoryOffset()); 1647 assertArrayEquals(expectedStampCertificateDigest, actualStampCertificateDigest); 1648 } 1649 } 1650 1651 @Test testSignApk_existingStampFile_sameSourceStamp()1652 public void testSignApk_existingStampFile_sameSourceStamp() throws Exception { 1653 List<ApkSigner.SignerConfig> signers = 1654 Collections.singletonList( 1655 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1656 ApkSigner.SignerConfig sourceStampSigner = 1657 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 1658 1659 File signedApk = 1660 sign( 1661 "original-with-stamp-file.apk", 1662 new ApkSigner.Builder(signers) 1663 .setV1SigningEnabled(true) 1664 .setV2SigningEnabled(true) 1665 .setV3SigningEnabled(true) 1666 .setSourceStampSignerConfig(sourceStampSigner)); 1667 1668 ApkVerifier.Result sourceStampVerificationResult = 1669 verify(signedApk, /* minSdkVersionOverride= */ null); 1670 assertSourceStampVerified(signedApk, sourceStampVerificationResult); 1671 } 1672 1673 @Test testSignApk_existingStampFile_differentSourceStamp()1674 public void testSignApk_existingStampFile_differentSourceStamp() throws Exception { 1675 List<ApkSigner.SignerConfig> signers = 1676 Collections.singletonList( 1677 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1678 ApkSigner.SignerConfig sourceStampSigner = 1679 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1680 1681 Exception exception = 1682 assertThrows( 1683 ApkFormatException.class, 1684 () -> 1685 sign( 1686 "original-with-stamp-file.apk", 1687 new ApkSigner.Builder(signers) 1688 .setV1SigningEnabled(true) 1689 .setV2SigningEnabled(true) 1690 .setV3SigningEnabled(true) 1691 .setSourceStampSignerConfig(sourceStampSigner))); 1692 assertEquals( 1693 String.format( 1694 "Cannot generate SourceStamp. APK contains an existing entry with the" 1695 + " name: %s, and it is different than the provided source stamp" 1696 + " certificate", 1697 SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME), 1698 exception.getMessage()); 1699 } 1700 1701 @Test testSignApk_existingStampFile_differentSourceStamp_forceOverwrite()1702 public void testSignApk_existingStampFile_differentSourceStamp_forceOverwrite() 1703 throws Exception { 1704 List<ApkSigner.SignerConfig> signers = 1705 Collections.singletonList( 1706 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1707 ApkSigner.SignerConfig sourceStampSigner = 1708 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1709 1710 File signedApk = 1711 sign( 1712 "original-with-stamp-file.apk", 1713 new ApkSigner.Builder(signers) 1714 .setV1SigningEnabled(true) 1715 .setV2SigningEnabled(true) 1716 .setV3SigningEnabled(true) 1717 .setForceSourceStampOverwrite(true) 1718 .setSourceStampSignerConfig(sourceStampSigner)); 1719 1720 ApkVerifier.Result sourceStampVerificationResult = 1721 verify(signedApk, /* minSdkVersionOverride= */ null); 1722 assertSourceStampVerified(signedApk, sourceStampVerificationResult); 1723 } 1724 1725 @Test testSignApk_stampBlock_noStampGenerated()1726 public void testSignApk_stampBlock_noStampGenerated() throws Exception { 1727 List<ApkSigner.SignerConfig> signersList = 1728 Collections.singletonList( 1729 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1730 1731 File signedApkFile = 1732 sign( 1733 "original.apk", 1734 new ApkSigner.Builder(signersList) 1735 .setV1SigningEnabled(true) 1736 .setV2SigningEnabled(true) 1737 .setV3SigningEnabled(true)); 1738 1739 try (RandomAccessFile f = new RandomAccessFile(signedApkFile, "r")) { 1740 DataSource signedApk = DataSources.asDataSource(f, 0, f.length()); 1741 1742 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(signedApk); 1743 ApkSigningBlockUtils.Result result = 1744 new ApkSigningBlockUtils.Result(ApkSigningBlockUtils.VERSION_SOURCE_STAMP); 1745 assertThrows( 1746 ApkSigningBlockUtils.SignatureNotFoundException.class, 1747 () -> 1748 ApkSigningBlockUtils.findSignature( 1749 signedApk, 1750 zipSections, 1751 ApkSigningBlockUtils.VERSION_SOURCE_STAMP, 1752 result)); 1753 } 1754 } 1755 1756 @Test testSignApk_stampBlock_whenV1SignaturePresent()1757 public void testSignApk_stampBlock_whenV1SignaturePresent() throws Exception { 1758 List<ApkSigner.SignerConfig> signersList = 1759 Collections.singletonList( 1760 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1761 ApkSigner.SignerConfig sourceStampSigner = 1762 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1763 1764 File signedApk = 1765 sign( 1766 "original.apk", 1767 new ApkSigner.Builder(signersList) 1768 .setV1SigningEnabled(true) 1769 .setV2SigningEnabled(false) 1770 .setV3SigningEnabled(false) 1771 .setV4SigningEnabled(false) 1772 .setSourceStampSignerConfig(sourceStampSigner)); 1773 1774 ApkVerifier.Result sourceStampVerificationResult = 1775 verify(signedApk, /* minSdkVersionOverride= */ null); 1776 assertSourceStampVerified(signedApk, sourceStampVerificationResult); 1777 } 1778 1779 @Test testSignApk_stampBlock_whenV2SignaturePresent()1780 public void testSignApk_stampBlock_whenV2SignaturePresent() throws Exception { 1781 List<ApkSigner.SignerConfig> signersList = 1782 Collections.singletonList( 1783 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1784 ApkSigner.SignerConfig sourceStampSigner = 1785 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1786 1787 File signedApk = 1788 sign( 1789 "original.apk", 1790 new ApkSigner.Builder(signersList) 1791 .setV1SigningEnabled(false) 1792 .setV2SigningEnabled(true) 1793 .setV3SigningEnabled(false) 1794 .setSourceStampSignerConfig(sourceStampSigner)); 1795 1796 ApkVerifier.Result sourceStampVerificationResult = 1797 verifyForMinSdkVersion(signedApk, /* minSdkVersion= */ AndroidSdkVersion.N); 1798 assertSourceStampVerified(signedApk, sourceStampVerificationResult); 1799 } 1800 1801 @Test testSignApk_stampBlock_whenV3SignaturePresent()1802 public void testSignApk_stampBlock_whenV3SignaturePresent() throws Exception { 1803 List<ApkSigner.SignerConfig> signersList = 1804 Collections.singletonList( 1805 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1806 ApkSigner.SignerConfig sourceStampSigner = 1807 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1808 1809 File signedApk = 1810 sign( 1811 "original.apk", 1812 new ApkSigner.Builder(signersList) 1813 .setV1SigningEnabled(false) 1814 .setV2SigningEnabled(false) 1815 .setV3SigningEnabled(true) 1816 .setSourceStampSignerConfig(sourceStampSigner)); 1817 1818 ApkVerifier.Result sourceStampVerificationResult = 1819 verifyForMinSdkVersion(signedApk, /* minSdkVersion= */ AndroidSdkVersion.N); 1820 assertSourceStampVerified(signedApk, sourceStampVerificationResult); 1821 } 1822 1823 @Test testSignApk_stampBlock_withStampLineage()1824 public void testSignApk_stampBlock_withStampLineage() throws Exception { 1825 List<ApkSigner.SignerConfig> signersList = 1826 Collections.singletonList( 1827 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1828 ApkSigner.SignerConfig sourceStampSigner = 1829 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1830 SigningCertificateLineage sourceStampLineage = 1831 Resources.toSigningCertificateLineage( 1832 getClass(), LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 1833 1834 File signedApk = 1835 sign( 1836 "original.apk", 1837 new ApkSigner.Builder(signersList) 1838 .setV1SigningEnabled(true) 1839 .setV2SigningEnabled(true) 1840 .setV3SigningEnabled(true) 1841 .setSourceStampSignerConfig(sourceStampSigner) 1842 .setSourceStampSigningCertificateLineage(sourceStampLineage)); 1843 1844 ApkVerifier.Result sourceStampVerificationResult = 1845 verify(signedApk, /* minSdkVersion= */ null); 1846 assertSourceStampVerified(signedApk, sourceStampVerificationResult); 1847 } 1848 1849 @Test testSignApk_Pinlist()1850 public void testSignApk_Pinlist() throws Exception { 1851 List<ApkSigner.SignerConfig> rsa2048SignerConfig = 1852 Collections.singletonList( 1853 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1854 assertGolden( 1855 "pinsapp-unsigned.apk", 1856 "golden-pinsapp-signed.apk", 1857 new ApkSigner.Builder(rsa2048SignerConfig) 1858 .setV1SigningEnabled(true) 1859 .setV2SigningEnabled(true) 1860 .setV3SigningEnabled(true) 1861 .setVerityEnabled(true) 1862 .setAlignmentPreserved(true)); 1863 assertTrue("pinlist.meta file must be in the signed APK.", 1864 resourceZipFileContains("golden-pinsapp-signed.apk", "pinlist.meta")); 1865 } 1866 1867 @Test testOtherSignersSignaturesPreserved_extraSigBlock_signatureAppended()1868 public void testOtherSignersSignaturesPreserved_extraSigBlock_signatureAppended() 1869 throws Exception { 1870 // The DefaultApkSignerEngine contains support to append a signature to an existing 1871 // signing block; any existing signature blocks within the APK signing block should be 1872 // left intact except for the original verity padding block (since this is regenerated) and 1873 // the source stamp. This test verifies that an extra signature block is still in 1874 // the APK signing block after appending a V2 signature. 1875 List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList( 1876 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); 1877 1878 File signedApk = sign("v2-rsa-2048-with-extra-sig-block.apk", 1879 new ApkSigner.Builder(ecP256SignerConfig) 1880 .setV1SigningEnabled(false) 1881 .setV2SigningEnabled(true) 1882 .setV3SigningEnabled(false) 1883 .setV4SigningEnabled(false) 1884 .setOtherSignersSignaturesPreserved(true)); 1885 1886 ApkVerifier.Result result = verify(signedApk, null); 1887 assertVerified(result); 1888 assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 1889 EC_P256_SIGNER_RESOURCE_NAME); 1890 assertSigningBlockContains(signedApk, Pair.of(EXTRA_BLOCK_VALUE, EXTRA_BLOCK_ID)); 1891 } 1892 1893 @Test testOtherSignersSignaturesPreserved_v1Only_signatureAppended()1894 public void testOtherSignersSignaturesPreserved_v1Only_signatureAppended() throws Exception { 1895 // This test verifies appending an additional V1 signature to an existing V1 signer behaves 1896 // similar to jarsigner where the APK is then verified as signed by both signers. 1897 List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList( 1898 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); 1899 1900 File signedApk = sign("v1-only-with-rsa-2048.apk", 1901 new ApkSigner.Builder(ecP256SignerConfig) 1902 .setV1SigningEnabled(true) 1903 .setV2SigningEnabled(false) 1904 .setV3SigningEnabled(false) 1905 .setV4SigningEnabled(false) 1906 .setOtherSignersSignaturesPreserved(true)); 1907 1908 ApkVerifier.Result result = verify(signedApk, null); 1909 assertVerified(result); 1910 assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 1911 EC_P256_SIGNER_RESOURCE_NAME); 1912 } 1913 1914 @Test testOtherSignersSignaturesPreserved_v3OnlyDifferentSigner_throwsException()1915 public void testOtherSignersSignaturesPreserved_v3OnlyDifferentSigner_throwsException() 1916 throws Exception { 1917 // The V3 Signature Scheme only supports a single signer; if an attempt is made to append 1918 // a different signer to a V3 signature then an exception should be thrown. 1919 // The APK used for this test is signed with the ec-p256 signer so use the rsa-2048 to 1920 // attempt to append a different signature. 1921 List<ApkSigner.SignerConfig> rsa2048SignerConfig = Collections.singletonList( 1922 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1923 1924 assertThrows(IllegalStateException.class, () -> 1925 sign("v3-only-with-stamp.apk", 1926 new ApkSigner.Builder(rsa2048SignerConfig) 1927 .setV1SigningEnabled(false) 1928 .setV2SigningEnabled(false) 1929 .setV3SigningEnabled(true) 1930 .setV4SigningEnabled(false) 1931 .setOtherSignersSignaturesPreserved(true)) 1932 ); 1933 } 1934 1935 @Test testOtherSignersSignaturesPreserved_v2OnlyAppendV2V3SameSigner_signatureAppended()1936 public void testOtherSignersSignaturesPreserved_v2OnlyAppendV2V3SameSigner_signatureAppended() 1937 throws Exception { 1938 // A V2 and V3 signature can be appended to an existing V2 signature if the same signer is 1939 // used to resign the APK; this could be used in a case where an APK was previously signed 1940 // with just the V2 signature scheme along with additional non-APK signing scheme signature 1941 // blocks and the signer wanted to preserve those existing blocks. 1942 List<ApkSigner.SignerConfig> rsa2048SignerConfig = Collections.singletonList( 1943 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1944 1945 File signedApk = sign("v2-rsa-2048-with-extra-sig-block.apk", 1946 new ApkSigner.Builder(rsa2048SignerConfig) 1947 .setV1SigningEnabled(false) 1948 .setV2SigningEnabled(true) 1949 .setV3SigningEnabled(true) 1950 .setV4SigningEnabled(false) 1951 .setOtherSignersSignaturesPreserved(true)); 1952 1953 ApkVerifier.Result result = verify(signedApk, null); 1954 assertVerified(result); 1955 assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 1956 assertSigningBlockContains(signedApk, Pair.of(EXTRA_BLOCK_VALUE, EXTRA_BLOCK_ID)); 1957 } 1958 1959 @Test testOtherSignersSignaturesPreserved_v2OnlyAppendV3SameSigner_throwsException()1960 public void testOtherSignersSignaturesPreserved_v2OnlyAppendV3SameSigner_throwsException() 1961 throws Exception { 1962 // A V3 only signature cannot be appended to an existing V2 signature, even when using the 1963 // same signer, since the V2 signature would then not contain the stripping protection for 1964 // the V3 signature. If the same signer is being used then the signer should be configured 1965 // to resign using the V2 signature scheme as well as the V3 signature scheme. 1966 List<ApkSigner.SignerConfig> rsa2048SignerConfig = Collections.singletonList( 1967 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1968 1969 assertThrows(IllegalStateException.class, () -> 1970 sign("v2-rsa-2048-with-extra-sig-block.apk", 1971 new ApkSigner.Builder(rsa2048SignerConfig) 1972 .setV1SigningEnabled(false) 1973 .setV2SigningEnabled(false) 1974 .setV3SigningEnabled(true) 1975 .setV4SigningEnabled(false) 1976 .setOtherSignersSignaturesPreserved(true))); 1977 } 1978 1979 @Test testOtherSignersSignaturesPreserved_v1v2IndividuallySign_signaturesAppended()1980 public void testOtherSignersSignaturesPreserved_v1v2IndividuallySign_signaturesAppended() 1981 throws Exception { 1982 // One of the primary requirements for appending signatures is when an APK has already 1983 // released with two signers; with the minimum signature scheme v2 requirement for target 1984 // SDK version 30+ each signer must be able to append their signature to the existing 1985 // signature block. This test verifies an APK with appended signatures verifies as expected 1986 // after a series of appending V1 and V2 signatures. 1987 List<ApkSigner.SignerConfig> rsa2048SignerConfig = Collections.singletonList( 1988 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 1989 List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList( 1990 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); 1991 1992 // When two parties are signing an APK the first must sign with both V1 and V2; this will 1993 // write the stripping-protection attribute to the V1 signature. 1994 File signedApk = sign("original.apk", 1995 new ApkSigner.Builder(rsa2048SignerConfig) 1996 .setV1SigningEnabled(true) 1997 .setV2SigningEnabled(true) 1998 .setV3SigningEnabled(false) 1999 .setV4SigningEnabled(false)); 2000 2001 // The second party can then append their signature with both the V1 and V2 signature; this 2002 // will invalidate the V2 signature of the initial signer since the APK itself will be 2003 // modified with this signers V1 / jar signature. 2004 signedApk = sign(signedApk, 2005 new ApkSigner.Builder(ecP256SignerConfig) 2006 .setV1SigningEnabled(true) 2007 .setV2SigningEnabled(true) 2008 .setV3SigningEnabled(false) 2009 .setV4SigningEnabled(false) 2010 .setOtherSignersSignaturesPreserved(true)); 2011 2012 // The first party will then need to resign with just the V2 signature after its previous 2013 // signature was invalidated by the V1 signature of the second signer; however since this 2014 // signature is appended its previous V2 signature should be removed from the signature 2015 // block and replaced with this new signature while preserving the V2 signature of the 2016 // other signer. 2017 signedApk = sign(signedApk, 2018 new ApkSigner.Builder(rsa2048SignerConfig) 2019 .setV1SigningEnabled(false) 2020 .setV2SigningEnabled(true) 2021 .setV3SigningEnabled(false) 2022 .setV4SigningEnabled(false) 2023 .setOtherSignersSignaturesPreserved(true)); 2024 2025 ApkVerifier.Result result = verify(signedApk, null); 2026 assertVerified(result); 2027 assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2028 EC_P256_SIGNER_RESOURCE_NAME); 2029 } 2030 2031 @Test testSetMinSdkVersionForRotation_lessThanT_noV31Block()2032 public void testSetMinSdkVersionForRotation_lessThanT_noV31Block() throws Exception { 2033 // The V3.1 signing block is intended to allow APK signing key rotation to target T+, but 2034 // a minimum SDK version can be explicitly set for rotation; if it is less than T than 2035 // the rotated key will be included in the V3.0 block. 2036 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 2037 Arrays.asList( 2038 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 2039 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 2040 SigningCertificateLineage lineage = 2041 Resources.toSigningCertificateLineage( 2042 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2043 2044 File signedApkMinRotationP = sign("original.apk", 2045 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2046 .setV1SigningEnabled(true) 2047 .setV2SigningEnabled(true) 2048 .setV3SigningEnabled(true) 2049 .setV4SigningEnabled(false) 2050 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 2051 .setSigningCertificateLineage(lineage)); 2052 ApkVerifier.Result resultMinRotationP = verify(signedApkMinRotationP, null); 2053 // The V3.1 signature scheme was introduced in T; specifying an older SDK version as the 2054 // minimum for rotation should cause the APK to still be signed with rotation in the V3.0 2055 // signing block. 2056 File signedApkMinRotationS = sign("original.apk", 2057 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2058 .setV1SigningEnabled(true) 2059 .setV2SigningEnabled(true) 2060 .setV3SigningEnabled(true) 2061 .setV4SigningEnabled(false) 2062 .setMinSdkVersionForRotation(AndroidSdkVersion.S) 2063 .setSigningCertificateLineage(lineage)); 2064 ApkVerifier.Result resultMinRotationS = verify(signedApkMinRotationS, null); 2065 2066 assertVerified(resultMinRotationP); 2067 assertFalse(resultMinRotationP.isVerifiedUsingV31Scheme()); 2068 assertEquals(1, resultMinRotationP.getV3SchemeSigners().size()); 2069 assertResultContainsSigners(resultMinRotationP, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2070 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2071 assertVerified(resultMinRotationS); 2072 assertFalse(resultMinRotationS.isVerifiedUsingV31Scheme()); 2073 // While rotation is targeting S, signer blocks targeting specific SDK versions have not 2074 // been tested in previous platform releases; ensure only a single signer block with the 2075 // rotated key is in the V3 block. 2076 assertEquals(1, resultMinRotationS.getV3SchemeSigners().size()); 2077 assertResultContainsSigners(resultMinRotationS, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2078 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2079 } 2080 2081 @Test testSetMinSdkVersionForRotation_TAndLater_v31Block()2082 public void testSetMinSdkVersionForRotation_TAndLater_v31Block() throws Exception { 2083 // When T or later is specified as the minimum SDK version for rotation, then a new V3.1 2084 // signing block should be created with the new rotated key, and the V3.0 signing block 2085 // should still be signed with the original key. 2086 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 2087 Arrays.asList( 2088 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 2089 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 2090 SigningCertificateLineage lineage = 2091 Resources.toSigningCertificateLineage( 2092 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2093 2094 File signedApkMinRotationT = sign("original.apk", 2095 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2096 .setV1SigningEnabled(true) 2097 .setV2SigningEnabled(true) 2098 .setV3SigningEnabled(true) 2099 .setV4SigningEnabled(false) 2100 .setMinSdkVersionForRotation(AndroidSdkVersion.T) 2101 .setSigningCertificateLineage(lineage)); 2102 ApkVerifier.Result resultMinRotationT = verify(signedApkMinRotationT, null); 2103 // The API level for a release after T is not yet defined, so for now treat it as T + 1. 2104 File signedApkMinRotationU = sign("original.apk", 2105 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2106 .setV1SigningEnabled(true) 2107 .setV2SigningEnabled(true) 2108 .setV3SigningEnabled(true) 2109 .setV4SigningEnabled(false) 2110 .setMinSdkVersionForRotation(AndroidSdkVersion.T + 1) 2111 .setSigningCertificateLineage(lineage)); 2112 ApkVerifier.Result resultMinRotationU = verify(signedApkMinRotationU, null); 2113 2114 assertVerified(resultMinRotationT); 2115 assertTrue(resultMinRotationT.isVerifiedUsingV31Scheme()); 2116 assertResultContainsSigners(resultMinRotationT, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2117 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2118 assertV31SignerTargetsMinApiLevel(resultMinRotationT, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2119 AndroidSdkVersion.T); 2120 assertVerified(resultMinRotationU); 2121 assertTrue(resultMinRotationU.isVerifiedUsingV31Scheme()); 2122 assertResultContainsSigners(resultMinRotationU, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2123 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2124 assertV31SignerTargetsMinApiLevel(resultMinRotationU, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2125 AndroidSdkVersion.T + 1); 2126 } 2127 2128 @Test testSetMinSdkVersionForRotation_targetTNoOriginalSigner_fails()2129 public void testSetMinSdkVersionForRotation_targetTNoOriginalSigner_fails() throws Exception { 2130 // Similar to the V1 and V2 signatures schemes, if an app is targeting P or later with 2131 // rotation targeting T, the original signer must be provided so that it can be used in the 2132 // V3.0 signing block; if it is not provided the signer should throw an Exception. 2133 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = List 2134 .of(getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 2135 SigningCertificateLineage lineage = 2136 Resources.toSigningCertificateLineage( 2137 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2138 2139 assertThrows(IllegalArgumentException.class, () -> 2140 sign("original.apk", 2141 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2142 .setV1SigningEnabled(false) 2143 .setV2SigningEnabled(false) 2144 .setV3SigningEnabled(true) 2145 .setV4SigningEnabled(false) 2146 .setMinSdkVersion(28) 2147 .setMinSdkVersionForRotation(AndroidSdkVersion.T) 2148 .setSigningCertificateLineage(lineage))); 2149 } 2150 2151 @Test testSetMinSdkVersionForRotation_targetTAndApkMinSdkT_onlySignsV3Block()2152 public void testSetMinSdkVersionForRotation_targetTAndApkMinSdkT_onlySignsV3Block() 2153 throws Exception { 2154 // A V3.1 signing block should only exist alongside a V3.0 signing block; if an APK's 2155 // min SDK version is greater than or equal to the SDK version for rotation then the 2156 // original signer should not be required, and the rotated signing key should be in 2157 // a V3.0 signing block. 2158 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = List 2159 .of(getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 2160 SigningCertificateLineage lineage = 2161 Resources.toSigningCertificateLineage( 2162 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2163 2164 File signedApk = sign("original.apk", 2165 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2166 .setV1SigningEnabled(false) 2167 .setV2SigningEnabled(false) 2168 .setV3SigningEnabled(true) 2169 .setV4SigningEnabled(false) 2170 .setMinSdkVersion(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT) 2171 .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT) 2172 .setSigningCertificateLineage(lineage)); 2173 ApkVerifier.Result result = verifyForMinSdkVersion(signedApk, 2174 V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT); 2175 2176 assertVerified(result); 2177 assertFalse(result.isVerifiedUsingV31Scheme()); 2178 assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2179 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2180 } 2181 2182 @Test testSetMinSdkVersionForRotation_targetTWithSourceStamp_noWarnings()2183 public void testSetMinSdkVersionForRotation_targetTWithSourceStamp_noWarnings() 2184 throws Exception { 2185 // Source stamp verification will report a warning if a stamp signature is not found for any 2186 // of the APK Signature Schemes used to sign the APK. This test verifies an APK signed with 2187 // a rotated key in the v3.1 block and a source stamp successfully verifies, including the 2188 // source stamp, without any warnings. 2189 ApkSigner.SignerConfig rsa2048OriginalSignerConfig = getDefaultSignerConfigFromResources( 2190 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2191 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 2192 Arrays.asList( 2193 rsa2048OriginalSignerConfig, 2194 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 2195 SigningCertificateLineage lineage = 2196 Resources.toSigningCertificateLineage( 2197 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2198 2199 File signedApk = sign("original.apk", 2200 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2201 .setV1SigningEnabled(true) 2202 .setV2SigningEnabled(true) 2203 .setV3SigningEnabled(true) 2204 .setV4SigningEnabled(false) 2205 .setMinSdkVersionForRotation(AndroidSdkVersion.T) 2206 .setSigningCertificateLineage(lineage) 2207 .setSourceStampSignerConfig(rsa2048OriginalSignerConfig)); 2208 ApkVerifier.Result result = verify(signedApk, null); 2209 2210 assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2211 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2212 assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2213 AndroidSdkVersion.T); 2214 assertSourceStampVerified(signedApk, result); 2215 } 2216 2217 @Test testSetRotationTargetsDevRelease_target34_v30SignerTargetsAtLeast34()2218 public void testSetRotationTargetsDevRelease_target34_v30SignerTargetsAtLeast34() 2219 throws Exception { 2220 // During development of a new platform release the new platform will use the SDK version 2221 // of the previously released platform, so in order to test rotation on a new platform 2222 // release it must target the SDK version of the previous platform. However an APK signed 2223 // with the v3.1 signature scheme and targeting rotation on the previous platform release X 2224 // would still use rotation if that APK were installed on a device running release version 2225 // X. To support targeting rotation on the main branch, the v3.1 signature scheme supports 2226 // a rotation-targets-dev-release attribute; this allows the APK to use the v3.1 signer 2227 // block on a development platform with SDK version X while a release platform X will 2228 // skip this signer block when it sees this additional attribute. To ensure that the APK 2229 // will still target the released platform X, the v3.0 signer must have a maxSdkVersion 2230 // of at least X for the signer. 2231 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 2232 Arrays.asList( 2233 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 2234 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 2235 SigningCertificateLineage lineage = 2236 Resources.toSigningCertificateLineage( 2237 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2238 int rotationMinSdkVersion = 10000; 2239 2240 File signedApk = sign("original.apk", 2241 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2242 .setV1SigningEnabled(true) 2243 .setV2SigningEnabled(true) 2244 .setV3SigningEnabled(true) 2245 .setV4SigningEnabled(false) 2246 .setMinSdkVersionForRotation(rotationMinSdkVersion) 2247 .setSigningCertificateLineage(lineage) 2248 .setRotationTargetsDevRelease(true)); 2249 ApkVerifier.Result result = verify(signedApk, null); 2250 2251 assertVerified(result); 2252 assertTrue(result.isVerifiedUsingV31Scheme()); 2253 assertTrue(result.getV31SchemeSigners().get(0).getRotationTargetsDevRelease()); 2254 assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2255 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2256 assertTrue(result.getV3SchemeSigners().get(0).getMaxSdkVersion() >= rotationMinSdkVersion); 2257 } 2258 2259 @Test testV3_rotationMinSdkVersionLessThanTV3Only_origSignerNotRequired()2260 public void testV3_rotationMinSdkVersionLessThanTV3Only_origSignerNotRequired() 2261 throws Exception { 2262 // The v3.1 signature scheme allows a rotation-min-sdk-version be specified to target T+ 2263 // for rotation; however if this value is less than the expected SDK version of T, then 2264 // apksig should just use the rotated signing key in the v3.0 block. An APK that targets 2265 // P+ that wants to use rotation in the v3.0 signing block should only need to provide 2266 // the rotated signing key and lineage; this test ensures this behavior when the 2267 // rotation-min-sdk-version is set to a value > P and < T. 2268 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 2269 Arrays.asList( 2270 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 2271 SigningCertificateLineage lineage = 2272 Resources.toSigningCertificateLineage( 2273 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2274 2275 File signedApkRotationOnQ = sign("original.apk", 2276 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2277 .setV1SigningEnabled(false) 2278 .setV2SigningEnabled(false) 2279 .setV3SigningEnabled(true) 2280 .setV4SigningEnabled(false) 2281 .setMinSdkVersion(AndroidSdkVersion.P) 2282 .setMinSdkVersionForRotation(AndroidSdkVersion.Q) 2283 .setSigningCertificateLineage(lineage)); 2284 ApkVerifier.Result resultRotationOnQ = verify(signedApkRotationOnQ, AndroidSdkVersion.P); 2285 2286 assertVerified(resultRotationOnQ); 2287 assertEquals(1, resultRotationOnQ.getV3SchemeSigners().size()); 2288 assertFalse(resultRotationOnQ.isVerifiedUsingV31Scheme()); 2289 assertResultContainsSigners(resultRotationOnQ, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2290 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2291 } 2292 2293 @Test testV31_rotationMinSdkVersionEqualsMinSdkVersion_v3SignerPresent()2294 public void testV31_rotationMinSdkVersionEqualsMinSdkVersion_v3SignerPresent() 2295 throws Exception { 2296 // The SDK version for Sv2 (32) is used as the minSdkVersion for the V3.1 signature 2297 // scheme to allow rotation to target the T development platform; this will be updated 2298 // to the real SDK version of T once its SDK is finalized. This test verifies if a 2299 // package has Sv2 as its minSdkVersion, the signing can complete as expected with the 2300 // v3 block signed by the original signer and targeting just Sv2, and the v3.1 block 2301 // signed by the rotated signer and targeting the dev release of Sv2 and all later releases. 2302 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 2303 Arrays.asList( 2304 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 2305 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 2306 SigningCertificateLineage lineage = 2307 Resources.toSigningCertificateLineage( 2308 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2309 2310 File signedApk = sign("original-minSdk32.apk", 2311 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2312 .setV1SigningEnabled(true) 2313 .setV2SigningEnabled(true) 2314 .setV3SigningEnabled(true) 2315 .setV4SigningEnabled(false) 2316 .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT) 2317 .setSigningCertificateLineage(lineage)); 2318 ApkVerifier.Result result = verify(signedApk, null); 2319 2320 assertVerified(result); 2321 assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); 2322 } 2323 2324 @Test testV31_rotationMinSdkVersionTWithoutLineage_v30VerificationSucceeds()2325 public void testV31_rotationMinSdkVersionTWithoutLineage_v30VerificationSucceeds() 2326 throws Exception { 2327 // apksig allows setting a rotation-min-sdk-version without providing a rotated signing 2328 // key / lineage; however in the absence of rotation, the rotation-min-sdk-version should 2329 // be a no-op, and the stripping protection attribute should not be written to the v3.0 2330 // signer. 2331 List<ApkSigner.SignerConfig> rsa2048SignerConfig = 2332 Collections.singletonList( 2333 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 2334 2335 File signedApk = sign("original.apk", 2336 new ApkSigner.Builder(rsa2048SignerConfig) 2337 .setV1SigningEnabled(true) 2338 .setV2SigningEnabled(true) 2339 .setV3SigningEnabled(true) 2340 .setV4SigningEnabled(false) 2341 .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT)); 2342 ApkVerifier.Result result = verify(signedApk, null); 2343 2344 assertVerified(result); 2345 assertFalse(result.isVerifiedUsingV31Scheme()); 2346 assertTrue(result.isVerifiedUsingV3Scheme()); 2347 } 2348 2349 @Test testV31_rotationMinSdkVersionDefault_rotationTargetsT()2350 public void testV31_rotationMinSdkVersionDefault_rotationTargetsT() throws Exception { 2351 // The v3.1 signature scheme was introduced in T to allow developers to target T+ for 2352 // rotation due to known issues with rotation on previous platform releases. This test 2353 // verifies an APK signed with a rotated signing key defaults to the original signing 2354 // key used in the v3 signing block for pre-T devices, and the rotated signing key used 2355 // in the v3.1 signing block for T+ devices. 2356 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 2357 Arrays.asList( 2358 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 2359 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 2360 SigningCertificateLineage lineage = 2361 Resources.toSigningCertificateLineage( 2362 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2363 2364 File signedApk = sign("original.apk", 2365 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2366 .setV1SigningEnabled(true) 2367 .setV2SigningEnabled(true) 2368 .setV3SigningEnabled(true) 2369 .setV4SigningEnabled(false) 2370 .setSigningCertificateLineage(lineage)); 2371 ApkVerifier.Result result = verify(signedApk, null); 2372 2373 assertVerified(result); 2374 assertTrue(result.isVerifiedUsingV3Scheme()); 2375 assertTrue(result.isVerifiedUsingV31Scheme()); 2376 assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); 2377 assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2378 AndroidSdkVersion.T); 2379 } 2380 2381 @Test testV31_rotationMinSdkVersionP_rotationTargetsP()2382 public void testV31_rotationMinSdkVersionP_rotationTargetsP() throws Exception { 2383 // While the V3.1 signature scheme will target T by default, a package that has 2384 // previously rotated can provide a rotation-min-sdk-version less than T to continue 2385 // using the rotated signing key in the v3.0 block. 2386 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 2387 Arrays.asList( 2388 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 2389 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 2390 SigningCertificateLineage lineage = 2391 Resources.toSigningCertificateLineage( 2392 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2393 2394 File signedApk = sign("original.apk", 2395 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2396 .setV1SigningEnabled(true) 2397 .setV2SigningEnabled(true) 2398 .setV3SigningEnabled(true) 2399 .setV4SigningEnabled(false) 2400 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 2401 .setSigningCertificateLineage(lineage)); 2402 ApkVerifier.Result result = verify(signedApk, null); 2403 2404 assertVerified(result); 2405 assertTrue(result.isVerifiedUsingV3Scheme()); 2406 assertFalse(result.isVerifiedUsingV31Scheme()); 2407 } 2408 2409 @Test testV31_rotationMinSdkVersionDevRelease_rotationTargetsDevRelease()2410 public void testV31_rotationMinSdkVersionDevRelease_rotationTargetsDevRelease() 2411 throws Exception { 2412 // The V3.1 signature scheme can be used to target rotation for a development release; 2413 // a development release uses the SDK version of the previously finalized release until 2414 // its own SDK is finalized. This test verifies if the rotation-min-sdk-version is set to 2415 // the current development release, then the resulting APK should target the previously 2416 // finalized release and the rotation-targets-dev-release attribute should be set for 2417 // the signer. 2418 // If the development release is less than the first release that supports V3.1, then 2419 // a development release is not currently supported. 2420 assumeTrue(V3SchemeConstants.DEV_RELEASE >= V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT); 2421 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 2422 Arrays.asList( 2423 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 2424 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 2425 SigningCertificateLineage lineage = 2426 Resources.toSigningCertificateLineage( 2427 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2428 2429 File signedApk = sign("original.apk", 2430 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 2431 .setV1SigningEnabled(true) 2432 .setV2SigningEnabled(true) 2433 .setV3SigningEnabled(true) 2434 .setV4SigningEnabled(false) 2435 .setMinSdkVersionForRotation(V3SchemeConstants.DEV_RELEASE) 2436 .setSigningCertificateLineage(lineage)); 2437 ApkVerifier.Result result = verify(signedApk, null); 2438 2439 assertVerified(result); 2440 assertVerificationWarning(result, null); 2441 assertTrue(result.isVerifiedUsingV3Scheme()); 2442 assertTrue(result.isVerifiedUsingV31Scheme()); 2443 assertEquals(V3SchemeConstants.PROD_RELEASE, 2444 result.getV31SchemeSigners().get(0).getMinSdkVersion()); 2445 assertTrue(result.getV31SchemeSigners().get(0).getRotationTargetsDevRelease()); 2446 // The maxSdkVersion for the V3 signer should overlap with the minSdkVersion for the V3.1 2447 // signer. 2448 assertEquals(V3SchemeConstants.PROD_RELEASE, 2449 result.getV3SchemeSigners().get(0).getMaxSdkVersion()); 2450 } 2451 2452 2453 @Test testV31_oneTargetedSigningConfigT_targetsT()2454 public void testV31_oneTargetedSigningConfigT_targetsT() throws Exception { 2455 // The V3.1 signature scheme supports targeting a signing config for devices running 2456 // T+. This test verifies a single signing config targeting T+ is written to the v3.1 2457 // block, and the original signer is used for pre-T devices in the v3.0 block. This 2458 // is functionally equivalent to calling setMinSdkVersionForRotation(AndroidSdkVersion.T). 2459 SigningCertificateLineage lineage = 2460 Resources.toSigningCertificateLineage( 2461 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2462 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 2463 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2464 ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources( 2465 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineage); 2466 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, targetedSigner); 2467 2468 File signedApk = sign("original.apk", 2469 new ApkSigner.Builder(signerConfigs) 2470 .setV1SigningEnabled(true) 2471 .setV2SigningEnabled(true) 2472 .setV3SigningEnabled(true) 2473 .setV4SigningEnabled(false)); 2474 ApkVerifier.Result result = verify(signedApk, null); 2475 2476 assertVerified(result); 2477 assertVerificationWarning(result, null); 2478 assertTrue(result.isVerifiedUsingV3Scheme()); 2479 assertTrue(result.isVerifiedUsingV31Scheme()); 2480 assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); 2481 assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2482 AndroidSdkVersion.T); 2483 assertEquals(1, result.getV31SchemeSigners().size()); 2484 assertLineageContainsExpectedSigners( 2485 result.getV31SchemeSigners().get(0).getSigningCertificateLineage(), 2486 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2487 } 2488 2489 @Test testV31_oneTargetedSigningConfig10000_targets10000()2490 public void testV31_oneTargetedSigningConfig10000_targets10000() throws Exception { 2491 // When a signing config targets a later release, the V3.0 signature should be used for all 2492 // platform releases prior to the targeted release. This test verifies a signing config 2493 // targeting SDK 10000 has a V3.0 block that targets through SDK 9999. 2494 SigningCertificateLineage lineage = 2495 Resources.toSigningCertificateLineage( 2496 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2497 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 2498 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2499 ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources( 2500 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, 10000, lineage); 2501 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, targetedSigner); 2502 2503 File signedApk = sign("original.apk", 2504 new ApkSigner.Builder(signerConfigs) 2505 .setV1SigningEnabled(true) 2506 .setV2SigningEnabled(true) 2507 .setV3SigningEnabled(true) 2508 .setV4SigningEnabled(false)); 2509 ApkVerifier.Result result = verify(signedApk, null); 2510 2511 assertVerified(result); 2512 assertVerificationWarning(result, null); 2513 assertTrue(result.isVerifiedUsingV3Scheme()); 2514 assertTrue(result.isVerifiedUsingV31Scheme()); 2515 assertEquals(9999, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); 2516 assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 10000); 2517 assertEquals(1, result.getV31SchemeSigners().size()); 2518 assertLineageContainsExpectedSigners( 2519 result.getV31SchemeSigners().get(0).getSigningCertificateLineage(), 2520 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2521 } 2522 2523 2524 @Test test31_twoTargetedSigningConfigs_twoV31Signers()2525 public void test31_twoTargetedSigningConfigs_twoV31Signers() throws Exception { 2526 // This test verifies multiple signing configs targeting T+ can be added to the V3.1 2527 // signing block. 2528 SigningCertificateLineage lineageTargetT = 2529 Resources.toSigningCertificateLineage( 2530 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2531 SigningCertificateLineage lineageTargetU = 2532 Resources.toSigningCertificateLineage( 2533 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 2534 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 2535 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2536 ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( 2537 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); 2538 ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( 2539 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU); 2540 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT, 2541 signerTargetU); 2542 2543 File signedApk = sign("original.apk", 2544 new ApkSigner.Builder(signerConfigs) 2545 .setV1SigningEnabled(true) 2546 .setV2SigningEnabled(true) 2547 .setV3SigningEnabled(true) 2548 .setV4SigningEnabled(false)); 2549 ApkVerifier.Result result = verify(signedApk, null); 2550 2551 assertVerified(result); 2552 assertVerificationWarning(result, null); 2553 assertTrue(result.isVerifiedUsingV3Scheme()); 2554 assertTrue(result.isVerifiedUsingV31Scheme()); 2555 assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); 2556 assertEquals(2, result.getV31SchemeSigners().size()); 2557 assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2558 AndroidSdkVersion.T); 2559 assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 2560 AndroidSdkVersion.U); 2561 assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, 2562 AndroidSdkVersion.T).getSigningCertificateLineage(), 2563 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2564 assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, 2565 AndroidSdkVersion.U).getSigningCertificateLineage(), 2566 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2567 THIRD_RSA_2048_SIGNER_RESOURCE_NAME); 2568 } 2569 2570 @Test test31_threeTargetedSigningConfigs_threeV31Signers()2571 public void test31_threeTargetedSigningConfigs_threeV31Signers() throws Exception { 2572 // This test verifies multiple signing configs targeting T+ with modified capabilities 2573 // can be added to the V3.1 signing block. 2574 SigningCertificateLineage lineageTargetT = 2575 Resources.toSigningCertificateLineage( 2576 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2577 SigningCertificateLineage lineageTargetU = 2578 Resources.toSigningCertificateLineage( 2579 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 2580 SigningCertificateLineage lineageTarget10000 = 2581 Resources.toSigningCertificateLineage( 2582 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_1_NO_CAPS_RESOURCE_NAME); 2583 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 2584 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2585 ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( 2586 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); 2587 ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( 2588 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, 2589 lineageTargetU); 2590 ApkSigner.SignerConfig signerTarget10000 = getDefaultSignerConfigFromResources( 2591 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, 10000, 2592 lineageTarget10000); 2593 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT, 2594 signerTargetU, signerTarget10000); 2595 2596 File signedApk = sign("original.apk", 2597 new ApkSigner.Builder(signerConfigs) 2598 .setV1SigningEnabled(true) 2599 .setV2SigningEnabled(true) 2600 .setV3SigningEnabled(true) 2601 .setV4SigningEnabled(false)); 2602 ApkVerifier.Result result = verify(signedApk, null); 2603 2604 assertVerified(result); 2605 assertVerificationWarning(result, null); 2606 assertTrue(result.isVerifiedUsingV3Scheme()); 2607 assertTrue(result.isVerifiedUsingV31Scheme()); 2608 assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); 2609 assertEquals(3, result.getV31SchemeSigners().size()); 2610 assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2611 AndroidSdkVersion.T); 2612 assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 2613 AndroidSdkVersion.U); 2614 assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 10000); 2615 assertLineageContainsExpectedSignersWithCapabilities(getV31SignerTargetingSdkVersion(result, 2616 AndroidSdkVersion.T).getSigningCertificateLineage(), 2617 new String[]{FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2618 SECOND_RSA_2048_SIGNER_RESOURCE_NAME}, 2619 new SignerCapabilities[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); 2620 assertLineageContainsExpectedSignersWithCapabilities(getV31SignerTargetingSdkVersion(result, 2621 AndroidSdkVersion.U).getSigningCertificateLineage(), 2622 new String[]{FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2623 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME}, 2624 new SignerCapabilities[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES, 2625 DEFAULT_CAPABILITIES}); 2626 assertLineageContainsExpectedSignersWithCapabilities(getV31SignerTargetingSdkVersion(result, 2627 10000).getSigningCertificateLineage(), 2628 new String[]{FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 2629 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME}, 2630 new SignerCapabilities[]{NO_CAPABILITIES, DEFAULT_CAPABILITIES, 2631 DEFAULT_CAPABILITIES}); 2632 } 2633 2634 @Test testV31_oneTargetedSigningConfigP_targetsP()2635 public void testV31_oneTargetedSigningConfigP_targetsP() throws Exception { 2636 // A single signing config can be specified targeting < T; this test verifies a single 2637 // config targeting P is written to the V3.0 signing block 2638 SigningCertificateLineage lineage = 2639 Resources.toSigningCertificateLineage( 2640 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2641 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 2642 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2643 ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources( 2644 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.P, lineage); 2645 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, targetedSigner); 2646 2647 File signedApk = sign("original.apk", 2648 new ApkSigner.Builder(signerConfigs) 2649 .setV1SigningEnabled(true) 2650 .setV2SigningEnabled(true) 2651 .setV3SigningEnabled(true) 2652 .setV4SigningEnabled(false)); 2653 ApkVerifier.Result result = verify(signedApk, null); 2654 2655 assertVerified(result); 2656 assertVerificationWarning(result, null); 2657 assertTrue(result.isVerifiedUsingV3Scheme()); 2658 assertFalse(result.isVerifiedUsingV31Scheme()); 2659 assertEquals(1, result.getV3SchemeSigners().size()); 2660 assertEquals(AndroidSdkVersion.P, result.getV3SchemeSigners().get(0).getMinSdkVersion()); 2661 assertLineageContainsExpectedSigners( 2662 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), 2663 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2664 } 2665 2666 @Test testV31_oneTargetedSigningConfigS_targetsP()2667 public void testV31_oneTargetedSigningConfigS_targetsP() throws Exception { 2668 // A single signing config can be specified targeting < T, but the V3.0 signature scheme 2669 // does not have verified SDK targeting. If a signing config is specified to target < T and 2670 // > P, the targeted SDK version should be set to P to ensure it applies on all platform 2671 // releases that support the V3.0 signature scheme. 2672 SigningCertificateLineage lineage = 2673 Resources.toSigningCertificateLineage( 2674 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2675 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 2676 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2677 ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources( 2678 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.S, lineage); 2679 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, targetedSigner); 2680 2681 File signedApk = sign("original.apk", 2682 new ApkSigner.Builder(signerConfigs) 2683 .setV1SigningEnabled(true) 2684 .setV2SigningEnabled(true) 2685 .setV3SigningEnabled(true) 2686 .setV4SigningEnabled(false)); 2687 ApkVerifier.Result result = verify(signedApk, null); 2688 2689 assertVerified(result); 2690 assertVerificationWarning(result, null); 2691 assertTrue(result.isVerifiedUsingV3Scheme()); 2692 assertFalse(result.isVerifiedUsingV31Scheme()); 2693 assertEquals(1, result.getV3SchemeSigners().size()); 2694 assertEquals(AndroidSdkVersion.P, result.getV3SchemeSigners().get(0).getMinSdkVersion()); 2695 assertLineageContainsExpectedSigners( 2696 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), 2697 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2698 } 2699 2700 @Test testV31_twoTargetedSigningConfigsTargetT_throwsException()2701 public void testV31_twoTargetedSigningConfigsTargetT_throwsException() throws Exception { 2702 // The V3.1 signature scheme does not support multiple targeted signers targeting the same 2703 // SDK version; this test ensures an Exception is thrown if the caller specifies multiple 2704 // signers targeting the same release. 2705 SigningCertificateLineage lineageTargetT = 2706 Resources.toSigningCertificateLineage( 2707 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2708 SigningCertificateLineage secondLineageTargetT = 2709 Resources.toSigningCertificateLineage( 2710 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 2711 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 2712 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2713 ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( 2714 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); 2715 ApkSigner.SignerConfig secondSignerTargetT = getDefaultSignerConfigFromResources( 2716 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, 2717 secondLineageTargetT); 2718 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT, 2719 secondSignerTargetT); 2720 2721 assertThrows(IllegalStateException.class, () -> sign("original.apk", 2722 new ApkSigner.Builder(signerConfigs) 2723 .setV1SigningEnabled(true) 2724 .setV2SigningEnabled(true) 2725 .setV3SigningEnabled(true) 2726 .setV4SigningEnabled(false))); 2727 } 2728 2729 @Test testV31_oneTargetedSignerUAndDefaultRotationMinSdkVersion_multipleV31Signers()2730 public void testV31_oneTargetedSignerUAndDefaultRotationMinSdkVersion_multipleV31Signers() 2731 throws Exception { 2732 // SDK targeted signing configs can be specified alongside the rotation-min-sdk-version 2733 // for the initial rotation. This test verifies when the initial rotation is specified with 2734 // the default value for rotation-min-sdk-version and a separate signing config targeting U, 2735 // the two signing configs are written as separate V3.1 signatures. 2736 SigningCertificateLineage lineage = 2737 Resources.toSigningCertificateLineage( 2738 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2739 SigningCertificateLineage lineageTargetU = 2740 Resources.toSigningCertificateLineage( 2741 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 2742 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 2743 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2744 ApkSigner.SignerConfig rotatedSigner = getDefaultSignerConfigFromResources( 2745 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2746 ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( 2747 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, 2748 lineageTargetU); 2749 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, rotatedSigner, 2750 signerTargetU); 2751 2752 File signedApk = sign("original.apk", 2753 new ApkSigner.Builder(signerConfigs) 2754 .setV1SigningEnabled(true) 2755 .setV2SigningEnabled(true) 2756 .setV3SigningEnabled(true) 2757 .setV4SigningEnabled(false) 2758 .setSigningCertificateLineage(lineage)); 2759 ApkVerifier.Result result = verify(signedApk, null); 2760 2761 assertVerified(result); 2762 assertVerificationWarning(result, null); 2763 assertTrue(result.isVerifiedUsingV3Scheme()); 2764 assertTrue(result.isVerifiedUsingV31Scheme()); 2765 assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); 2766 assertEquals(2, result.getV31SchemeSigners().size()); 2767 assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2768 AndroidSdkVersion.T); 2769 assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 2770 AndroidSdkVersion.U); 2771 assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, 2772 AndroidSdkVersion.T).getSigningCertificateLineage(), 2773 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2774 assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, 2775 AndroidSdkVersion.U).getSigningCertificateLineage(), 2776 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2777 THIRD_RSA_2048_SIGNER_RESOURCE_NAME); 2778 } 2779 2780 @Test testV31_oneTargetedSignerSAndRotationMinSdkVersionP_throwsException()2781 public void testV31_oneTargetedSignerSAndRotationMinSdkVersionP_throwsException() 2782 throws Exception { 2783 // Since the v3.0 does not have verified targeted signing configs, any targeted SDK < T 2784 // will target P. If a signing config targets < T and the rotation-min-sdk-version targets 2785 // < T, then an exception should be thrown to prevent both signers from targeting P. 2786 SigningCertificateLineage lineage = 2787 Resources.toSigningCertificateLineage( 2788 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2789 SigningCertificateLineage lineageTargetS = 2790 Resources.toSigningCertificateLineage( 2791 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 2792 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 2793 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2794 ApkSigner.SignerConfig rotatedSigner = getDefaultSignerConfigFromResources( 2795 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2796 ApkSigner.SignerConfig signerTargetS = getDefaultSignerConfigFromResources( 2797 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.S, 2798 lineageTargetS); 2799 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, rotatedSigner, 2800 signerTargetS); 2801 2802 assertThrows( 2803 IllegalStateException.class, 2804 () -> 2805 sign( 2806 "original.apk", 2807 new ApkSigner.Builder(signerConfigs) 2808 .setV1SigningEnabled(true) 2809 .setV2SigningEnabled(true) 2810 .setV3SigningEnabled(true) 2811 .setV4SigningEnabled(false) 2812 .setSigningCertificateLineage(lineage) 2813 .setMinSdkVersionForRotation(AndroidSdkVersion.P))); 2814 } 2815 2816 @Test testV31_twoTargetedSignerPAndS_throwsException()2817 public void testV31_twoTargetedSignerPAndS_throwsException() throws Exception { 2818 // Since the v3.0 does not have verified targeted signing configs, any targeted SDK < T 2819 // will target P. If two signing configs target < T, then an exception should be thrown to 2820 // prevent both signers from targeting P. 2821 SigningCertificateLineage lineageTargetP = 2822 Resources.toSigningCertificateLineage( 2823 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2824 SigningCertificateLineage lineageTargetS = 2825 Resources.toSigningCertificateLineage( 2826 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 2827 ApkSigner.SignerConfig originalSigner = 2828 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2829 ApkSigner.SignerConfig signerTargetP = 2830 getDefaultSignerConfigFromResources( 2831 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2832 false, 2833 AndroidSdkVersion.P, 2834 lineageTargetP); 2835 ApkSigner.SignerConfig signerTargetS = getDefaultSignerConfigFromResources( 2836 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.S, lineageTargetS); 2837 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetP, 2838 signerTargetS); 2839 2840 assertThrows(IllegalStateException.class, () -> sign("original.apk", 2841 new ApkSigner.Builder(signerConfigs) 2842 .setV1SigningEnabled(true) 2843 .setV2SigningEnabled(true) 2844 .setV3SigningEnabled(true) 2845 .setV4SigningEnabled(false))); 2846 } 2847 2848 @Test testV31_oneTargetedSignerTAndRotationMinSdkVersionP_rotationInV3andV31()2849 public void testV31_oneTargetedSignerTAndRotationMinSdkVersionP_rotationInV3andV31() 2850 throws Exception { 2851 // An initial rotation could target P with a separate signing config targeting T+; this 2852 // test verifies a rotation-min-sdk-version < T and a signing config targeting T results 2853 // in the initial rotation being written to the V3 signing block and the targeted signing 2854 // config written to the V3.1 block. 2855 SigningCertificateLineage lineage = 2856 Resources.toSigningCertificateLineage( 2857 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2858 SigningCertificateLineage lineageTargetT = 2859 Resources.toSigningCertificateLineage( 2860 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 2861 ApkSigner.SignerConfig originalSigner = 2862 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 2863 ApkSigner.SignerConfig rotatedSigner = 2864 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2865 ApkSigner.SignerConfig signerTargetT = 2866 getDefaultSignerConfigFromResources( 2867 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 2868 false, 2869 AndroidSdkVersion.T, 2870 lineageTargetT); 2871 List<ApkSigner.SignerConfig> signerConfigs = 2872 Arrays.asList(originalSigner, rotatedSigner, signerTargetT); 2873 2874 File signedApk = 2875 sign( 2876 "original.apk", 2877 new ApkSigner.Builder(signerConfigs) 2878 .setV1SigningEnabled(true) 2879 .setV2SigningEnabled(true) 2880 .setV3SigningEnabled(true) 2881 .setV4SigningEnabled(false) 2882 .setSigningCertificateLineage(lineage) 2883 .setMinSdkVersionForRotation(AndroidSdkVersion.P)); 2884 ApkVerifier.Result result = verify(signedApk, null); 2885 2886 assertVerified(result); 2887 assertVerificationWarning(result, null); 2888 assertTrue(result.isVerifiedUsingV3Scheme()); 2889 assertTrue(result.isVerifiedUsingV31Scheme()); 2890 assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); 2891 assertEquals(1, result.getV31SchemeSigners().size()); 2892 assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 2893 AndroidSdkVersion.T); 2894 assertLineageContainsExpectedSigners( 2895 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), 2896 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2897 assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, 2898 AndroidSdkVersion.T).getSigningCertificateLineage(), 2899 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2900 THIRD_RSA_2048_SIGNER_RESOURCE_NAME); 2901 } 2902 2903 @Test testV31_oneTargetedSignerTApkMinSdkT_oneV3Signer()2904 public void testV31_oneTargetedSignerTApkMinSdkT_oneV3Signer() 2905 throws Exception { 2906 // The V3.1 signature scheme was introduced in SDK version 33; an APK with 33 as its 2907 // minSdkVersion can only be installed on devices with v3.1 support. However the V3.1 2908 // signature scheme should only be used if there's a separate signing config in the V3.0 2909 // block. This test verifies a single signing config targeting an APK's minSdkVersion of 2910 // 33 is written to the V3.0 block. 2911 SigningCertificateLineage lineageTargetT = 2912 Resources.toSigningCertificateLineage( 2913 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2914 ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( 2915 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, 2916 lineageTargetT); 2917 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetT); 2918 2919 File signedApk = sign("original-minSdk33.apk", 2920 new ApkSigner.Builder(signerConfigs) 2921 .setV1SigningEnabled(false) 2922 .setV2SigningEnabled(false) 2923 .setV3SigningEnabled(true) 2924 .setV4SigningEnabled(false)); 2925 ApkVerifier.Result result = verify(signedApk, null); 2926 2927 assertVerified(result); 2928 assertVerificationWarning(result, null); 2929 assertTrue(result.isVerifiedUsingV3Scheme()); 2930 assertFalse(result.isVerifiedUsingV31Scheme()); 2931 assertLineageContainsExpectedSigners( 2932 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), 2933 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 2934 } 2935 2936 @Test testV31_oneTargetedSignerTApkMinSdkSv2_throwsException()2937 public void testV31_oneTargetedSignerTApkMinSdkSv2_throwsException() 2938 throws Exception { 2939 // When a signing config targeting T+ is specified for an APK with a minSdkVersion < T, 2940 // the original signer (or another config targeting the minSdkVersion), must be specified 2941 // to ensure the APK can be installed on all supported platform releases. If a signer is 2942 // not provided for the minimum SDK version, then an Exception should be thrown. 2943 SigningCertificateLineage lineageTargetT = 2944 Resources.toSigningCertificateLineage( 2945 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2946 ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( 2947 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, 2948 lineageTargetT); 2949 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetT); 2950 2951 assertThrows(IllegalArgumentException.class, () -> sign("original-minSdk32.apk", 2952 new ApkSigner.Builder(signerConfigs) 2953 .setV1SigningEnabled(false) 2954 .setV2SigningEnabled(false) 2955 .setV3SigningEnabled(true) 2956 .setV4SigningEnabled(false))); 2957 } 2958 2959 @Test testV31_twoTargetedSignersSv2AndTApkMinSdkSv2_v3AndV31Signed()2960 public void testV31_twoTargetedSignersSv2AndTApkMinSdkSv2_v3AndV31Signed() 2961 throws Exception { 2962 // V3.0 does not support verified SDK targeting, so a signing config targeting SDK > P and 2963 // < T will be applied to P in the V3.0 signing block. If an app's minSdkVersion > P, then 2964 // the app should still successfully sign and verify with one of the signers targeting the 2965 // APK's minSdkVersion. 2966 SigningCertificateLineage lineageTargetSv2 = 2967 Resources.toSigningCertificateLineage( 2968 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 2969 SigningCertificateLineage lineageTargetT = 2970 Resources.toSigningCertificateLineage( 2971 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 2972 ApkSigner.SignerConfig signerTargetSv2 = 2973 getDefaultSignerConfigFromResources( 2974 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 2975 false, 2976 AndroidSdkVersion.Sv2, 2977 lineageTargetSv2); 2978 ApkSigner.SignerConfig signerTargetT = 2979 getDefaultSignerConfigFromResources( 2980 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 2981 false, 2982 AndroidSdkVersion.T, 2983 lineageTargetT); 2984 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetSv2, signerTargetT); 2985 2986 File signedApk = 2987 sign( 2988 "original-minSdk32.apk", 2989 new ApkSigner.Builder(signerConfigs) 2990 .setV1SigningEnabled(false) 2991 .setV2SigningEnabled(false) 2992 .setV3SigningEnabled(true) 2993 .setV4SigningEnabled(false)); 2994 ApkVerifier.Result result = verify(signedApk, null); 2995 2996 assertVerified(result); 2997 assertVerificationWarning(result, null); 2998 assertTrue(result.isVerifiedUsingV3Scheme()); 2999 assertTrue(result.isVerifiedUsingV31Scheme()); 3000 assertEquals(1, result.getV31SchemeSigners().size()); 3001 assertV31SignerTargetsMinApiLevel( 3002 result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); 3003 assertLineageContainsExpectedSigners( 3004 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), 3005 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 3006 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 3007 assertLineageContainsExpectedSigners( 3008 getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.T) 3009 .getSigningCertificateLineage(), 3010 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 3011 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 3012 THIRD_RSA_2048_SIGNER_RESOURCE_NAME); 3013 } 3014 3015 @Test testV31_twoTargetedSignersTAndUApkMinSdkT_v3AndV31Signed()3016 public void testV31_twoTargetedSignersTAndUApkMinSdkT_v3AndV31Signed() 3017 throws Exception { 3018 // A V3.0 block is always required before a V3.1 block can be written to the APK's signing 3019 // block. If an APK targets T (the first release with support for V3.1), and has two 3020 // targeted signers, the signer targeting T should be written to the V3.0 block and the 3021 // signer targeting a later release should be written to the V3.1 block. 3022 SigningCertificateLineage lineageTargetT = 3023 Resources.toSigningCertificateLineage( 3024 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 3025 SigningCertificateLineage lineageTargetU = 3026 Resources.toSigningCertificateLineage( 3027 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 3028 ApkSigner.SignerConfig signerTargetT = 3029 getDefaultSignerConfigFromResources( 3030 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 3031 false, 3032 AndroidSdkVersion.T, 3033 lineageTargetT); 3034 ApkSigner.SignerConfig signerTargetU = 3035 getDefaultSignerConfigFromResources( 3036 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 3037 false, 3038 AndroidSdkVersion.U, 3039 lineageTargetU); 3040 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetT, signerTargetU); 3041 3042 File signedApk = 3043 sign( 3044 "original-minSdk33.apk", 3045 new ApkSigner.Builder(signerConfigs) 3046 .setV1SigningEnabled(false) 3047 .setV2SigningEnabled(false) 3048 .setV3SigningEnabled(true) 3049 .setV4SigningEnabled(false)); 3050 ApkVerifier.Result result = verify(signedApk, null); 3051 3052 assertVerified(result); 3053 assertVerificationWarning(result, null); 3054 assertTrue(result.isVerifiedUsingV3Scheme()); 3055 assertTrue(result.isVerifiedUsingV31Scheme()); 3056 assertEquals(1, result.getV3SchemeSigners().size()); 3057 assertEquals(1, result.getV31SchemeSigners().size()); 3058 assertV31SignerTargetsMinApiLevel( 3059 result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.U); 3060 assertLineageContainsExpectedSigners( 3061 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), 3062 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 3063 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 3064 assertLineageContainsExpectedSigners( 3065 getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.U) 3066 .getSigningCertificateLineage(), 3067 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 3068 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 3069 THIRD_RSA_2048_SIGNER_RESOURCE_NAME); 3070 } 3071 3072 @Test testV31_twoTargetedSignersTAndUWithTruncatedLineage_v3AndV31Signed()3073 public void testV31_twoTargetedSignersTAndUWithTruncatedLineage_v3AndV31Signed() 3074 throws Exception { 3075 // The V3.1 signature scheme allows different lineages to be specified for each targeted 3076 // signing config as long as all the lineages can be merged to form a common lineage. A 3077 // signing lineage with signers A -> B -> C could be truncated to only signer C in a 3078 // targeted signing config. 3079 SigningCertificateLineage lineage = 3080 Resources.toSigningCertificateLineage( 3081 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 3082 ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( 3083 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineage); 3084 // Manually instantiate this signer instance to make use of the Builder's setMinSdkVersion. 3085 ApkSigner.SignerConfig signerTargetU = 3086 new ApkSigner.SignerConfig.Builder( 3087 signerTargetT.getName(), 3088 signerTargetT.getKeyConfig(), 3089 signerTargetT.getCertificates()) 3090 .setMinSdkVersion(AndroidSdkVersion.U) 3091 .build(); 3092 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetT, signerTargetU); 3093 3094 File signedApk = sign("original-minSdk33.apk", 3095 new ApkSigner.Builder(signerConfigs) 3096 .setV1SigningEnabled(false) 3097 .setV2SigningEnabled(false) 3098 .setV3SigningEnabled(true) 3099 .setV4SigningEnabled(false)); 3100 ApkVerifier.Result result = verify(signedApk, null); 3101 3102 assertVerified(result); 3103 assertVerificationWarning(result, null); 3104 assertTrue(result.isVerifiedUsingV3Scheme()); 3105 assertTrue(result.isVerifiedUsingV31Scheme()); 3106 assertEquals(1, result.getV3SchemeSigners().size()); 3107 assertEquals(1, result.getV31SchemeSigners().size()); 3108 assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 3109 AndroidSdkVersion.U); 3110 assertLineageContainsExpectedSigners( 3111 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), 3112 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 3113 THIRD_RSA_2048_SIGNER_RESOURCE_NAME); 3114 assertNull(getV31SignerTargetingSdkVersion(result, 3115 AndroidSdkVersion.U).getSigningCertificateLineage()); 3116 } 3117 3118 @Test testV31_twoTargetedSignersTAndUWithSignerNotInLineage_throwsException()3119 public void testV31_twoTargetedSignersTAndUWithSignerNotInLineage_throwsException() 3120 throws Exception { 3121 // While the V3.1 signature scheme allows a targeted signing config to omit a lineage, 3122 // this can only be used if a previous targeted signer has specified a lineage that 3123 // includes the new signer without a lineage. If an independent signer is specified 3124 // that is not in the common lineage, an Exception should be thrown. 3125 SigningCertificateLineage lineage = 3126 Resources.toSigningCertificateLineage( 3127 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 3128 ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( 3129 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineage); 3130 ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( 3131 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, null); 3132 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetT, signerTargetU); 3133 3134 assertThrows(IllegalStateException.class, () -> sign("original-minSdk33.apk", 3135 new ApkSigner.Builder(signerConfigs) 3136 .setV1SigningEnabled(false) 3137 .setV2SigningEnabled(false) 3138 .setV3SigningEnabled(true) 3139 .setV4SigningEnabled(false))); 3140 } 3141 3142 @Test testV31_twoTargetedSignersSeparateLineages_throwsException()3143 public void testV31_twoTargetedSignersSeparateLineages_throwsException() throws Exception { 3144 // When multiple SDK targeted signers are specified, the lineage for each signer must 3145 // be part of a common lineage; if any of the targeted signers has a lineage that diverges 3146 // from the common lineage, then an Exception should be thrown. 3147 SigningCertificateLineage lineageTargetT = 3148 Resources.toSigningCertificateLineage( 3149 ApkSignerTest.class, LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME); 3150 SigningCertificateLineage lineageTargetU = 3151 Resources.toSigningCertificateLineage( 3152 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 3153 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 3154 EC_P256_SIGNER_RESOURCE_NAME); 3155 ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( 3156 EC_P256_2_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); 3157 ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( 3158 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU); 3159 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT, 3160 signerTargetU); 3161 3162 assertThrows(IllegalStateException.class, () -> sign("original.apk", 3163 new ApkSigner.Builder(signerConfigs) 3164 .setV1SigningEnabled(false) 3165 .setV2SigningEnabled(false) 3166 .setV3SigningEnabled(true) 3167 .setV4SigningEnabled(false))); 3168 } 3169 3170 @Test testV31_targetedSignerTAndRotationMinSdkVersionPSeparateLineages_throwsException()3171 public void testV31_targetedSignerTAndRotationMinSdkVersionPSeparateLineages_throwsException() 3172 throws Exception { 3173 // When one or more SDK targeted signers are specified with the initial rotation using 3174 // rotation-min-sdk-version, the lineage for each signer must be part of a common lineage; 3175 // if any of the targeted signers has a lineage that diverges from the common lineage, 3176 // then an Exception should be thrown. 3177 SigningCertificateLineage lineage = 3178 Resources.toSigningCertificateLineage( 3179 ApkSignerTest.class, LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME); 3180 SigningCertificateLineage lineageTargetT = 3181 Resources.toSigningCertificateLineage( 3182 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 3183 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 3184 EC_P256_SIGNER_RESOURCE_NAME); 3185 ApkSigner.SignerConfig rotatedSigner = getDefaultSignerConfigFromResources( 3186 EC_P256_2_SIGNER_RESOURCE_NAME); 3187 ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( 3188 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetT); 3189 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, rotatedSigner, 3190 signerTargetT); 3191 3192 assertThrows(IllegalStateException.class, () -> sign("original.apk", 3193 new ApkSigner.Builder(signerConfigs) 3194 .setV1SigningEnabled(false) 3195 .setV2SigningEnabled(false) 3196 .setV3SigningEnabled(true) 3197 .setV4SigningEnabled(false) 3198 .setSigningCertificateLineage(lineage) 3199 .setMinSdkVersionForRotation(AndroidSdkVersion.P))); 3200 } 3201 3202 @Test testV31_targetedSignerWithSignerNotInLineage_throwsException()3203 public void testV31_targetedSignerWithSignerNotInLineage_throwsException() 3204 throws Exception { 3205 // When a targeted signer is created with a lineage, the signer must be in the provided 3206 // lineage otherwise an Exception should be thrown. 3207 SigningCertificateLineage lineageTargetT = 3208 Resources.toSigningCertificateLineage( 3209 ApkSignerTest.class, LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME); 3210 3211 assertThrows(IllegalArgumentException.class, () -> 3212 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, 3213 AndroidSdkVersion.T, lineageTargetT)); 3214 } 3215 3216 @Test testV31_targetedSignerTCertNotLastInLineage_truncatesLineage()3217 public void testV31_targetedSignerTCertNotLastInLineage_truncatesLineage() throws Exception { 3218 // Previously when a rotation signing config was provided with a lineage that did not 3219 // contain the signer as the last node, the lineage was truncated to the signer's position. 3220 // This test verifies a targeted signing config specified with a lineage containing signers 3221 // later than the current signer will be truncated to the provided signer. 3222 SigningCertificateLineage lineageTargetT = 3223 Resources.toSigningCertificateLineage( 3224 ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); 3225 ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( 3226 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); 3227 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 3228 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 3229 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT); 3230 3231 File signedApk = sign("original.apk", 3232 new ApkSigner.Builder(signerConfigs) 3233 .setV1SigningEnabled(true) 3234 .setV2SigningEnabled(true) 3235 .setV3SigningEnabled(true) 3236 .setV4SigningEnabled(false)); 3237 ApkVerifier.Result result = verify(signedApk, null); 3238 3239 assertVerified(result); 3240 assertVerificationWarning(result, null); 3241 assertTrue(result.isVerifiedUsingV3Scheme()); 3242 assertTrue(result.isVerifiedUsingV31Scheme()); 3243 assertEquals(1, result.getV3SchemeSigners().size()); 3244 assertEquals(1, result.getV31SchemeSigners().size()); 3245 assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 3246 AndroidSdkVersion.T); 3247 assertLineageContainsExpectedSigners( 3248 result.getV31SchemeSigners().get(0).getSigningCertificateLineage(), 3249 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 3250 } 3251 3252 @Test testV31_targetedSignerTAndUSubLineages_signsWithExpectedLineages()3253 public void testV31_targetedSignerTAndUSubLineages_signsWithExpectedLineages() 3254 throws Exception { 3255 // Since the V3.1 signature scheme supports targeted signing configs with separate lineages 3256 // as long as the lineages can be merged into a common lineage, this test verifies two 3257 // targeted signing configs with lineages A -> B and B -> C can be used to sign an APK 3258 // and that each signer from a verification has the expected lineage. 3259 SigningCertificateLineage lineageTargetT = 3260 Resources.toSigningCertificateLineage( 3261 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 3262 SigningCertificateLineage lineageTargetU = 3263 Resources.toSigningCertificateLineage( 3264 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_2_3_RESOURCE_NAME); 3265 ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( 3266 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); 3267 ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( 3268 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU); 3269 ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( 3270 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 3271 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT, 3272 signerTargetU); 3273 3274 File signedApk = sign("original.apk", 3275 new ApkSigner.Builder(signerConfigs) 3276 .setV1SigningEnabled(true) 3277 .setV2SigningEnabled(true) 3278 .setV3SigningEnabled(true) 3279 .setV4SigningEnabled(false)); 3280 ApkVerifier.Result result = verify(signedApk, null); 3281 3282 assertVerified(result); 3283 assertVerificationWarning(result, null); 3284 assertTrue(result.isVerifiedUsingV3Scheme()); 3285 assertTrue(result.isVerifiedUsingV31Scheme()); 3286 assertEquals(1, result.getV3SchemeSigners().size()); 3287 assertEquals(2, result.getV31SchemeSigners().size()); 3288 assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 3289 AndroidSdkVersion.T); 3290 assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 3291 AndroidSdkVersion.U); 3292 assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, 3293 AndroidSdkVersion.T).getSigningCertificateLineage(), 3294 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 3295 assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, 3296 AndroidSdkVersion.U).getSigningCertificateLineage(), 3297 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); 3298 assertLineageContainsExpectedSigners(result.getSigningCertificateLineage(), 3299 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 3300 THIRD_RSA_2048_SIGNER_RESOURCE_NAME); 3301 } 3302 3303 @Test testV31_targetedSignerPNoOriginalSigner_throwsException()3304 public void testV31_targetedSignerPNoOriginalSigner_throwsException() throws Exception { 3305 // Targeted signing configs can only target Android P and later since this was the initial 3306 // release that added support for V3. This test verifies if a signing config with a lineage 3307 // targeting P is provided without an original signer, an Exception is thrown to indicate 3308 // the original signer is required for the V1 and V2 signature schemes. 3309 SigningCertificateLineage lineageTargetP = 3310 Resources.toSigningCertificateLineage( 3311 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 3312 ApkSigner.SignerConfig signerTargetP = getDefaultSignerConfigFromResources( 3313 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.P, lineageTargetP); 3314 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetP); 3315 3316 assertThrows(IllegalArgumentException.class, () -> sign("original.apk", 3317 new ApkSigner.Builder(signerConfigs) 3318 .setV1SigningEnabled(true) 3319 .setV2SigningEnabled(true) 3320 .setV3SigningEnabled(true) 3321 .setV4SigningEnabled(false))); 3322 } 3323 3324 @Test testV31_targetedSignerPOriginalSigner_signed()3325 public void testV31_targetedSignerPOriginalSigner_signed() throws Exception { 3326 // While SDK targeted signing configs are intended to target later platform releases for 3327 // rotation, it is possible for a signer to target P with the original signing key. Without 3328 // a lineage, the signer will treat this as the original signing key and can use it to sign 3329 // the V1 and V2 blocks as well. 3330 ApkSigner.SignerConfig signerTargetP = getDefaultSignerConfigFromResources( 3331 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.P, null); 3332 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetP); 3333 3334 File signedApk = sign("original.apk", 3335 new ApkSigner.Builder(signerConfigs) 3336 .setV1SigningEnabled(true) 3337 .setV2SigningEnabled(true) 3338 .setV3SigningEnabled(true) 3339 .setV4SigningEnabled(false)); 3340 ApkVerifier.Result result = verify(signedApk, null); 3341 3342 assertVerified(result); 3343 assertVerificationWarning(result, null); 3344 assertTrue(result.isVerifiedUsingV3Scheme()); 3345 assertFalse(result.isVerifiedUsingV31Scheme()); 3346 } 3347 3348 @Test testV4_rotationMinSdkVersionLessThanT_signatureOnlyHasRotatedSigner()3349 public void testV4_rotationMinSdkVersionLessThanT_signatureOnlyHasRotatedSigner() 3350 throws Exception { 3351 // To support SDK version targeting in the v3.1 signature scheme, apksig added a 3352 // rotation-min-sdk-version option to allow the caller to specify the level from which 3353 // the rotated signer should be used. A value less than T should result in a single 3354 // rotated signer in the V3 block (along with the corresponding lineage), and the V4 3355 // signature should use this signer. 3356 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 3357 Arrays.asList( 3358 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 3359 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 3360 SigningCertificateLineage lineage = 3361 Resources.toSigningCertificateLineage( 3362 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 3363 3364 File signedApk = sign("original.apk", 3365 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 3366 .setV1SigningEnabled(true) 3367 .setV2SigningEnabled(true) 3368 .setV3SigningEnabled(true) 3369 .setV4SigningEnabled(true) 3370 .setMinSdkVersionForRotation(AndroidSdkVersion.P) 3371 .setSigningCertificateLineage(lineage)); 3372 ApkVerifier.Result result = verify(signedApk, null); 3373 3374 assertResultContainsV4Signers(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 3375 } 3376 3377 @Test testV4_rotationMinSdkVersionT_signatureHasOrigAndRotatedKey()3378 public void testV4_rotationMinSdkVersionT_signatureHasOrigAndRotatedKey() throws Exception { 3379 // When an APK is signed with a rotated key and the rotation-min-sdk-version X is set to T+, 3380 // a V3.1 block will be signed with the rotated signing key targeting X and later, and 3381 // a V3.0 block will be signed with the original signing key targeting P - X-1. The 3382 // V4 signature should contain both the original signing key and the rotated signing 3383 // key; this ensures if an APK is installed on a device running an SDK version less than X, 3384 // the V4 signature will be verified using the original signing key which will be the only 3385 // signing key visible to the platform. 3386 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 3387 Arrays.asList( 3388 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 3389 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 3390 SigningCertificateLineage lineage = 3391 Resources.toSigningCertificateLineage( 3392 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 3393 3394 File signedApk = sign("original.apk", 3395 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 3396 .setV1SigningEnabled(true) 3397 .setV2SigningEnabled(true) 3398 .setV3SigningEnabled(true) 3399 .setV4SigningEnabled(true) 3400 .setMinSdkVersionForRotation(AndroidSdkVersion.T) 3401 .setSigningCertificateLineage(lineage)); 3402 ApkVerifier.Result result = verify(signedApk, null); 3403 3404 assertResultContainsV4Signers(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 3405 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 3406 } 3407 3408 @Test testV41_rotationWithDifferentDigestAlgos_v41UsesCorrectDigest()3409 public void testV41_rotationWithDifferentDigestAlgos_v41UsesCorrectDigest() throws Exception { 3410 // When signing an APK, the digest algorithm is determined by the number of bits in the 3411 // signing key to ensure the digest is not weaker than the key. If an original signing key 3412 // meets the requirements for the CHUNKED_SHA256 digest and the rotated signing key 3413 // meets the requirements for CHUNKED_SHA512, then the v3.0 and v3.1 signing blocks will 3414 // use different digests. The v4.1 signature must use the content digest from the v3.1 3415 // block since that's the digest that will be used to verify the v4.1 signature on all 3416 // platform versions that support the v3.1 signer. 3417 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = 3418 Arrays.asList( 3419 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 3420 getDefaultSignerConfigFromResources(FIRST_RSA_4096_SIGNER_RESOURCE_NAME)); 3421 SigningCertificateLineage lineage = 3422 Resources.toSigningCertificateLineage( 3423 ApkSignerTest.class, LINEAGE_RSA_2048_TO_RSA_4096_RESOURCE_NAME); 3424 3425 File signedApk = sign("original.apk", 3426 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 3427 .setV1SigningEnabled(true) 3428 .setV2SigningEnabled(true) 3429 .setV3SigningEnabled(true) 3430 .setV4SigningEnabled(true) 3431 .setMinSdkVersionForRotation(AndroidSdkVersion.T) 3432 .setSigningCertificateLineage(lineage)); 3433 ApkVerifier.Result result = verify(signedApk, null); 3434 3435 assertResultContainsV4Signers(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 3436 FIRST_RSA_4096_SIGNER_RESOURCE_NAME); 3437 } 3438 3439 @Test 3440 public void testSourceStampTimestamp_signWithSourceStampAndTimestampDefault_validTimestampValue()3441 testSourceStampTimestamp_signWithSourceStampAndTimestampDefault_validTimestampValue() 3442 throws Exception { 3443 // Source stamps should include a timestamp attribute with the epoch time the stamp block 3444 // was signed. This test verifies a standard signing with a source stamp includes a valid 3445 // value for the source stamp timestamp attribute by default. 3446 ApkSigner.SignerConfig rsa2048SignerConfig = getDefaultSignerConfigFromResources( 3447 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 3448 List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList( 3449 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); 3450 3451 File signedApk = sign("original.apk", 3452 new ApkSigner.Builder(ecP256SignerConfig) 3453 .setV1SigningEnabled(true) 3454 .setV2SigningEnabled(true) 3455 .setV3SigningEnabled(true) 3456 .setV4SigningEnabled(false) 3457 .setSourceStampSignerConfig(rsa2048SignerConfig)); 3458 ApkVerifier.Result result = verify(signedApk, null); 3459 3460 assertSourceStampVerified(signedApk, result); 3461 long timestamp = result.getSourceStampInfo().getTimestampEpochSeconds(); 3462 assertTrue("Invalid source stamp timestamp value: " + timestamp, timestamp > 0); 3463 } 3464 3465 @Test 3466 public void testSourceStampTimestamp_signWithSourceStampAndTimestampEnabled_validTimestampValue()3467 testSourceStampTimestamp_signWithSourceStampAndTimestampEnabled_validTimestampValue() 3468 throws Exception { 3469 // Similar to above, this test verifies a valid timestamp value is written to the 3470 // attribute when the caller explicitly requests to enable the source stamp timestamp. 3471 ApkSigner.SignerConfig rsa2048SignerConfig = getDefaultSignerConfigFromResources( 3472 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 3473 List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList( 3474 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); 3475 3476 File signedApk = sign("original.apk", 3477 new ApkSigner.Builder(ecP256SignerConfig) 3478 .setV1SigningEnabled(true) 3479 .setV2SigningEnabled(true) 3480 .setV3SigningEnabled(true) 3481 .setV4SigningEnabled(false) 3482 .setSourceStampSignerConfig(rsa2048SignerConfig) 3483 .setSourceStampTimestampEnabled(true)); 3484 ApkVerifier.Result result = verify(signedApk, null); 3485 3486 assertSourceStampVerified(signedApk, result); 3487 long timestamp = result.getSourceStampInfo().getTimestampEpochSeconds(); 3488 assertTrue("Invalid source stamp timestamp value: " + timestamp, timestamp > 0); 3489 } 3490 3491 @Test 3492 public void testSourceStampTimestamp_signWithSourceStampAndTimestampDisabled_defaultTimestampValue()3493 testSourceStampTimestamp_signWithSourceStampAndTimestampDisabled_defaultTimestampValue() 3494 throws Exception { 3495 // While source stamps should include a timestamp attribute indicating the time at which 3496 // the stamp was signed, this can cause problems for reproducible builds. The 3497 // ApkSigner.Builder#setSourceStampTimestampEnabled API allows the caller to specify 3498 // whether the timestamp attribute should be written; this test verifies no timestamp is 3499 // written to the source stamp if this API is used to disable the timestamp. 3500 ApkSigner.SignerConfig rsa2048SignerConfig = getDefaultSignerConfigFromResources( 3501 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 3502 List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList( 3503 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); 3504 3505 File signedApk = sign("original.apk", 3506 new ApkSigner.Builder(ecP256SignerConfig) 3507 .setV1SigningEnabled(true) 3508 .setV2SigningEnabled(true) 3509 .setV3SigningEnabled(true) 3510 .setV4SigningEnabled(false) 3511 .setSourceStampSignerConfig(rsa2048SignerConfig) 3512 .setSourceStampTimestampEnabled(false)); 3513 ApkVerifier.Result result = verify(signedApk, null); 3514 3515 assertSourceStampVerified(signedApk, result); 3516 long timestamp = result.getSourceStampInfo().getTimestampEpochSeconds(); 3517 assertEquals(0, timestamp); 3518 } 3519 3520 /** 3521 * Asserts the provided {@code signedApk} contains a signature block with the expected 3522 * {@code byte[]} value and block ID as specified in the {@code expectedBlock}. 3523 */ assertSigningBlockContains(File signedApk, Pair<byte[], Integer> expectedBlock)3524 private static void assertSigningBlockContains(File signedApk, 3525 Pair<byte[], Integer> expectedBlock) throws Exception { 3526 try (RandomAccessFile apkFile = new RandomAccessFile(signedApk, "r")) { 3527 ApkUtils.ApkSigningBlock apkSigningBlock = ApkUtils.findApkSigningBlock( 3528 DataSources.asDataSource(apkFile)); 3529 List<Pair<byte[], Integer>> signatureBlocks = 3530 ApkSigningBlockUtils.getApkSignatureBlocks(apkSigningBlock.getContents()); 3531 for (Pair<byte[], Integer> signatureBlock : signatureBlocks) { 3532 if (signatureBlock.getSecond().equals(expectedBlock.getSecond())) { 3533 if (Arrays.equals(signatureBlock.getFirst(), expectedBlock.getFirst())) { 3534 return; 3535 } 3536 } 3537 } 3538 fail(String.format( 3539 "The APK signing block did not contain the expected block with ID %08x", 3540 expectedBlock.getSecond())); 3541 } 3542 } 3543 3544 /** 3545 * Asserts the provided verification {@code result} contains the expected {@code signers} for 3546 * each scheme that was used to verify the APK's signature. 3547 */ assertResultContainsSigners(ApkVerifier.Result result, String... signers)3548 static void assertResultContainsSigners(ApkVerifier.Result result, String... signers) 3549 throws Exception { 3550 assertResultContainsSigners(result, false, signers); 3551 } 3552 3553 /** 3554 * Asserts the provided verification {@code result} contains the expected {@code signers} for 3555 * each scheme that was used to verify the APK's signature; if {@code rotationExpected} is set 3556 * to {@code true}, then the first element in {@code signers} is treated as the expected 3557 * original signer for any V1, V2, and V3 (where applicable) signatures, and the last element 3558 * is the rotated expected signer for V3+. 3559 */ assertResultContainsSigners(ApkVerifier.Result result, boolean rotationExpected, String... signers)3560 static void assertResultContainsSigners(ApkVerifier.Result result, 3561 boolean rotationExpected, String... signers) throws Exception { 3562 // A result must be successfully verified before verifying any of the result's signers. 3563 assertTrue(result.isVerified()); 3564 3565 List<X509Certificate> expectedSigners = new ArrayList<>(); 3566 for (String signer : signers) { 3567 ApkSigner.SignerConfig signerConfig = getDefaultSignerConfigFromResources(signer); 3568 expectedSigners.addAll(signerConfig.getCertificates()); 3569 } 3570 // If rotation is expected then the V1 and V2 signature should only be signed by the 3571 // original signer. 3572 List<X509Certificate> expectedV1Signers = 3573 rotationExpected ? List.of(expectedSigners.get(0)) : expectedSigners; 3574 List<X509Certificate> expectedV2Signers = 3575 rotationExpected ? List.of(expectedSigners.get(0)) : expectedSigners; 3576 // V3 only supports a single signer; if rotation is not expected or the V3.1 block contains 3577 // the rotated signing key then the expected V3.0 signer should be the original signer. 3578 List<X509Certificate> expectedV3Signers = 3579 !rotationExpected || result.isVerifiedUsingV31Scheme() 3580 ? List.of(expectedSigners.get(0)) 3581 : List.of(expectedSigners.get(expectedSigners.size() - 1)); 3582 3583 if (result.isVerifiedUsingV1Scheme()) { 3584 Set<X509Certificate> v1Signers = new HashSet<>(); 3585 for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) { 3586 v1Signers.add(signer.getCertificate()); 3587 } 3588 assertTrue("Expected V1 signers: " + getAllSubjectNamesFrom(expectedV1Signers) 3589 + ", actual V1 signers: " + getAllSubjectNamesFrom(v1Signers), 3590 v1Signers.containsAll(expectedV1Signers)); 3591 } 3592 3593 if (result.isVerifiedUsingV2Scheme()) { 3594 Set<X509Certificate> v2Signers = new HashSet<>(); 3595 for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) { 3596 v2Signers.add(signer.getCertificate()); 3597 } 3598 assertTrue("Expected V2 signers: " + getAllSubjectNamesFrom(expectedV2Signers) 3599 + ", actual V2 signers: " + getAllSubjectNamesFrom(v2Signers), 3600 v2Signers.containsAll(expectedV2Signers)); 3601 } 3602 3603 if (result.isVerifiedUsingV3Scheme()) { 3604 Set<X509Certificate> v3Signers = new HashSet<>(); 3605 for (V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { 3606 v3Signers.add(signer.getCertificate()); 3607 } 3608 assertTrue("Expected V3 signers: " + getAllSubjectNamesFrom(expectedV3Signers) 3609 + ", actual V3 signers: " + getAllSubjectNamesFrom(v3Signers), 3610 v3Signers.containsAll(expectedV3Signers)); 3611 } 3612 3613 if (result.isVerifiedUsingV31Scheme()) { 3614 Set<X509Certificate> v31Signers = new HashSet<>(); 3615 for (V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { 3616 v31Signers.add(signer.getCertificate()); 3617 } 3618 // V3.1 only supports specifying signatures with a rotated signing key; if a V3.1 3619 // signing block was verified then ensure it contains the expected rotated signer. 3620 List<X509Certificate> expectedV31Signers = List 3621 .of(expectedSigners.get(expectedSigners.size() - 1)); 3622 assertTrue("Expected V3.1 signers: " + getAllSubjectNamesFrom(expectedV31Signers) 3623 + ", actual V3.1 signers: " + getAllSubjectNamesFrom(v31Signers), 3624 v31Signers.containsAll(expectedV31Signers)); 3625 } 3626 } 3627 3628 /** 3629 * Asserts the provided verification {@code result} contains the expected V4 {@code signers}. 3630 */ assertResultContainsV4Signers(ApkVerifier.Result result, String... signers)3631 private static void assertResultContainsV4Signers(ApkVerifier.Result result, String... signers) 3632 throws Exception { 3633 assertTrue(result.isVerified()); 3634 assertTrue(result.isVerifiedUsingV4Scheme()); 3635 List<X509Certificate> expectedSigners = new ArrayList<>(); 3636 for (String signer : signers) { 3637 ApkSigner.SignerConfig signerConfig = getDefaultSignerConfigFromResources(signer); 3638 expectedSigners.addAll(signerConfig.getCertificates()); 3639 } 3640 List<X509Certificate> v4Signers = new ArrayList<>(); 3641 for (ApkVerifier.Result.V4SchemeSignerInfo signer : result.getV4SchemeSigners()) { 3642 v4Signers.addAll(signer.getCertificates()); 3643 } 3644 assertTrue("Expected V4 signers: " + getAllSubjectNamesFrom(expectedSigners) 3645 + ", actual V4 signers: " + getAllSubjectNamesFrom(v4Signers), 3646 v4Signers.containsAll(expectedSigners)); 3647 } 3648 3649 /** 3650 * Asserts the provided {@code result} contains the expected {@code signer} targeting 3651 * {@code minSdkVersion} as the minimum version for rotation. 3652 */ assertV31SignerTargetsMinApiLevel(ApkVerifier.Result result, String signer, int minSdkVersion)3653 static void assertV31SignerTargetsMinApiLevel(ApkVerifier.Result result, String signer, 3654 int minSdkVersion) throws Exception { 3655 assertTrue(result.isVerifiedUsingV31Scheme()); 3656 ApkSigner.SignerConfig expectedSignerConfig = getDefaultSignerConfigFromResources(signer); 3657 StringBuilder errorMessage = new StringBuilder(); 3658 3659 boolean signerTargetsDevRelease = false; 3660 if (minSdkVersion == V3SchemeConstants.DEV_RELEASE) { 3661 minSdkVersion = V3SchemeConstants.PROD_RELEASE; 3662 signerTargetsDevRelease = true; 3663 } 3664 3665 for (V3SchemeSignerInfo signerConfig : result.getV31SchemeSigners()) { 3666 if (signerConfig.getCertificates() 3667 .containsAll(expectedSignerConfig.getCertificates())) { 3668 // The V3.1 signature scheme allows the same signer to target multiple SDK versions 3669 // with different capabilities in the lineage, so save the current error message 3670 // in case no subsequent instances of this signer target the specified SDK version. 3671 if (minSdkVersion != signerConfig.getMinSdkVersion()) { 3672 if (errorMessage.length() > 0) { 3673 errorMessage.append(System.getProperty("line.separator")); 3674 } 3675 errorMessage.append( 3676 "The signer, " + getAllSubjectNamesFrom(signerConfig.getCertificates()) 3677 + ", is expected to target SDK version " + minSdkVersion 3678 + ", instead it is targeting " 3679 + signerConfig.getMinSdkVersion()); 3680 } else if (signerTargetsDevRelease 3681 && !signerConfig.getRotationTargetsDevRelease()) { 3682 if (errorMessage.length() > 0) { 3683 errorMessage.append(System.getProperty("line.separator")); 3684 } 3685 errorMessage.append( 3686 "The signer, " + getAllSubjectNamesFrom(signerConfig.getCertificates()) 3687 + ", is targeting a development release, " + minSdkVersion 3688 + ", but the attribute to target a development release is not" 3689 + " set"); 3690 } else { 3691 return; 3692 } 3693 } 3694 } 3695 fail("Did not find the expected signer, " + getAllSubjectNamesFrom( 3696 expectedSignerConfig.getCertificates()) + ": " + errorMessage); 3697 } 3698 3699 /** 3700 * Returns the V3.1 signer from the provided {@code result} targeting the specified {@code 3701 * targetSdkVersion}. 3702 */ getV31SignerTargetingSdkVersion(ApkVerifier.Result result, int targetSdkVersion)3703 private V3SchemeSignerInfo getV31SignerTargetingSdkVersion(ApkVerifier.Result result, 3704 int targetSdkVersion) throws Exception { 3705 boolean signerTargetsDevRelease = false; 3706 if (targetSdkVersion == V3SchemeConstants.DEV_RELEASE) { 3707 targetSdkVersion = V3SchemeConstants.PROD_RELEASE; 3708 signerTargetsDevRelease = true; 3709 } 3710 for (V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { 3711 if (signer.getMinSdkVersion() == targetSdkVersion) { 3712 // If a signer is targeting a development release and another signer is targeting 3713 // the most recent production release, then both could be targeting the same SDK 3714 // version. 3715 if (signerTargetsDevRelease != signer.getRotationTargetsDevRelease()) { 3716 continue; 3717 } 3718 return signer; 3719 } 3720 } 3721 fail("No V3.1 signer found targeting min SDK version " + targetSdkVersion 3722 + ", dev release: " + signerTargetsDevRelease); 3723 return null; 3724 } 3725 3726 /** 3727 * Returns a comma delimited {@code String} containing all of the Subject Names from the 3728 * provided {@code certificates}. 3729 */ getAllSubjectNamesFrom(Collection<X509Certificate> certificates)3730 private static String getAllSubjectNamesFrom(Collection<X509Certificate> certificates) { 3731 StringBuilder result = new StringBuilder(); 3732 for (X509Certificate certificate : certificates) { 3733 if (result.length() > 0) { 3734 result.append(", "); 3735 } 3736 result.append(certificate.getSubjectDN().getName()); 3737 } 3738 return result.toString(); 3739 } 3740 resourceZipFileContains(String resourceName, String zipEntryName)3741 private static boolean resourceZipFileContains(String resourceName, String zipEntryName) 3742 throws IOException { 3743 ZipInputStream zip = new ZipInputStream( 3744 Resources.toInputStream(ApkSignerTest.class, resourceName)); 3745 while (true) { 3746 ZipEntry entry = zip.getNextEntry(); 3747 if (entry == null) { 3748 break; 3749 } 3750 3751 if (entry.getName().equals(zipEntryName)) { 3752 return true; 3753 } 3754 } 3755 3756 return false; 3757 } 3758 getRSAPublicKeyFromSigningBlock(File apk, int signatureVersionId)3759 private RSAPublicKey getRSAPublicKeyFromSigningBlock(File apk, int signatureVersionId) 3760 throws Exception { 3761 int signatureVersionBlockId; 3762 switch (signatureVersionId) { 3763 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2: 3764 signatureVersionBlockId = V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID; 3765 break; 3766 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3: 3767 signatureVersionBlockId = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; 3768 break; 3769 default: 3770 throw new Exception( 3771 "Invalid signature version ID specified: " + signatureVersionId); 3772 } 3773 SignatureInfo signatureInfo = 3774 getSignatureInfoFromApk(apk, signatureVersionId, signatureVersionBlockId); 3775 // FORMAT: 3776 // * length prefixed sequence of length prefixed signers 3777 // * length-prefixed signed data 3778 // * V3+ only - minSDK (uint32) 3779 // * V3+ only - maxSDK (uint32) 3780 // * length-prefixed sequence of length-prefixed signatures: 3781 // * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded) 3782 ByteBuffer signers = 3783 ApkSigningBlockUtils.getLengthPrefixedSlice(signatureInfo.signatureBlock); 3784 ByteBuffer signer = ApkSigningBlockUtils.getLengthPrefixedSlice(signers); 3785 // Since all the data is read from the signer block the signedData and signatures are 3786 // discarded. 3787 ApkSigningBlockUtils.getLengthPrefixedSlice(signer); 3788 // For V3+ signature version IDs discard the min / max SDKs as well 3789 if (signatureVersionId >= ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3) { 3790 signer.getInt(); 3791 signer.getInt(); 3792 } 3793 ApkSigningBlockUtils.getLengthPrefixedSlice(signer); 3794 ByteBuffer publicKey = ApkSigningBlockUtils.getLengthPrefixedSlice(signer); 3795 SubjectPublicKeyInfo subjectPublicKeyInfo = 3796 Asn1BerParser.parse(publicKey, SubjectPublicKeyInfo.class); 3797 ByteBuffer subjectPublicKeyBuffer = subjectPublicKeyInfo.subjectPublicKey; 3798 // The SubjectPublicKey is stored as a bit string in the SubjectPublicKeyInfo with the first 3799 // byte indicating the number of padding bits in the public key. Read this first byte to 3800 // allow parsing the rest of the RSAPublicKey as a sequence. 3801 subjectPublicKeyBuffer.get(); 3802 return Asn1BerParser.parse(subjectPublicKeyBuffer, RSAPublicKey.class); 3803 } 3804 getSignatureInfoFromApk( File apkFile, int signatureVersionId, int signatureVersionBlockId)3805 private static SignatureInfo getSignatureInfoFromApk( 3806 File apkFile, int signatureVersionId, int signatureVersionBlockId) 3807 throws IOException, ZipFormatException, 3808 ApkSigningBlockUtils.SignatureNotFoundException { 3809 try (RandomAccessFile f = new RandomAccessFile(apkFile, "r")) { 3810 DataSource apk = DataSources.asDataSource(f, 0, f.length()); 3811 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 3812 ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( 3813 signatureVersionId); 3814 return ApkSigningBlockUtils.findSignature(apk, zipSections, signatureVersionBlockId, 3815 result); 3816 } 3817 } 3818 3819 /** 3820 * Asserts that signing the specified golden input file using the provided signing configuration 3821 * produces output identical to the specified golden output file. 3822 */ assertGolden( String inResourceName, String expectedOutResourceName, ApkSigner.Builder apkSignerBuilder)3823 private void assertGolden( 3824 String inResourceName, 3825 String expectedOutResourceName, 3826 ApkSigner.Builder apkSignerBuilder) 3827 throws Exception { 3828 // Sign the provided golden input 3829 File out = sign(inResourceName, apkSignerBuilder); 3830 assertVerified(verify(out, AndroidSdkVersion.P)); 3831 3832 // Assert that the output is identical to the provided golden output 3833 if (out.length() > Integer.MAX_VALUE) { 3834 throw new RuntimeException("Output too large: " + out.length() + " bytes"); 3835 } 3836 byte[] outData = new byte[(int) out.length()]; 3837 try (FileInputStream fis = new FileInputStream(out)) { 3838 fis.read(outData); 3839 } 3840 ByteBuffer actualOutBuf = ByteBuffer.wrap(outData); 3841 3842 ByteBuffer expectedOutBuf = 3843 ByteBuffer.wrap(Resources.toByteArray(getClass(), expectedOutResourceName)); 3844 3845 boolean identical = false; 3846 if (actualOutBuf.remaining() == expectedOutBuf.remaining()) { 3847 while (actualOutBuf.hasRemaining()) { 3848 if (actualOutBuf.get() != expectedOutBuf.get()) { 3849 break; 3850 } 3851 } 3852 identical = !actualOutBuf.hasRemaining(); 3853 } 3854 3855 if (identical) { 3856 return; 3857 } 3858 3859 if (KEEP_FAILING_OUTPUT_AS_FILES) { 3860 File tmp = File.createTempFile(getClass().getSimpleName(), ".apk"); 3861 Files.copy(out.toPath(), tmp.toPath()); 3862 fail(tmp + " differs from " + expectedOutResourceName); 3863 } else { 3864 fail("Output differs from " + expectedOutResourceName); 3865 } 3866 } 3867 sign(File inApkFile, ApkSigner.Builder apkSignerBuilder)3868 private File sign(File inApkFile, ApkSigner.Builder apkSignerBuilder) throws Exception { 3869 try (RandomAccessFile apkFile = new RandomAccessFile(inApkFile, "r")) { 3870 DataSource in = DataSources.asDataSource(apkFile); 3871 return sign(in, apkSignerBuilder); 3872 } 3873 } 3874 sign(String inResourceName, ApkSigner.Builder apkSignerBuilder)3875 private File sign(String inResourceName, ApkSigner.Builder apkSignerBuilder) throws Exception { 3876 DataSource in = 3877 DataSources.asDataSource( 3878 ByteBuffer.wrap(Resources.toByteArray(getClass(), inResourceName))); 3879 return sign(in, apkSignerBuilder); 3880 } 3881 sign(DataSource in, ApkSigner.Builder apkSignerBuilder)3882 private File sign(DataSource in, ApkSigner.Builder apkSignerBuilder) throws Exception { 3883 File outFile = mTemporaryFolder.newFile(); 3884 apkSignerBuilder.setInputApk(in).setOutputApk(outFile); 3885 3886 File outFileIdSig = new File(outFile.getCanonicalPath() + ".idsig"); 3887 apkSignerBuilder.setV4SignatureOutputFile(outFileIdSig); 3888 apkSignerBuilder.setV4ErrorReportingEnabled(true); 3889 3890 apkSignerBuilder.build().sign(); 3891 return outFile; 3892 } 3893 verifyForMinSdkVersion(File apk, int minSdkVersion)3894 private static ApkVerifier.Result verifyForMinSdkVersion(File apk, int minSdkVersion) 3895 throws IOException, ApkFormatException, NoSuchAlgorithmException { 3896 return verify(apk, minSdkVersion); 3897 } 3898 verify(File apk, Integer minSdkVersionOverride)3899 private static ApkVerifier.Result verify(File apk, Integer minSdkVersionOverride) 3900 throws IOException, ApkFormatException, NoSuchAlgorithmException { 3901 ApkVerifier.Builder builder = new ApkVerifier.Builder(apk); 3902 if (minSdkVersionOverride != null) { 3903 builder.setMinCheckedPlatformVersion(minSdkVersionOverride); 3904 } 3905 File idSig = new File(apk.getCanonicalPath() + ".idsig"); 3906 if (idSig.exists()) { 3907 builder.setV4SignatureFile(idSig); 3908 } 3909 return builder.build().verify(); 3910 } 3911 assertVerified(ApkVerifier.Result result)3912 private static void assertVerified(ApkVerifier.Result result) { 3913 ApkVerifierTest.assertVerified(result); 3914 } 3915 assertSourceStampVerified(File signedApk, ApkVerifier.Result result)3916 private static void assertSourceStampVerified(File signedApk, ApkVerifier.Result result) 3917 throws ApkSigningBlockUtils.SignatureNotFoundException, 3918 IOException, 3919 ZipFormatException { 3920 SignatureInfo signatureInfo = 3921 getSignatureInfoFromApk( 3922 signedApk, 3923 ApkSigningBlockUtils.VERSION_SOURCE_STAMP, 3924 SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID); 3925 assertNotNull(signatureInfo.signatureBlock); 3926 assertTrue(result.isSourceStampVerified()); 3927 } 3928 assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue)3929 private static void assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue) { 3930 ApkVerifierTest.assertVerificationFailure(result, expectedIssue); 3931 } 3932 assertFileContentsEqual(File first, File second)3933 private void assertFileContentsEqual(File first, File second) throws IOException { 3934 assertArrayEquals(Files.readAllBytes(Paths.get(first.getPath())), 3935 Files.readAllBytes(Paths.get(second.getPath()))); 3936 } 3937 getSignerConfigsFromResources( String... signerNames)3938 private static List<ApkSigner.SignerConfig> getSignerConfigsFromResources( 3939 String... signerNames) throws Exception { 3940 List<ApkSigner.SignerConfig> signerConfigs = new ArrayList<>(); 3941 for (String signerName : signerNames) { 3942 signerConfigs.add(getDefaultSignerConfigFromResources(signerName)); 3943 } 3944 return signerConfigs; 3945 } 3946 getDefaultSignerConfigFromResources( String keyNameInResources)3947 private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources( 3948 String keyNameInResources) throws Exception { 3949 return getDefaultSignerConfigFromResources(keyNameInResources, false); 3950 } 3951 getDefaultSignerConfigFromResources( String keyNameInResources, boolean deterministicDsaSigning)3952 private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources( 3953 String keyNameInResources, boolean deterministicDsaSigning) throws Exception { 3954 return getDefaultSignerConfigFromResources( 3955 keyNameInResources, deterministicDsaSigning, 0, null); 3956 } 3957 3958 /** 3959 * Returns a new {@link ApkSigner.SignerConfig} with the certificate and private key in 3960 * resources with the file prefix {@code keyNameInResources} targeting {@code targetSdkVersion} 3961 * with lineage {@code lineage} and using deterministic DSA signing when {@code 3962 * deterministicDsaSigning} is set to true. 3963 */ getDefaultSignerConfigFromResources( String keyNameInResources, boolean deterministicDsaSigning, int targetSdkVersion, SigningCertificateLineage lineage)3964 private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources( 3965 String keyNameInResources, 3966 boolean deterministicDsaSigning, 3967 int targetSdkVersion, 3968 SigningCertificateLineage lineage) 3969 throws Exception { 3970 PrivateKey privateKey = 3971 Resources.toPrivateKey(ApkSignerTest.class, keyNameInResources + ".pk8"); 3972 List<X509Certificate> certs = 3973 Resources.toCertificateChain(ApkSignerTest.class, keyNameInResources + ".x509.pem"); 3974 ApkSigner.SignerConfig.Builder signerConfigBuilder = 3975 new ApkSigner.SignerConfig.Builder( 3976 keyNameInResources, 3977 new KeyConfig.Jca(privateKey), 3978 certs, 3979 deterministicDsaSigning); 3980 if (targetSdkVersion > 0) { 3981 signerConfigBuilder.setLineageForMinSdkVersion(lineage, targetSdkVersion); 3982 } 3983 return signerConfigBuilder.build(); 3984 } 3985 getDefaultSignerConfigFromResources( String keyNameInResources, String certNameInResources)3986 private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources( 3987 String keyNameInResources, String certNameInResources) throws Exception { 3988 PrivateKey privateKey = 3989 Resources.toPrivateKey(ApkSignerTest.class, keyNameInResources + ".pk8"); 3990 List<X509Certificate> certs = 3991 Resources.toCertificateChain(ApkSignerTest.class, certNameInResources); 3992 return new ApkSigner.SignerConfig.Builder( 3993 keyNameInResources, new KeyConfig.Jca(privateKey), certs) 3994 .build(); 3995 } 3996 getDeterministicDsaSignerConfigFromResources( String keyNameInResources)3997 private static ApkSigner.SignerConfig getDeterministicDsaSignerConfigFromResources( 3998 String keyNameInResources) throws Exception { 3999 return getDefaultSignerConfigFromResources(keyNameInResources, true); 4000 } 4001 } 4002