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 org.junit.Assert.assertTrue; 20 import static org.junit.Assert.fail; 21 22 import com.android.apksig.ApkVerifier.Issue; 23 import com.android.apksig.apk.ApkFormatException; 24 import com.android.apksig.apk.ApkUtils; 25 import com.android.apksig.internal.apk.ApkSigningBlockUtils; 26 import com.android.apksig.internal.apk.SignatureInfo; 27 import com.android.apksig.internal.apk.v2.V2SchemeSigner; 28 import com.android.apksig.internal.apk.v3.V3SchemeSigner; 29 import com.android.apksig.internal.asn1.Asn1BerParser; 30 import com.android.apksig.internal.util.ByteBufferDataSource; 31 import com.android.apksig.internal.util.Resources; 32 import com.android.apksig.internal.x509.RSAPublicKey; 33 import com.android.apksig.internal.x509.SubjectPublicKeyInfo; 34 import com.android.apksig.util.DataSinks; 35 import com.android.apksig.util.DataSource; 36 import com.android.apksig.util.DataSources; 37 import com.android.apksig.util.ReadableDataSink; 38 import java.io.File; 39 import java.io.IOException; 40 import java.nio.ByteBuffer; 41 import java.nio.channels.ByteChannel; 42 import java.nio.file.Files; 43 import java.nio.file.StandardOpenOption; 44 import java.security.NoSuchAlgorithmException; 45 import java.security.PrivateKey; 46 import java.security.SignatureException; 47 import java.security.cert.X509Certificate; 48 import java.util.Arrays; 49 import java.util.Collections; 50 import java.util.List; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.junit.runners.JUnit4; 54 55 import java.math.BigInteger; 56 57 @RunWith(JUnit4.class) 58 public class ApkSignerTest { 59 60 /** 61 * Whether to preserve, as files, outputs of failed tests. This is useful for investigating test 62 * failures. 63 */ 64 private static final boolean KEEP_FAILING_OUTPUT_AS_FILES = false; 65 66 // All signers with the same prefix and an _X suffix were signed with the private key of the 67 // (X-1) signer. 68 private static final String FIRST_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048"; 69 private static final String SECOND_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_2"; 70 private static final String THIRD_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_3"; 71 72 // This is the same cert as above with the modulus reencoded to remove the leading 0 sign bit. 73 private static final String FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS = 74 "rsa-2048_negmod.x509.der"; 75 76 private static final String LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME = 77 "rsa-2048-lineage-2-signers"; 78 main(String[] params)79 public static void main(String[] params) throws Exception { 80 File outDir = (params.length > 0) ? new File(params[0]) : new File("."); 81 generateGoldenFiles(outDir); 82 } 83 generateGoldenFiles(File outDir)84 private static void generateGoldenFiles(File outDir) throws Exception { 85 System.out.println( 86 "Generating golden files " + ApkSignerTest.class.getSimpleName() 87 + " into " + outDir); 88 if (!outDir.mkdirs()) { 89 throw new IOException("Failed to create directory: " + outDir); 90 } 91 List<ApkSigner.SignerConfig> rsa2048SignerConfig = 92 Collections.singletonList( 93 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 94 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = Arrays.asList( 95 rsa2048SignerConfig.get(0), 96 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 97 SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( 98 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 99 100 signGolden( 101 "golden-unaligned-in.apk", 102 new File(outDir, "golden-unaligned-out.apk"), 103 new ApkSigner.Builder(rsa2048SignerConfig)); 104 signGolden( 105 "golden-legacy-aligned-in.apk", 106 new File(outDir, "golden-legacy-aligned-out.apk"), 107 new ApkSigner.Builder(rsa2048SignerConfig)); 108 signGolden( 109 "golden-aligned-in.apk", 110 new File(outDir, "golden-aligned-out.apk"), 111 new ApkSigner.Builder(rsa2048SignerConfig)); 112 113 signGolden( 114 "golden-unaligned-in.apk", 115 new File(outDir, "golden-unaligned-v1-out.apk"), 116 new ApkSigner.Builder(rsa2048SignerConfig) 117 .setV1SigningEnabled(true) 118 .setV2SigningEnabled(false) 119 .setV3SigningEnabled(false)); 120 signGolden( 121 "golden-legacy-aligned-in.apk", 122 new File(outDir, "golden-legacy-aligned-v1-out.apk"), 123 new ApkSigner.Builder(rsa2048SignerConfig) 124 .setV1SigningEnabled(true) 125 .setV2SigningEnabled(false) 126 .setV3SigningEnabled(false)); 127 signGolden( 128 "golden-aligned-in.apk", 129 new File(outDir, "golden-aligned-v1-out.apk"), 130 new ApkSigner.Builder(rsa2048SignerConfig) 131 .setV1SigningEnabled(true) 132 .setV2SigningEnabled(false) 133 .setV3SigningEnabled(false)); 134 135 signGolden( 136 "golden-unaligned-in.apk", 137 new File(outDir, "golden-unaligned-v2-out.apk"), 138 new ApkSigner.Builder(rsa2048SignerConfig) 139 .setV1SigningEnabled(false) 140 .setV2SigningEnabled(true) 141 .setV3SigningEnabled(false)); 142 signGolden( 143 "golden-legacy-aligned-in.apk", 144 new File(outDir, "golden-legacy-aligned-v2-out.apk"), 145 new ApkSigner.Builder(rsa2048SignerConfig) 146 .setV1SigningEnabled(false) 147 .setV2SigningEnabled(true) 148 .setV3SigningEnabled(false)); 149 signGolden( 150 "golden-aligned-in.apk", 151 new File(outDir, "golden-aligned-v2-out.apk"), 152 new ApkSigner.Builder(rsa2048SignerConfig) 153 .setV1SigningEnabled(false) 154 .setV2SigningEnabled(true) 155 .setV3SigningEnabled(false)); 156 157 signGolden( 158 "golden-unaligned-in.apk", 159 new File(outDir, "golden-unaligned-v3-out.apk"), 160 new ApkSigner.Builder(rsa2048SignerConfig) 161 .setV1SigningEnabled(false) 162 .setV2SigningEnabled(false) 163 .setV3SigningEnabled(true)); 164 signGolden( 165 "golden-legacy-aligned-in.apk", 166 new File(outDir, "golden-legacy-aligned-v3-out.apk"), 167 new ApkSigner.Builder(rsa2048SignerConfig) 168 .setV1SigningEnabled(false) 169 .setV2SigningEnabled(false) 170 .setV3SigningEnabled(true)); 171 signGolden( 172 "golden-aligned-in.apk", 173 new File(outDir, "golden-aligned-v3-out.apk"), 174 new ApkSigner.Builder(rsa2048SignerConfig) 175 .setV1SigningEnabled(false) 176 .setV2SigningEnabled(false) 177 .setV3SigningEnabled(true)); 178 179 signGolden( 180 "golden-unaligned-in.apk", 181 new File(outDir, "golden-unaligned-v3-lineage-out.apk"), 182 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 183 .setV1SigningEnabled(false) 184 .setV2SigningEnabled(false) 185 .setV3SigningEnabled(true) 186 .setSigningCertificateLineage(lineage)); 187 signGolden( 188 "golden-legacy-aligned-in.apk", 189 new File(outDir, "golden-legacy-aligned-v3-lineage-out.apk"), 190 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 191 .setV1SigningEnabled(false) 192 .setV2SigningEnabled(false) 193 .setV3SigningEnabled(true) 194 .setSigningCertificateLineage(lineage)); 195 signGolden( 196 "golden-aligned-in.apk", 197 new File(outDir, "golden-aligned-v3-lineage-out.apk"), 198 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 199 .setV1SigningEnabled(false) 200 .setV2SigningEnabled(false) 201 .setV3SigningEnabled(true) 202 .setSigningCertificateLineage(lineage)); 203 204 signGolden( 205 "golden-unaligned-in.apk", 206 new File(outDir, "golden-unaligned-v1v2-out.apk"), 207 new ApkSigner.Builder(rsa2048SignerConfig) 208 .setV1SigningEnabled(true) 209 .setV2SigningEnabled(true) 210 .setV3SigningEnabled(false)); 211 signGolden( 212 "golden-legacy-aligned-in.apk", 213 new File(outDir, "golden-legacy-aligned-v1v2-out.apk"), 214 new ApkSigner.Builder(rsa2048SignerConfig) 215 .setV1SigningEnabled(true) 216 .setV2SigningEnabled(true) 217 .setV3SigningEnabled(false)); 218 signGolden( 219 "golden-aligned-in.apk", 220 new File(outDir, "golden-aligned-v1v2-out.apk"), 221 new ApkSigner.Builder(rsa2048SignerConfig) 222 .setV1SigningEnabled(true) 223 .setV2SigningEnabled(true) 224 .setV3SigningEnabled(false)); 225 226 signGolden( 227 "golden-unaligned-in.apk", 228 new File(outDir, "golden-unaligned-v2v3-out.apk"), 229 new ApkSigner.Builder(rsa2048SignerConfig) 230 .setV1SigningEnabled(false) 231 .setV2SigningEnabled(true) 232 .setV3SigningEnabled(true)); 233 signGolden( 234 "golden-legacy-aligned-in.apk", 235 new File(outDir, "golden-legacy-aligned-v2v3-out.apk"), 236 new ApkSigner.Builder(rsa2048SignerConfig) 237 .setV1SigningEnabled(false) 238 .setV2SigningEnabled(true) 239 .setV3SigningEnabled(true)); 240 signGolden( 241 "golden-aligned-in.apk", 242 new File(outDir, "golden-aligned-v2v3-out.apk"), 243 new ApkSigner.Builder(rsa2048SignerConfig) 244 .setV1SigningEnabled(false) 245 .setV2SigningEnabled(true) 246 .setV3SigningEnabled(true)); 247 signGolden( 248 "golden-unaligned-in.apk", 249 new File(outDir, "golden-unaligned-v2v3-lineage-out.apk"), 250 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 251 .setV1SigningEnabled(false) 252 .setV2SigningEnabled(true) 253 .setV3SigningEnabled(true) 254 .setSigningCertificateLineage(lineage)); 255 signGolden( 256 "golden-legacy-aligned-in.apk", 257 new File(outDir, "golden-legacy-aligned-v2v3-lineage-out.apk"), 258 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 259 .setV1SigningEnabled(false) 260 .setV2SigningEnabled(true) 261 .setV3SigningEnabled(true) 262 .setSigningCertificateLineage(lineage)); 263 signGolden( 264 "golden-aligned-in.apk", 265 new File(outDir, "golden-aligned-v2v3-lineage-out.apk"), 266 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 267 .setV1SigningEnabled(false) 268 .setV2SigningEnabled(true) 269 .setV3SigningEnabled(true) 270 .setSigningCertificateLineage(lineage)); 271 272 signGolden( 273 "golden-unaligned-in.apk", 274 new File(outDir, "golden-unaligned-v1v2v3-out.apk"), 275 new ApkSigner.Builder(rsa2048SignerConfig) 276 .setV1SigningEnabled(true) 277 .setV2SigningEnabled(true) 278 .setV3SigningEnabled(true)); 279 signGolden( 280 "golden-legacy-aligned-in.apk", 281 new File(outDir, "golden-legacy-aligned-v1v2v3-out.apk"), 282 new ApkSigner.Builder(rsa2048SignerConfig) 283 .setV1SigningEnabled(true) 284 .setV2SigningEnabled(true) 285 .setV3SigningEnabled(true)); 286 signGolden( 287 "golden-aligned-in.apk", 288 new File(outDir, "golden-aligned-v1v2v3-out.apk"), 289 new ApkSigner.Builder(rsa2048SignerConfig) 290 .setV1SigningEnabled(true) 291 .setV2SigningEnabled(true) 292 .setV3SigningEnabled(true)); 293 signGolden( 294 "golden-unaligned-in.apk", 295 new File(outDir, "golden-unaligned-v1v2v3-lineage-out.apk"), 296 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 297 .setV1SigningEnabled(true) 298 .setV2SigningEnabled(true) 299 .setV3SigningEnabled(true) 300 .setSigningCertificateLineage(lineage)); 301 signGolden( 302 "golden-legacy-aligned-in.apk", 303 new File(outDir, "golden-legacy-aligned-v1v2v3-lineage-out.apk"), 304 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 305 .setV1SigningEnabled(true) 306 .setV2SigningEnabled(true) 307 .setV3SigningEnabled(true) 308 .setSigningCertificateLineage(lineage)); 309 signGolden( 310 "golden-aligned-in.apk", 311 new File(outDir, "golden-aligned-v1v2v3-lineage-out.apk"), 312 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 313 .setV1SigningEnabled(true) 314 .setV2SigningEnabled(true) 315 .setV3SigningEnabled(true) 316 .setSigningCertificateLineage(lineage)); 317 318 signGolden( 319 "original.apk", new File(outDir, "golden-rsa-out.apk"), 320 new ApkSigner.Builder(rsa2048SignerConfig)); 321 signGolden( 322 "original.apk", new File(outDir, "golden-rsa-minSdkVersion-1-out.apk"), 323 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(1)); 324 signGolden( 325 "original.apk", new File(outDir, "golden-rsa-minSdkVersion-18-out.apk"), 326 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(18)); 327 signGolden( 328 "original.apk", new File(outDir, "golden-rsa-minSdkVersion-24-out.apk"), 329 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(24)); 330 } 331 signGolden( String inResourceName, File outFile, ApkSigner.Builder apkSignerBuilder)332 private static void signGolden( 333 String inResourceName, File outFile, ApkSigner.Builder apkSignerBuilder) 334 throws Exception { 335 DataSource in = 336 DataSources.asDataSource( 337 ByteBuffer.wrap(Resources.toByteArray(ApkSigner.class, inResourceName))); 338 apkSignerBuilder 339 .setInputApk(in) 340 .setOutputApk(outFile) 341 .build() 342 .sign(); 343 } 344 345 @Test testAlignmentPreserved_Golden()346 public void testAlignmentPreserved_Golden() throws Exception { 347 // Regression tests for preserving (mis)alignment of ZIP Local File Header data 348 // NOTE: Expected output files can be re-generated by running the "main" method. 349 350 List<ApkSigner.SignerConfig> rsa2048SignerConfig = 351 Collections.singletonList( 352 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 353 List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = Arrays.asList( 354 rsa2048SignerConfig.get(0), 355 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 356 SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), 357 LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); 358 // Uncompressed entries in this input file are not aligned -- the file was created using 359 // the jar utility. temp4.txt entry was then manually added into the archive. This entry's 360 // ZIP Local File Header "extra" field declares that the entry's data must be aligned to 361 // 4 kB boundary, but the data isn't actually aligned in the file. 362 assertGolden( 363 "golden-unaligned-in.apk", "golden-unaligned-out.apk", 364 new ApkSigner.Builder(rsa2048SignerConfig)); 365 assertGolden( 366 "golden-unaligned-in.apk", "golden-unaligned-v1-out.apk", 367 new ApkSigner.Builder(rsa2048SignerConfig) 368 .setV1SigningEnabled(true) 369 .setV2SigningEnabled(false) 370 .setV3SigningEnabled(false)); 371 assertGolden( 372 "golden-unaligned-in.apk", "golden-unaligned-v2-out.apk", 373 new ApkSigner.Builder(rsa2048SignerConfig) 374 .setV1SigningEnabled(false) 375 .setV2SigningEnabled(true) 376 .setV3SigningEnabled(false)); 377 assertGolden( 378 "golden-unaligned-in.apk", "golden-unaligned-v3-out.apk", 379 new ApkSigner.Builder(rsa2048SignerConfig) 380 .setV1SigningEnabled(false) 381 .setV2SigningEnabled(false) 382 .setV3SigningEnabled(true)); 383 assertGolden( 384 "golden-unaligned-in.apk", "golden-unaligned-v3-lineage-out.apk", 385 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 386 .setV1SigningEnabled(false) 387 .setV2SigningEnabled(false) 388 .setV3SigningEnabled(true) 389 .setSigningCertificateLineage(lineage)); 390 assertGolden( 391 "golden-unaligned-in.apk", "golden-unaligned-v1v2-out.apk", 392 new ApkSigner.Builder(rsa2048SignerConfig) 393 .setV1SigningEnabled(true) 394 .setV2SigningEnabled(true) 395 .setV3SigningEnabled(false)); 396 assertGolden( 397 "golden-unaligned-in.apk", "golden-unaligned-v2v3-out.apk", 398 new ApkSigner.Builder(rsa2048SignerConfig) 399 .setV1SigningEnabled(false) 400 .setV2SigningEnabled(true) 401 .setV3SigningEnabled(true)); 402 assertGolden( 403 "golden-unaligned-in.apk", "golden-unaligned-v2v3-lineage-out.apk", 404 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 405 .setV1SigningEnabled(false) 406 .setV2SigningEnabled(true) 407 .setV3SigningEnabled(true) 408 .setSigningCertificateLineage(lineage)); 409 assertGolden( 410 "golden-unaligned-in.apk", "golden-unaligned-v1v2v3-out.apk", 411 new ApkSigner.Builder(rsa2048SignerConfig) 412 .setV1SigningEnabled(true) 413 .setV2SigningEnabled(true) 414 .setV3SigningEnabled(true)); 415 assertGolden( 416 "golden-unaligned-in.apk", "golden-unaligned-v1v2v3-lineage-out.apk", 417 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 418 .setV1SigningEnabled(true) 419 .setV2SigningEnabled(true) 420 .setV3SigningEnabled(true) 421 .setSigningCertificateLineage(lineage)); 422 423 // Uncompressed entries in this input file are aligned by zero-padding the "extra" field, as 424 // performed by zipalign at the time of writing. This padding technique produces ZIP 425 // archives whose "extra" field are not compliant with APPNOTE.TXT. Hence, this technique 426 // was deprecated. 427 assertGolden( 428 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-out.apk", 429 new ApkSigner.Builder(rsa2048SignerConfig)); 430 assertGolden( 431 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1-out.apk", 432 new ApkSigner.Builder(rsa2048SignerConfig) 433 .setV1SigningEnabled(true) 434 .setV2SigningEnabled(false) 435 .setV3SigningEnabled(false)); 436 assertGolden( 437 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2-out.apk", 438 new ApkSigner.Builder(rsa2048SignerConfig) 439 .setV1SigningEnabled(false) 440 .setV2SigningEnabled(true) 441 .setV3SigningEnabled(false)); 442 assertGolden( 443 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v3-out.apk", 444 new ApkSigner.Builder(rsa2048SignerConfig) 445 .setV1SigningEnabled(false) 446 .setV2SigningEnabled(false) 447 .setV3SigningEnabled(true)); 448 assertGolden( 449 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v3-lineage-out.apk", 450 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 451 .setV1SigningEnabled(false) 452 .setV2SigningEnabled(false) 453 .setV3SigningEnabled(true) 454 .setSigningCertificateLineage(lineage)); 455 assertGolden( 456 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2-out.apk", 457 new ApkSigner.Builder(rsa2048SignerConfig) 458 .setV1SigningEnabled(true) 459 .setV2SigningEnabled(true) 460 .setV3SigningEnabled(false)); 461 assertGolden( 462 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2v3-out.apk", 463 new ApkSigner.Builder(rsa2048SignerConfig) 464 .setV1SigningEnabled(false) 465 .setV2SigningEnabled(true) 466 .setV3SigningEnabled(true)); 467 assertGolden( 468 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2v3-lineage-out.apk", 469 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 470 .setV1SigningEnabled(false) 471 .setV2SigningEnabled(true) 472 .setV3SigningEnabled(true) 473 .setSigningCertificateLineage(lineage)); 474 assertGolden( 475 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2v3-out.apk", 476 new ApkSigner.Builder(rsa2048SignerConfig) 477 .setV1SigningEnabled(true) 478 .setV2SigningEnabled(true) 479 .setV3SigningEnabled(true)); 480 assertGolden( 481 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2v3-lineage-out.apk", 482 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 483 .setV1SigningEnabled(true) 484 .setV2SigningEnabled(true) 485 .setV3SigningEnabled(true) 486 .setSigningCertificateLineage(lineage)); 487 488 // Uncompressed entries in this input file are aligned by padding the "extra" field, as 489 // generated by signapk and apksigner. This padding technique produces "extra" fields which 490 // are compliant with APPNOTE.TXT. 491 assertGolden( 492 "golden-aligned-in.apk", "golden-aligned-out.apk", 493 new ApkSigner.Builder(rsa2048SignerConfig)); 494 assertGolden( 495 "golden-aligned-in.apk", "golden-aligned-v1-out.apk", 496 new ApkSigner.Builder(rsa2048SignerConfig) 497 .setV1SigningEnabled(true) 498 .setV2SigningEnabled(false) 499 .setV3SigningEnabled(false)); 500 assertGolden( 501 "golden-aligned-in.apk", "golden-aligned-v2-out.apk", 502 new ApkSigner.Builder(rsa2048SignerConfig) 503 .setV1SigningEnabled(false) 504 .setV2SigningEnabled(true) 505 .setV3SigningEnabled(false)); 506 assertGolden( 507 "golden-aligned-in.apk", "golden-aligned-v3-out.apk", 508 new ApkSigner.Builder(rsa2048SignerConfig) 509 .setV1SigningEnabled(false) 510 .setV2SigningEnabled(false) 511 .setV3SigningEnabled(true)); 512 assertGolden( 513 "golden-aligned-in.apk", "golden-aligned-v3-lineage-out.apk", 514 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 515 .setV1SigningEnabled(false) 516 .setV2SigningEnabled(false) 517 .setV3SigningEnabled(true) 518 .setSigningCertificateLineage(lineage)); 519 assertGolden( 520 "golden-aligned-in.apk", "golden-aligned-v1v2-out.apk", 521 new ApkSigner.Builder(rsa2048SignerConfig) 522 .setV1SigningEnabled(true) 523 .setV2SigningEnabled(true) 524 .setV3SigningEnabled(false)); 525 assertGolden( 526 "golden-aligned-in.apk", "golden-aligned-v2v3-out.apk", 527 new ApkSigner.Builder(rsa2048SignerConfig) 528 .setV1SigningEnabled(false) 529 .setV2SigningEnabled(true) 530 .setV3SigningEnabled(true)); 531 assertGolden( 532 "golden-aligned-in.apk", "golden-aligned-v2v3-lineage-out.apk", 533 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 534 .setV1SigningEnabled(false) 535 .setV2SigningEnabled(true) 536 .setV3SigningEnabled(true) 537 .setSigningCertificateLineage(lineage)); 538 assertGolden( 539 "golden-aligned-in.apk", "golden-aligned-v1v2v3-out.apk", 540 new ApkSigner.Builder(rsa2048SignerConfig) 541 .setV1SigningEnabled(true) 542 .setV2SigningEnabled(true) 543 .setV3SigningEnabled(true)); 544 assertGolden( 545 "golden-aligned-in.apk", "golden-aligned-v1v2v3-lineage-out.apk", 546 new ApkSigner.Builder(rsa2048SignerConfigWithLineage) 547 .setV1SigningEnabled(true) 548 .setV2SigningEnabled(true) 549 .setV3SigningEnabled(true) 550 .setSigningCertificateLineage(lineage)); 551 } 552 553 @Test testMinSdkVersion_Golden()554 public void testMinSdkVersion_Golden() throws Exception { 555 // Regression tests for minSdkVersion-based signature/digest algorithm selection 556 // NOTE: Expected output files can be re-generated by running the "main" method. 557 558 List<ApkSigner.SignerConfig> rsaSignerConfig = Collections.singletonList( 559 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 560 assertGolden("original.apk", "golden-rsa-out.apk", new ApkSigner.Builder(rsaSignerConfig)); 561 assertGolden( 562 "original.apk", "golden-rsa-minSdkVersion-1-out.apk", 563 new ApkSigner.Builder(rsaSignerConfig).setMinSdkVersion(1)); 564 assertGolden( 565 "original.apk", "golden-rsa-minSdkVersion-18-out.apk", 566 new ApkSigner.Builder(rsaSignerConfig).setMinSdkVersion(18)); 567 assertGolden( 568 "original.apk", "golden-rsa-minSdkVersion-24-out.apk", 569 new ApkSigner.Builder(rsaSignerConfig).setMinSdkVersion(24)); 570 571 // TODO: Add tests for DSA and ECDSA. This is non-trivial because the default 572 // implementations of these signature algorithms are non-deterministic which means output 573 // files always differ from golden files. 574 } 575 576 @Test testRsaSignedVerifies()577 public void testRsaSignedVerifies() throws Exception { 578 List<ApkSigner.SignerConfig> signers = Collections.singletonList( 579 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 580 String in = "original.apk"; 581 582 // Sign so that the APK is guaranteed to verify on API Level 1+ 583 DataSource out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1)); 584 assertVerified(verifyForMinSdkVersion(out, 1)); 585 586 // Sign so that the APK is guaranteed to verify on API Level 18+ 587 out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(18)); 588 assertVerified(verifyForMinSdkVersion(out, 18)); 589 // Does not verify on API Level 17 because RSA with SHA-256 not supported 590 assertVerificationFailure( 591 verifyForMinSdkVersion(out, 17), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG); 592 } 593 594 @Test testDsaSignedVerifies()595 public void testDsaSignedVerifies() throws Exception { 596 List<ApkSigner.SignerConfig> signers = 597 Collections.singletonList(getDefaultSignerConfigFromResources("dsa-1024")); 598 String in = "original.apk"; 599 600 // Sign so that the APK is guaranteed to verify on API Level 1+ 601 DataSource out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1)); 602 assertVerified(verifyForMinSdkVersion(out, 1)); 603 604 // Sign so that the APK is guaranteed to verify on API Level 21+ 605 out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(21)); 606 assertVerified(verifyForMinSdkVersion(out, 21)); 607 // Does not verify on API Level 20 because DSA with SHA-256 not supported 608 assertVerificationFailure( 609 verifyForMinSdkVersion(out, 20), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG); 610 } 611 612 @Test testEcSignedVerifies()613 public void testEcSignedVerifies() throws Exception { 614 List<ApkSigner.SignerConfig> signers = 615 Collections.singletonList(getDefaultSignerConfigFromResources("ec-p256")); 616 String in = "original.apk"; 617 618 // NOTE: EC APK signatures are not supported prior to API Level 18 619 // Sign so that the APK is guaranteed to verify on API Level 18+ 620 DataSource out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(18)); 621 assertVerified(verifyForMinSdkVersion(out, 18)); 622 // Does not verify on API Level 17 because EC not supported 623 assertVerificationFailure( 624 verifyForMinSdkVersion(out, 17), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG); 625 } 626 627 @Test testV1SigningRejectsInvalidZipEntryNames()628 public void testV1SigningRejectsInvalidZipEntryNames() throws Exception { 629 // ZIP/JAR entry name cannot contain CR, LF, or NUL characters when the APK is being 630 // JAR-signed. 631 List<ApkSigner.SignerConfig> signers = Collections.singletonList( 632 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 633 try { 634 sign("v1-only-with-cr-in-entry-name.apk", 635 new ApkSigner.Builder(signers).setV1SigningEnabled(true)); 636 fail(); 637 } catch (ApkFormatException expected) {} 638 639 try { 640 sign("v1-only-with-lf-in-entry-name.apk", 641 new ApkSigner.Builder(signers).setV1SigningEnabled(true)); 642 fail(); 643 } catch (ApkFormatException expected) {} 644 645 try { 646 sign("v1-only-with-nul-in-entry-name.apk", 647 new ApkSigner.Builder(signers).setV1SigningEnabled(true)); 648 fail(); 649 } catch (ApkFormatException expected) {} 650 } 651 652 @Test testWeirdZipCompressionMethod()653 public void testWeirdZipCompressionMethod() throws Exception { 654 // Any ZIP compression method other than STORED is treated as DEFLATED by Android. 655 // This APK declares compression method 21 (neither STORED nor DEFLATED) for CERT.RSA entry, 656 // but the entry is actually Deflate-compressed. 657 List<ApkSigner.SignerConfig> signers = Collections.singletonList( 658 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 659 sign("weird-compression-method.apk", new ApkSigner.Builder(signers)); 660 } 661 662 @Test testZipCompressionMethodMismatchBetweenLfhAndCd()663 public void testZipCompressionMethodMismatchBetweenLfhAndCd() throws Exception { 664 // Android Package Manager ignores compressionMethod field in Local File Header and always 665 // uses the compressionMethod from Central Directory instead. 666 // In this APK, compression method of CERT.RSA is declared as STORED in Local File Header 667 // and as DEFLATED in Central Directory. The entry is actually Deflate-compressed. 668 List<ApkSigner.SignerConfig> signers = Collections.singletonList( 669 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 670 sign("mismatched-compression-method.apk", new ApkSigner.Builder(signers)); 671 } 672 673 @Test testDebuggableApk()674 public void testDebuggableApk() throws Exception { 675 // APK which uses a boolean value "true" in its android:debuggable 676 String apk = "debuggable-boolean.apk"; 677 List<ApkSigner.SignerConfig> signers = Collections.singletonList( 678 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); 679 // Signing debuggable APKs is permitted by default 680 sign(apk, new ApkSigner.Builder(signers)); 681 // Signing debuggable APK succeeds when explicitly requested 682 sign(apk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(true)); 683 // Signing debuggable APK fails when requested 684 try { 685 sign(apk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(false)); 686 fail(); 687 } catch (SignatureException expected) {} 688 689 // APK which uses a reference value, pointing to boolean "false", in its android:debuggable 690 apk = "debuggable-resource.apk"; 691 // When we permit signing regardless of whether the APK is debuggable, the value of 692 // android:debuggable should be ignored. 693 sign(apk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(true)); 694 695 // When we disallow signing debuggable APKs, APKs with android:debuggable being a resource 696 // reference must be rejected, because there's no easy way to establish whether the resolved 697 // boolean value is the same for all resource configurations. 698 try { 699 sign(apk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(false)); 700 fail(); 701 } catch (SignatureException expected) {} 702 } 703 704 @Test(expected = IllegalStateException.class) testV3SigningWithSignersNotInLineageFails()705 public void testV3SigningWithSignersNotInLineageFails() throws Exception { 706 // APKs signed with the v3 scheme after a key rotation must specify the lineage containing 707 // the proof of rotation. This test verifies that the signing will fail if the provided 708 // signers are not in the specified lineage. 709 List<ApkSigner.SignerConfig> signers = Arrays.asList( 710 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 711 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 712 SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), 713 "rsa-1024-lineage-2-signers"); 714 sign("original.apk", new ApkSigner.Builder(signers).setSigningCertificateLineage(lineage)); 715 } 716 717 @Test testSigningWithLineageRequiresOldestSignerForV1AndV2()718 public void testSigningWithLineageRequiresOldestSignerForV1AndV2() throws Exception { 719 // After a key rotation the oldest signer must still be specified for v1 and v2 signing. 720 // The lineage contains the proof of rotation and will be used to determine the oldest 721 // signer. 722 ApkSigner.SignerConfig firstSigner = getDefaultSignerConfigFromResources( 723 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 724 ApkSigner.SignerConfig secondSigner = getDefaultSignerConfigFromResources( 725 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 726 ApkSigner.SignerConfig thirdSigner = getDefaultSignerConfigFromResources( 727 THIRD_RSA_2048_SIGNER_RESOURCE_NAME); 728 SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), 729 "rsa-2048-lineage-3-signers"); 730 731 // Verifies that the v1 signing scheme requires the oldest signer after a key rotation. 732 List<ApkSigner.SignerConfig> signers = Collections.singletonList(thirdSigner); 733 try { 734 sign("original.apk", new ApkSigner.Builder(signers) 735 .setV1SigningEnabled(true) 736 .setV2SigningEnabled(false) 737 .setV3SigningEnabled(true) 738 .setSigningCertificateLineage(lineage)); 739 fail("The signing should have failed due to the oldest signer in the lineage not being" 740 + " provided for v1 signing"); 741 } catch (IllegalArgumentException expected) {} 742 743 // Verifies that the v2 signing scheme requires the oldest signer after a key rotation. 744 try { 745 sign("original.apk", new ApkSigner.Builder(signers) 746 .setV1SigningEnabled(false) 747 .setV2SigningEnabled(true) 748 .setV3SigningEnabled(true) 749 .setSigningCertificateLineage(lineage)); 750 fail("The signing should have failed due to the oldest signer in the lineage not being" 751 + " provided for v2 signing"); 752 } catch (IllegalArgumentException expected) {} 753 754 // Verifies that when only the v3 signing scheme is requested the oldest signer does not 755 // need to be provided. 756 sign("original.apk", new ApkSigner.Builder(signers) 757 .setV1SigningEnabled(false) 758 .setV2SigningEnabled(false) 759 .setV3SigningEnabled(true) 760 .setSigningCertificateLineage(lineage)); 761 762 // Verifies that an intermediate signer in the lineage is not sufficient to satisfy the 763 // requirement that the oldest signer be provided for v1 and v2 signing. 764 signers = Arrays.asList(secondSigner, thirdSigner); 765 try { 766 sign("original.apk", new ApkSigner.Builder(signers) 767 .setV1SigningEnabled(true) 768 .setV2SigningEnabled(true) 769 .setV3SigningEnabled(true) 770 .setSigningCertificateLineage(lineage)); 771 fail("The signing should have failed due to the oldest signer in the lineage not being" 772 + " provided for v1/v2 signing"); 773 } catch (IllegalArgumentException expected) {} 774 775 // Verifies that the signing is successful when the oldest and newest signers are provided 776 // and that intermediate signers are not required. 777 signers = Arrays.asList(firstSigner, thirdSigner); 778 sign("original.apk", new ApkSigner.Builder(signers) 779 .setV1SigningEnabled(true) 780 .setV2SigningEnabled(true) 781 .setV3SigningEnabled(true) 782 .setSigningCertificateLineage(lineage)); 783 } 784 785 @Test(expected = IllegalStateException.class) testV3SigningWithMultipleSignersAndNoLineageFails()786 public void testV3SigningWithMultipleSignersAndNoLineageFails() throws Exception { 787 // The v3 signing scheme does not support multiple signers; if multiple signers are provided 788 // it is assumed these signers are part of the lineage. This test verifies v3 signing 789 // fails if multiple signers are provided without a lineage. 790 ApkSigner.SignerConfig firstSigner = getDefaultSignerConfigFromResources( 791 FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 792 ApkSigner.SignerConfig secondSigner = getDefaultSignerConfigFromResources( 793 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 794 List<ApkSigner.SignerConfig> signers = Arrays.asList(firstSigner, secondSigner); 795 sign("original.apk", new ApkSigner.Builder(signers) 796 .setV1SigningEnabled(true) 797 .setV2SigningEnabled(true) 798 .setV3SigningEnabled(true)); 799 } 800 801 @Test testLineageCanBeReadAfterV3Signing()802 public void testLineageCanBeReadAfterV3Signing() throws Exception { 803 SigningCertificateLineage.SignerConfig firstSigner = Resources.toLineageSignerConfig( 804 getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME); 805 SigningCertificateLineage.SignerConfig secondSigner = Resources.toLineageSignerConfig( 806 getClass(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 807 SigningCertificateLineage lineage = new SigningCertificateLineage.Builder(firstSigner, 808 secondSigner).build(); 809 List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList( 810 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), 811 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); 812 DataSource out = sign("original.apk", new ApkSigner.Builder(signerConfigs) 813 .setV3SigningEnabled(true) 814 .setSigningCertificateLineage(lineage)); 815 SigningCertificateLineage lineageFromApk = SigningCertificateLineage.readFromApkDataSource( 816 out); 817 assertTrue("The first signer was not in the lineage from the signed APK", 818 lineageFromApk.isSignerInLineage((firstSigner))); 819 assertTrue("The second signer was not in the lineage from the signed APK", 820 lineageFromApk.isSignerInLineage((secondSigner))); 821 } 822 823 @Test testPublicKeyHasPositiveModulusAfterSigning()824 public void testPublicKeyHasPositiveModulusAfterSigning() throws Exception { 825 // The V2 and V3 signature schemes include the public key from the certificate in the 826 // signing block. If a certificate with an RSAPublicKey is improperly encoded with a 827 // negative modulus this was previously written to the signing block as is and failed on 828 // device verification since on device the public key in the certificate was reencoded with 829 // the correct encoding for the modulus. This test uses an improperly encoded certificate to 830 // sign an APK and verifies that the public key in the signing block is corrected with a 831 // positive modulus to allow on device installs / updates. 832 List<ApkSigner.SignerConfig> signersList = Collections.singletonList( 833 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 834 FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS)); 835 DataSource signedApk = sign("original.apk", new ApkSigner.Builder(signersList) 836 .setV1SigningEnabled(true) 837 .setV2SigningEnabled(true) 838 .setV3SigningEnabled(true)); 839 RSAPublicKey v2PublicKey = getRSAPublicKeyFromSigningBlock(signedApk, 840 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); 841 assertTrue("The modulus in the public key in the V2 signing block must not be negative", 842 v2PublicKey.modulus.compareTo(BigInteger.ZERO) > 0); 843 RSAPublicKey v3PublicKey = getRSAPublicKeyFromSigningBlock(signedApk, 844 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); 845 assertTrue("The modulus in the public key in the V3 signing block must not be negative", 846 v3PublicKey.modulus.compareTo(BigInteger.ZERO) > 0); 847 } 848 getRSAPublicKeyFromSigningBlock(DataSource apk, int signatureVersionId)849 private RSAPublicKey getRSAPublicKeyFromSigningBlock(DataSource apk, int signatureVersionId) 850 throws Exception { 851 int signatureVersionBlockId; 852 switch (signatureVersionId) { 853 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2: 854 signatureVersionBlockId = V2SchemeSigner.APK_SIGNATURE_SCHEME_V2_BLOCK_ID; 855 break; 856 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3: 857 signatureVersionBlockId = V3SchemeSigner.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; 858 break; 859 default: 860 throw new Exception( 861 "Invalid signature version ID specified: " + signatureVersionId); 862 } 863 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 864 ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( 865 signatureVersionId); 866 SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature(apk, zipSections, 867 signatureVersionBlockId, result); 868 // FORMAT: 869 // * length prefixed sequence of length prefixed signers 870 // * length-prefixed signed data 871 // * V3+ only - minSDK (uint32) 872 // * V3+ only - maxSDK (uint32) 873 // * length-prefixed sequence of length-prefixed signatures: 874 // * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded) 875 ByteBuffer signers = ApkSigningBlockUtils.getLengthPrefixedSlice( 876 signatureInfo.signatureBlock); 877 ByteBuffer signer = ApkSigningBlockUtils.getLengthPrefixedSlice(signers); 878 // Since all the data is read from the signer block the signedData and signatures are 879 // discarded. 880 ApkSigningBlockUtils.getLengthPrefixedSlice(signer); 881 // For V3+ signature version IDs discard the min / max SDKs as well 882 if (signatureVersionId >= ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3) { 883 signer.getInt(); 884 signer.getInt(); 885 } 886 ApkSigningBlockUtils.getLengthPrefixedSlice(signer); 887 ByteBuffer publicKey = ApkSigningBlockUtils.getLengthPrefixedSlice(signer); 888 SubjectPublicKeyInfo subjectPublicKeyInfo = Asn1BerParser.parse(publicKey, 889 SubjectPublicKeyInfo.class); 890 ByteBuffer subjectPublicKeyBuffer = subjectPublicKeyInfo.subjectPublicKey; 891 // The SubjectPublicKey is stored as a bit string in the SubjectPublicKeyInfo with the first 892 // byte indicating the number of padding bits in the public key. Read this first byte to 893 // allow parsing the rest of the RSAPublicKey as a sequence. 894 subjectPublicKeyBuffer.get(); 895 RSAPublicKey rsaPublicKey = Asn1BerParser.parse(subjectPublicKeyBuffer, 896 RSAPublicKey.class); 897 return rsaPublicKey; 898 } 899 900 /** 901 * Asserts that signing the specified golden input file using the provided signing 902 * configuration produces output identical to the specified golden output file. 903 */ assertGolden( String inResourceName, String expectedOutResourceName, ApkSigner.Builder apkSignerBuilder)904 private void assertGolden( 905 String inResourceName, String expectedOutResourceName, 906 ApkSigner.Builder apkSignerBuilder) throws Exception { 907 // Sign the provided golden input 908 DataSource out = sign(inResourceName, apkSignerBuilder); 909 910 // Assert that the output is identical to the provided golden output 911 if (out.size() > Integer.MAX_VALUE) { 912 throw new RuntimeException("Output too large: " + out.size() + " bytes"); 913 } 914 ByteBuffer actualOutBuf = out.getByteBuffer(0, (int) out.size()); 915 916 ByteBuffer expectedOutBuf = 917 ByteBuffer.wrap(Resources.toByteArray(getClass(), expectedOutResourceName)); 918 919 int actualStartPos = actualOutBuf.position(); 920 boolean identical = false; 921 if (actualOutBuf.remaining() == expectedOutBuf.remaining()) { 922 while (actualOutBuf.hasRemaining()) { 923 if (actualOutBuf.get() != expectedOutBuf.get()) { 924 break; 925 } 926 } 927 identical = !actualOutBuf.hasRemaining(); 928 } 929 930 if (identical) { 931 return; 932 } 933 actualOutBuf.position(actualStartPos); 934 935 if (KEEP_FAILING_OUTPUT_AS_FILES) { 936 File tmp = File.createTempFile(getClass().getSimpleName(), ".apk"); 937 try (ByteChannel outChannel = 938 Files.newByteChannel( 939 tmp.toPath(), 940 StandardOpenOption.WRITE, 941 StandardOpenOption.CREATE, 942 StandardOpenOption.TRUNCATE_EXISTING)) { 943 while (actualOutBuf.hasRemaining()) { 944 outChannel.write(actualOutBuf); 945 } 946 } 947 fail(tmp + " differs from " + expectedOutResourceName); 948 } else { 949 fail("Output differs from " + expectedOutResourceName); 950 } 951 } 952 sign( String inResourceName, ApkSigner.Builder apkSignerBuilder)953 private DataSource sign( 954 String inResourceName, ApkSigner.Builder apkSignerBuilder) throws Exception { 955 DataSource in = 956 DataSources.asDataSource( 957 ByteBuffer.wrap(Resources.toByteArray(getClass(), inResourceName))); 958 ReadableDataSink out = DataSinks.newInMemoryDataSink(); 959 apkSignerBuilder 960 .setInputApk(in) 961 .setOutputApk(out) 962 .build() 963 .sign(); 964 return out; 965 } 966 verifyForMinSdkVersion(DataSource apk, int minSdkVersion)967 private static ApkVerifier.Result verifyForMinSdkVersion(DataSource apk, int minSdkVersion) 968 throws IOException, ApkFormatException, NoSuchAlgorithmException { 969 return verify(apk, minSdkVersion); 970 } 971 verify(DataSource apk, Integer minSdkVersionOverride)972 private static ApkVerifier.Result verify(DataSource apk, Integer minSdkVersionOverride) 973 throws IOException, ApkFormatException, NoSuchAlgorithmException { 974 ApkVerifier.Builder builder = new ApkVerifier.Builder(apk); 975 if (minSdkVersionOverride != null) { 976 builder.setMinCheckedPlatformVersion(minSdkVersionOverride); 977 } 978 return builder.build().verify(); 979 } 980 assertVerified(ApkVerifier.Result result)981 private static void assertVerified(ApkVerifier.Result result) { 982 ApkVerifierTest.assertVerified(result); 983 } 984 assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue)985 private static void assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue) { 986 ApkVerifierTest.assertVerificationFailure(result, expectedIssue); 987 } 988 getDefaultSignerConfigFromResources( String keyNameInResources)989 private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources( 990 String keyNameInResources) throws Exception { 991 PrivateKey privateKey = 992 Resources.toPrivateKey(ApkSignerTest.class, keyNameInResources + ".pk8"); 993 List<X509Certificate> certs = 994 Resources.toCertificateChain(ApkSignerTest.class, keyNameInResources + ".x509.pem"); 995 return new ApkSigner.SignerConfig.Builder(keyNameInResources, privateKey, certs).build(); 996 } 997 getDefaultSignerConfigFromResources( String keyNameInResources, String certNameInResources)998 private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources( 999 String keyNameInResources, String certNameInResources) throws Exception { 1000 PrivateKey privateKey = Resources.toPrivateKey(ApkSignerTest.class, 1001 keyNameInResources + ".pk8"); 1002 List<X509Certificate> certs = Resources.toCertificateChain(ApkSignerTest.class, 1003 certNameInResources); 1004 return new ApkSigner.SignerConfig.Builder(keyNameInResources, privateKey, certs).build(); 1005 } 1006 } 1007