1 /* 2 * Copyright (C) 2016 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 com.android.apksig.apk.ApkFormatException; 20 import com.android.apksig.apk.ApkUtils; 21 import com.android.apksig.internal.apk.ApkSigningBlockUtils; 22 import com.android.apksig.internal.apk.SignatureAlgorithm; 23 import com.android.apksig.internal.apk.v1.DigestAlgorithm; 24 import com.android.apksig.internal.apk.v1.V1SchemeSigner; 25 import com.android.apksig.internal.apk.v2.V2SchemeSigner; 26 import com.android.apksig.internal.apk.v3.V3SchemeSigner; 27 import com.android.apksig.internal.util.AndroidSdkVersion; 28 import com.android.apksig.internal.util.Pair; 29 import com.android.apksig.internal.util.TeeDataSink; 30 import com.android.apksig.util.DataSink; 31 import com.android.apksig.util.DataSinks; 32 import com.android.apksig.util.DataSource; 33 34 import java.io.ByteArrayOutputStream; 35 import java.io.IOException; 36 import java.nio.ByteBuffer; 37 import java.security.InvalidKeyException; 38 import java.security.MessageDigest; 39 import java.security.NoSuchAlgorithmException; 40 import java.security.PrivateKey; 41 import java.security.PublicKey; 42 import java.security.SignatureException; 43 import java.security.cert.CertificateException; 44 import java.security.cert.X509Certificate; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Collections; 48 import java.util.HashMap; 49 import java.util.List; 50 import java.util.Map; 51 import java.util.Set; 52 53 /** 54 * Default implementation of {@link ApkSignerEngine}. 55 * 56 * <p>Use {@link Builder} to obtain instances of this engine. 57 */ 58 public class DefaultApkSignerEngine implements ApkSignerEngine { 59 60 // IMPLEMENTATION NOTE: This engine generates a signed APK as follows: 61 // 1. The engine asks its client to output input JAR entries which are not part of JAR 62 // signature. 63 // 2. If JAR signing (v1 signing) is enabled, the engine inspects the output JAR entries to 64 // compute their digests, to be placed into output META-INF/MANIFEST.MF. It also inspects 65 // the contents of input and output META-INF/MANIFEST.MF to borrow the main section of the 66 // file. It does not care about individual (i.e., JAR entry-specific) sections. It then 67 // emits the v1 signature (a set of JAR entries) and asks the client to output them. 68 // 3. If APK Signature Scheme v2 (v2 signing) is enabled, the engine emits an APK Signing Block 69 // from outputZipSections() and asks its client to insert this block into the output. 70 // 4. If APK Signature Scheme v3 (v3 signing) is enabled, the engine includes it in the APK 71 // Signing BLock output from outputZipSections() and asks its client to insert this block 72 // into the output. If both v2 and v3 signing is enabled, they are both added to the APK 73 // Signing Block before asking the client to insert it into the output. 74 75 private final boolean mV1SigningEnabled; 76 private final boolean mV2SigningEnabled; 77 private final boolean mV3SigningEnabled; 78 private final boolean mDebuggableApkPermitted; 79 private final boolean mOtherSignersSignaturesPreserved; 80 private final String mCreatedBy; 81 private final List<SignerConfig> mSignerConfigs; 82 private final int mMinSdkVersion; 83 private final SigningCertificateLineage mSigningCertificateLineage; 84 85 private List<V1SchemeSigner.SignerConfig> mV1SignerConfigs = Collections.emptyList(); 86 private DigestAlgorithm mV1ContentDigestAlgorithm; 87 88 private boolean mClosed; 89 90 private boolean mV1SignaturePending; 91 92 /** 93 * Names of JAR entries which this engine is expected to output as part of v1 signing. 94 */ 95 private Set<String> mSignatureExpectedOutputJarEntryNames = Collections.emptySet(); 96 97 /** Requests for digests of output JAR entries. */ 98 private final Map<String, GetJarEntryDataDigestRequest> mOutputJarEntryDigestRequests = 99 new HashMap<>(); 100 101 /** Digests of output JAR entries. */ 102 private final Map<String, byte[]> mOutputJarEntryDigests = new HashMap<>(); 103 104 /** Data of JAR entries emitted by this engine as v1 signature. */ 105 private final Map<String, byte[]> mEmittedSignatureJarEntryData = new HashMap<>(); 106 107 /** Requests for data of output JAR entries which comprise the v1 signature. */ 108 private final Map<String, GetJarEntryDataRequest> mOutputSignatureJarEntryDataRequests = 109 new HashMap<>(); 110 /** 111 * Request to obtain the data of MANIFEST.MF or {@code null} if the request hasn't been issued. 112 */ 113 private GetJarEntryDataRequest mInputJarManifestEntryDataRequest; 114 115 /** 116 * Request to obtain the data of AndroidManifest.xml or {@code null} if the request hasn't been 117 * issued. 118 */ 119 private GetJarEntryDataRequest mOutputAndroidManifestEntryDataRequest; 120 121 /** 122 * Whether the package being signed is marked as {@code android:debuggable} or {@code null} 123 * if this is not yet known. 124 */ 125 private Boolean mDebuggable; 126 127 /** 128 * Request to output the emitted v1 signature or {@code null} if the request hasn't been issued. 129 */ 130 private OutputJarSignatureRequestImpl mAddV1SignatureRequest; 131 132 private boolean mV2SignaturePending; 133 private boolean mV3SignaturePending; 134 135 /** 136 * Request to output the emitted v2 and/or v3 signature(s) {@code null} if the request hasn't 137 * been issued. 138 */ 139 private OutputApkSigningBlockRequestImpl mAddSigningBlockRequest; 140 141 private DefaultApkSignerEngine( 142 List<SignerConfig> signerConfigs, 143 int minSdkVersion, 144 boolean v1SigningEnabled, 145 boolean v2SigningEnabled, 146 boolean v3SigningEnabled, 147 boolean debuggableApkPermitted, 148 boolean otherSignersSignaturesPreserved, 149 String createdBy, 150 SigningCertificateLineage signingCertificateLineage) throws InvalidKeyException { 151 if (signerConfigs.isEmpty()) { 152 throw new IllegalArgumentException("At least one signer config must be provided"); 153 } 154 if (otherSignersSignaturesPreserved) { 155 throw new UnsupportedOperationException( 156 "Preserving other signer's signatures is not yet implemented"); 157 } 158 159 mV1SigningEnabled = v1SigningEnabled; 160 mV2SigningEnabled = v2SigningEnabled; 161 mV3SigningEnabled = v3SigningEnabled; 162 mV1SignaturePending = v1SigningEnabled; 163 mV2SignaturePending = v2SigningEnabled; 164 mV3SignaturePending = v3SigningEnabled; 165 mDebuggableApkPermitted = debuggableApkPermitted; 166 mOtherSignersSignaturesPreserved = otherSignersSignaturesPreserved; 167 mCreatedBy = createdBy; 168 mSignerConfigs = signerConfigs; 169 mMinSdkVersion = minSdkVersion; 170 mSigningCertificateLineage = signingCertificateLineage; 171 172 if (v1SigningEnabled) { 173 if (v3SigningEnabled) { 174 175 // v3 signing only supports single signers, of which the oldest (first) will be the 176 // one to use for v1 and v2 signing 177 SignerConfig oldestConfig = signerConfigs.get(0); 178 179 // in the event of signing certificate changes, make sure we have the oldest in the 180 // signing history to sign with v1 181 if (signingCertificateLineage != null) { 182 SigningCertificateLineage subLineage = 183 signingCertificateLineage.getSubLineage( 184 oldestConfig.mCertificates.get(0)); 185 if (subLineage.size() != 1) { 186 throw new IllegalArgumentException( 187 "v1 signing enabled but the oldest signer in the " 188 + "SigningCertificateLineage is missing. Please provide the oldest" 189 + " signer to enable v1 signing"); 190 } 191 } 192 createV1SignerConfigs( 193 Collections.singletonList(oldestConfig), minSdkVersion); 194 } else { 195 createV1SignerConfigs(signerConfigs, minSdkVersion); 196 } 197 } 198 } 199 200 private void createV1SignerConfigs(List<SignerConfig> signerConfigs, int minSdkVersion) 201 throws InvalidKeyException { 202 mV1SignerConfigs = new ArrayList<>(signerConfigs.size()); 203 Map<String, Integer> v1SignerNameToSignerIndex = new HashMap<>(signerConfigs.size()); 204 DigestAlgorithm v1ContentDigestAlgorithm = null; 205 for (int i = 0; i < signerConfigs.size(); i++) { 206 SignerConfig signerConfig = signerConfigs.get(i); 207 List<X509Certificate> certificates = signerConfig.getCertificates(); 208 PublicKey publicKey = certificates.get(0).getPublicKey(); 209 210 String v1SignerName = V1SchemeSigner.getSafeSignerName(signerConfig.getName()); 211 // Check whether the signer's name is unique among all v1 signers 212 Integer indexOfOtherSignerWithSameName = 213 v1SignerNameToSignerIndex.put(v1SignerName, i); 214 if (indexOfOtherSignerWithSameName != null) { 215 throw new IllegalArgumentException( 216 "Signers #" + (indexOfOtherSignerWithSameName + 1) 217 + " and #" + (i + 1) 218 + " have the same name: " + v1SignerName 219 + ". v1 signer names must be unique"); 220 } 221 222 DigestAlgorithm v1SignatureDigestAlgorithm = 223 V1SchemeSigner.getSuggestedSignatureDigestAlgorithm( 224 publicKey, minSdkVersion); 225 V1SchemeSigner.SignerConfig v1SignerConfig = new V1SchemeSigner.SignerConfig(); 226 v1SignerConfig.name = v1SignerName; 227 v1SignerConfig.privateKey = signerConfig.getPrivateKey(); 228 v1SignerConfig.certificates = certificates; 229 v1SignerConfig.signatureDigestAlgorithm = v1SignatureDigestAlgorithm; 230 // For digesting contents of APK entries and of MANIFEST.MF, pick the algorithm 231 // of comparable strength to the digest algorithm used for computing the signature. 232 // When there are multiple signers, pick the strongest digest algorithm out of their 233 // signature digest algorithms. This avoids reducing the digest strength used by any 234 // of the signers to protect APK contents. 235 if (v1ContentDigestAlgorithm == null) { 236 v1ContentDigestAlgorithm = v1SignatureDigestAlgorithm; 237 } else { 238 if (DigestAlgorithm.BY_STRENGTH_COMPARATOR.compare( 239 v1SignatureDigestAlgorithm, v1ContentDigestAlgorithm) > 0) { 240 v1ContentDigestAlgorithm = v1SignatureDigestAlgorithm; 241 } 242 } 243 mV1SignerConfigs.add(v1SignerConfig); 244 } 245 mV1ContentDigestAlgorithm = v1ContentDigestAlgorithm; 246 mSignatureExpectedOutputJarEntryNames = 247 V1SchemeSigner.getOutputEntryNames(mV1SignerConfigs); 248 } 249 250 private List<ApkSigningBlockUtils.SignerConfig> createV2SignerConfigs( 251 boolean apkSigningBlockPaddingSupported) throws InvalidKeyException { 252 if (mV3SigningEnabled) { 253 254 // v3 signing only supports single signers, of which the oldest (first) will be the one 255 // to use for v1 and v2 signing 256 List<ApkSigningBlockUtils.SignerConfig> signerConfig = 257 new ArrayList<>(); 258 259 SignerConfig oldestConfig = mSignerConfigs.get(0); 260 261 // first make sure that if we have signing certificate history that the oldest signer 262 // corresponds to the oldest ancestor 263 if (mSigningCertificateLineage != null) { 264 SigningCertificateLineage subLineage = 265 mSigningCertificateLineage.getSubLineage(oldestConfig.mCertificates.get(0)); 266 if (subLineage.size() != 1) { 267 throw new IllegalArgumentException("v2 signing enabled but the oldest signer in" 268 + " the SigningCertificateLineage is missing. Please provide" 269 + " the oldest signer to enable v2 signing."); 270 } 271 } 272 signerConfig.add( 273 createSigningBlockSignerConfig( 274 mSignerConfigs.get(0), apkSigningBlockPaddingSupported, 275 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2)); 276 return signerConfig; 277 } else { 278 return createSigningBlockSignerConfigs(apkSigningBlockPaddingSupported, 279 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); 280 } 281 } 282 283 private List<ApkSigningBlockUtils.SignerConfig> createV3SignerConfigs( 284 boolean apkSigningBlockPaddingSupported) throws InvalidKeyException { 285 List<ApkSigningBlockUtils.SignerConfig> rawConfigs = 286 createSigningBlockSignerConfigs(apkSigningBlockPaddingSupported, 287 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); 288 289 List<ApkSigningBlockUtils.SignerConfig> processedConfigs = new ArrayList<>(); 290 291 // we have our configs, now touch them up to appropriately cover all SDK levels since APK 292 // signature scheme v3 was introduced 293 int currentMinSdk = Integer.MAX_VALUE; 294 for (int i = rawConfigs.size() - 1; i >= 0; i--) { 295 ApkSigningBlockUtils.SignerConfig config = rawConfigs.get(i); 296 if (config.signatureAlgorithms == null) { 297 // no valid algorithm was found for this signer, and we haven't yet covered all 298 // platform versions, something's wrong 299 String keyAlgorithm = config.certificates.get(0).getPublicKey().getAlgorithm(); 300 throw new InvalidKeyException("Unsupported key algorithm " + keyAlgorithm + " is " 301 + "not supported for APK Signature Scheme v3 signing"); 302 } 303 if (i == rawConfigs.size() - 1) { 304 // first go through the loop, config should support all future platform versions. 305 // this assumes we don't deprecate support for signers in the future. If we do, 306 // this needs to change 307 config.maxSdkVersion = Integer.MAX_VALUE; 308 } else { 309 // otherwise, we only want to use this signer up to the minimum platform version 310 // on which a newer one is acceptable 311 config.maxSdkVersion = currentMinSdk - 1; 312 } 313 config.minSdkVersion = getMinSdkFromV3SignatureAlgorithms(config.signatureAlgorithms); 314 if (mSigningCertificateLineage != null) { 315 config.mSigningCertificateLineage = 316 mSigningCertificateLineage.getSubLineage(config.certificates.get(0)); 317 } 318 // we know that this config will be used, so add it to our result, order doesn't matter 319 // at this point (and likely only one will be needed 320 processedConfigs.add(config); 321 currentMinSdk = config.minSdkVersion; 322 if (currentMinSdk <= mMinSdkVersion || currentMinSdk <= AndroidSdkVersion.P) { 323 // this satisfies all we need, stop here 324 break; 325 } 326 } 327 if (currentMinSdk > AndroidSdkVersion.P && currentMinSdk > mMinSdkVersion) { 328 // we can't cover all desired SDK versions, abort 329 throw new InvalidKeyException("Provided key algorithms not supported on all desired " 330 + "Android SDK versions"); 331 } 332 return processedConfigs; 333 } 334 335 private int getMinSdkFromV3SignatureAlgorithms(List<SignatureAlgorithm> algorithms) { 336 int min = Integer.MAX_VALUE; 337 for (SignatureAlgorithm algorithm : algorithms) { 338 int current = algorithm.getMinSdkVersion(); 339 if (current < min) { 340 if (current <= mMinSdkVersion || current <= AndroidSdkVersion.P) { 341 // this algorithm satisfies all of our needs, no need to keep looking 342 return current; 343 } else { 344 min = current; 345 } 346 } 347 } 348 return min; 349 } 350 351 private List<ApkSigningBlockUtils.SignerConfig> createSigningBlockSignerConfigs( 352 boolean apkSigningBlockPaddingSupported, int schemeId) throws InvalidKeyException { 353 List<ApkSigningBlockUtils.SignerConfig> signerConfigs = 354 new ArrayList<>(mSignerConfigs.size()); 355 for (int i = 0; i < mSignerConfigs.size(); i++) { 356 SignerConfig signerConfig = mSignerConfigs.get(i); 357 signerConfigs.add( 358 createSigningBlockSignerConfig( 359 signerConfig, apkSigningBlockPaddingSupported, schemeId)); 360 } 361 return signerConfigs; 362 } 363 364 private ApkSigningBlockUtils.SignerConfig createSigningBlockSignerConfig( 365 SignerConfig signerConfig, boolean apkSigningBlockPaddingSupported, int schemeId) 366 throws InvalidKeyException { 367 List<X509Certificate> certificates = signerConfig.getCertificates(); 368 PublicKey publicKey = certificates.get(0).getPublicKey(); 369 370 ApkSigningBlockUtils.SignerConfig newSignerConfig = 371 new ApkSigningBlockUtils.SignerConfig(); 372 newSignerConfig.privateKey = signerConfig.getPrivateKey(); 373 newSignerConfig.certificates = certificates; 374 375 switch (schemeId) { 376 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2: 377 newSignerConfig.signatureAlgorithms = 378 V2SchemeSigner.getSuggestedSignatureAlgorithms(publicKey, mMinSdkVersion, 379 apkSigningBlockPaddingSupported); 380 break; 381 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3: 382 try { 383 newSignerConfig.signatureAlgorithms = 384 V3SchemeSigner.getSuggestedSignatureAlgorithms( 385 publicKey, mMinSdkVersion, apkSigningBlockPaddingSupported); 386 } catch (InvalidKeyException e) { 387 388 // It is possible for a signer used for v1/v2 signing to not be allowed for use 389 // with v3 signing. This is ok as long as there exists a more recent v3 signer 390 // that covers all supported platform versions. Populate signatureAlgorithm 391 // with null, it will be cleaned-up in a later step. 392 newSignerConfig.signatureAlgorithms = null; 393 } 394 break; 395 default: 396 throw new IllegalArgumentException("Unknown APK Signature Scheme ID requested"); 397 } 398 return newSignerConfig; 399 } 400 401 @Override 402 public void inputApkSigningBlock(DataSource apkSigningBlock) { 403 checkNotClosed(); 404 405 if ((apkSigningBlock == null) || (apkSigningBlock.size() == 0)) { 406 return; 407 } 408 409 if (mOtherSignersSignaturesPreserved) { 410 // TODO: Preserve blocks other than APK Signature Scheme v2 blocks of signers configured 411 // in this engine. 412 return; 413 } 414 // TODO: Preserve blocks other than APK Signature Scheme v2 blocks. 415 } 416 417 @Override 418 public InputJarEntryInstructions inputJarEntry(String entryName) { 419 checkNotClosed(); 420 421 InputJarEntryInstructions.OutputPolicy outputPolicy = 422 getInputJarEntryOutputPolicy(entryName); 423 switch (outputPolicy) { 424 case SKIP: 425 return new InputJarEntryInstructions(InputJarEntryInstructions.OutputPolicy.SKIP); 426 case OUTPUT: 427 return new InputJarEntryInstructions(InputJarEntryInstructions.OutputPolicy.OUTPUT); 428 case OUTPUT_BY_ENGINE: 429 if (V1SchemeSigner.MANIFEST_ENTRY_NAME.equals(entryName)) { 430 // We copy the main section of the JAR manifest from input to output. Thus, this 431 // invalidates v1 signature and we need to see the entry's data. 432 mInputJarManifestEntryDataRequest = new GetJarEntryDataRequest(entryName); 433 return new InputJarEntryInstructions( 434 InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE, 435 mInputJarManifestEntryDataRequest); 436 } 437 return new InputJarEntryInstructions( 438 InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE); 439 default: 440 throw new RuntimeException("Unsupported output policy: " + outputPolicy); 441 } 442 } 443 444 @Override 445 public InspectJarEntryRequest outputJarEntry(String entryName) { 446 checkNotClosed(); 447 invalidateV2Signature(); 448 449 if ((!mDebuggableApkPermitted) 450 && (ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME.equals(entryName))) { 451 forgetOutputApkDebuggableStatus(); 452 } 453 454 if (!mV1SigningEnabled) { 455 // No need to inspect JAR entries when v1 signing is not enabled. 456 if ((!mDebuggableApkPermitted) 457 && (ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME.equals(entryName))) { 458 // To reject debuggable APKs we need to inspect the APK's AndroidManifest.xml to 459 // check whether it declares that the APK is debuggable 460 mOutputAndroidManifestEntryDataRequest = new GetJarEntryDataRequest(entryName); 461 return mOutputAndroidManifestEntryDataRequest; 462 } 463 return null; 464 } 465 // v1 signing is enabled 466 467 if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName)) { 468 // This entry is covered by v1 signature. We thus need to inspect the entry's data to 469 // compute its digest(s) for v1 signature. 470 471 // TODO: Handle the case where other signer's v1 signatures are present and need to be 472 // preserved. In that scenario we can't modify MANIFEST.MF and add/remove JAR entries 473 // covered by v1 signature. 474 invalidateV1Signature(); 475 GetJarEntryDataDigestRequest dataDigestRequest = 476 new GetJarEntryDataDigestRequest( 477 entryName, 478 V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm)); 479 mOutputJarEntryDigestRequests.put(entryName, dataDigestRequest); 480 mOutputJarEntryDigests.remove(entryName); 481 482 if ((!mDebuggableApkPermitted) 483 && (ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME.equals(entryName))) { 484 // To reject debuggable APKs we need to inspect the APK's AndroidManifest.xml to 485 // check whether it declares that the APK is debuggable 486 mOutputAndroidManifestEntryDataRequest = new GetJarEntryDataRequest(entryName); 487 return new CompoundInspectJarEntryRequest( 488 entryName, mOutputAndroidManifestEntryDataRequest, dataDigestRequest); 489 } 490 491 return dataDigestRequest; 492 } 493 494 if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) { 495 // This entry is part of v1 signature generated by this engine. We need to check whether 496 // the entry's data is as output by the engine. 497 invalidateV1Signature(); 498 GetJarEntryDataRequest dataRequest; 499 if (V1SchemeSigner.MANIFEST_ENTRY_NAME.equals(entryName)) { 500 dataRequest = new GetJarEntryDataRequest(entryName); 501 mInputJarManifestEntryDataRequest = dataRequest; 502 } else { 503 // If this entry is part of v1 signature which has been emitted by this engine, 504 // check whether the output entry's data matches what the engine emitted. 505 dataRequest = 506 (mEmittedSignatureJarEntryData.containsKey(entryName)) 507 ? new GetJarEntryDataRequest(entryName) : null; 508 } 509 510 if (dataRequest != null) { 511 mOutputSignatureJarEntryDataRequests.put(entryName, dataRequest); 512 } 513 return dataRequest; 514 } 515 516 // This entry is not covered by v1 signature and isn't part of v1 signature. 517 return null; 518 } 519 520 @Override 521 public InputJarEntryInstructions.OutputPolicy inputJarEntryRemoved(String entryName) { 522 checkNotClosed(); 523 return getInputJarEntryOutputPolicy(entryName); 524 } 525 526 @Override 527 public void outputJarEntryRemoved(String entryName) { 528 checkNotClosed(); 529 invalidateV2Signature(); 530 if (!mV1SigningEnabled) { 531 return; 532 } 533 534 if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName)) { 535 // This entry is covered by v1 signature. 536 invalidateV1Signature(); 537 mOutputJarEntryDigests.remove(entryName); 538 mOutputJarEntryDigestRequests.remove(entryName); 539 mOutputSignatureJarEntryDataRequests.remove(entryName); 540 return; 541 } 542 543 if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) { 544 // This entry is part of the v1 signature generated by this engine. 545 invalidateV1Signature(); 546 return; 547 } 548 } 549 550 @Override 551 public OutputJarSignatureRequest outputJarEntries() 552 throws ApkFormatException, InvalidKeyException, SignatureException, 553 NoSuchAlgorithmException { 554 checkNotClosed(); 555 556 if (!mV1SignaturePending) { 557 return null; 558 } 559 560 if ((mInputJarManifestEntryDataRequest != null) 561 && (!mInputJarManifestEntryDataRequest.isDone())) { 562 throw new IllegalStateException( 563 "Still waiting to inspect input APK's " 564 + mInputJarManifestEntryDataRequest.getEntryName()); 565 } 566 567 for (GetJarEntryDataDigestRequest digestRequest 568 : mOutputJarEntryDigestRequests.values()) { 569 String entryName = digestRequest.getEntryName(); 570 if (!digestRequest.isDone()) { 571 throw new IllegalStateException( 572 "Still waiting to inspect output APK's " + entryName); 573 } 574 mOutputJarEntryDigests.put(entryName, digestRequest.getDigest()); 575 } 576 mOutputJarEntryDigestRequests.clear(); 577 578 for (GetJarEntryDataRequest dataRequest : mOutputSignatureJarEntryDataRequests.values()) { 579 if (!dataRequest.isDone()) { 580 throw new IllegalStateException( 581 "Still waiting to inspect output APK's " + dataRequest.getEntryName()); 582 } 583 } 584 585 List<Integer> apkSigningSchemeIds = new ArrayList<>(); 586 if (mV2SigningEnabled) { 587 apkSigningSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); 588 } 589 if (mV3SigningEnabled) { 590 apkSigningSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); 591 } 592 byte[] inputJarManifest = 593 (mInputJarManifestEntryDataRequest != null) 594 ? mInputJarManifestEntryDataRequest.getData() : null; 595 596 // Check whether the most recently used signature (if present) is still fine. 597 checkOutputApkNotDebuggableIfDebuggableMustBeRejected(); 598 List<Pair<String, byte[]>> signatureZipEntries; 599 if ((mAddV1SignatureRequest == null) || (!mAddV1SignatureRequest.isDone())) { 600 try { 601 signatureZipEntries = 602 V1SchemeSigner.sign( 603 mV1SignerConfigs, 604 mV1ContentDigestAlgorithm, 605 mOutputJarEntryDigests, 606 apkSigningSchemeIds, 607 inputJarManifest, 608 mCreatedBy); 609 } catch (CertificateException e) { 610 throw new SignatureException("Failed to generate v1 signature", e); 611 } 612 } else { 613 V1SchemeSigner.OutputManifestFile newManifest = 614 V1SchemeSigner.generateManifestFile( 615 mV1ContentDigestAlgorithm, 616 mOutputJarEntryDigests, 617 inputJarManifest); 618 byte[] emittedSignatureManifest = 619 mEmittedSignatureJarEntryData.get(V1SchemeSigner.MANIFEST_ENTRY_NAME); 620 if (!Arrays.equals(newManifest.contents, emittedSignatureManifest)) { 621 // Emitted v1 signature is no longer valid. 622 try { 623 signatureZipEntries = 624 V1SchemeSigner.signManifest( 625 mV1SignerConfigs, 626 mV1ContentDigestAlgorithm, 627 apkSigningSchemeIds, 628 mCreatedBy, 629 newManifest); 630 } catch (CertificateException e) { 631 throw new SignatureException("Failed to generate v1 signature", e); 632 } 633 } else { 634 // Emitted v1 signature is still valid. Check whether the signature is there in the 635 // output. 636 signatureZipEntries = new ArrayList<>(); 637 for (Map.Entry<String, byte[]> expectedOutputEntry 638 : mEmittedSignatureJarEntryData.entrySet()) { 639 String entryName = expectedOutputEntry.getKey(); 640 byte[] expectedData = expectedOutputEntry.getValue(); 641 GetJarEntryDataRequest actualDataRequest = 642 mOutputSignatureJarEntryDataRequests.get(entryName); 643 if (actualDataRequest == null) { 644 // This signature entry hasn't been output. 645 signatureZipEntries.add(Pair.of(entryName, expectedData)); 646 continue; 647 } 648 byte[] actualData = actualDataRequest.getData(); 649 if (!Arrays.equals(expectedData, actualData)) { 650 signatureZipEntries.add(Pair.of(entryName, expectedData)); 651 } 652 } 653 if (signatureZipEntries.isEmpty()) { 654 // v1 signature in the output is valid 655 return null; 656 } 657 // v1 signature in the output is not valid. 658 } 659 } 660 661 if (signatureZipEntries.isEmpty()) { 662 // v1 signature in the output is valid 663 mV1SignaturePending = false; 664 return null; 665 } 666 667 List<OutputJarSignatureRequest.JarEntry> sigEntries = 668 new ArrayList<>(signatureZipEntries.size()); 669 for (Pair<String, byte[]> entry : signatureZipEntries) { 670 String entryName = entry.getFirst(); 671 byte[] entryData = entry.getSecond(); 672 sigEntries.add(new OutputJarSignatureRequest.JarEntry(entryName, entryData)); 673 mEmittedSignatureJarEntryData.put(entryName, entryData); 674 } 675 mAddV1SignatureRequest = new OutputJarSignatureRequestImpl(sigEntries); 676 return mAddV1SignatureRequest; 677 } 678 679 @Deprecated 680 @Override 681 public OutputApkSigningBlockRequest outputZipSections( 682 DataSource zipEntries, 683 DataSource zipCentralDirectory, 684 DataSource zipEocd) 685 throws IOException, InvalidKeyException, SignatureException, 686 NoSuchAlgorithmException { 687 return outputZipSectionsInternal(zipEntries, zipCentralDirectory, zipEocd, false); 688 } 689 690 @Override 691 public OutputApkSigningBlockRequest2 outputZipSections2( 692 DataSource zipEntries, 693 DataSource zipCentralDirectory, 694 DataSource zipEocd) 695 throws IOException, InvalidKeyException, SignatureException, 696 NoSuchAlgorithmException { 697 return outputZipSectionsInternal(zipEntries, zipCentralDirectory, zipEocd, true); 698 } 699 700 private OutputApkSigningBlockRequestImpl outputZipSectionsInternal( 701 DataSource zipEntries, 702 DataSource zipCentralDirectory, 703 DataSource zipEocd, 704 boolean apkSigningBlockPaddingSupported) 705 throws IOException, InvalidKeyException, SignatureException, 706 NoSuchAlgorithmException { 707 checkNotClosed(); 708 checkV1SigningDoneIfEnabled(); 709 if (!mV2SigningEnabled && !mV3SigningEnabled) { 710 return null; 711 } 712 checkOutputApkNotDebuggableIfDebuggableMustBeRejected(); 713 714 // adjust to proper padding 715 Pair<DataSource, Integer> paddingPair = 716 ApkSigningBlockUtils.generateApkSigningBlockPadding(zipEntries, 717 apkSigningBlockPaddingSupported); 718 DataSource beforeCentralDir = paddingPair.getFirst(); 719 int padSizeBeforeApkSigningBlock = paddingPair.getSecond(); 720 DataSource eocd = 721 ApkSigningBlockUtils.copyWithModifiedCDOffset(beforeCentralDir, zipEocd); 722 723 List<Pair<byte[], Integer>> signingSchemeBlocks = new ArrayList<>(); 724 725 // create APK Signature Scheme V2 Signature if requested 726 if (mV2SigningEnabled) { 727 invalidateV2Signature(); 728 List<ApkSigningBlockUtils.SignerConfig> v2SignerConfigs = 729 createV2SignerConfigs(apkSigningBlockPaddingSupported); 730 signingSchemeBlocks.add( 731 V2SchemeSigner.generateApkSignatureSchemeV2Block(beforeCentralDir, 732 zipCentralDirectory, eocd, v2SignerConfigs, mV3SigningEnabled)); 733 } 734 if (mV3SigningEnabled) { 735 invalidateV3Signature(); 736 List<ApkSigningBlockUtils.SignerConfig> v3SignerConfigs = 737 createV3SignerConfigs(apkSigningBlockPaddingSupported); 738 signingSchemeBlocks.add( 739 V3SchemeSigner.generateApkSignatureSchemeV3Block(beforeCentralDir, 740 zipCentralDirectory, eocd, v3SignerConfigs)); 741 } 742 743 // create APK Signing Block with v2 and/or v3 blocks 744 byte[] apkSigningBlock = 745 ApkSigningBlockUtils.generateApkSigningBlock(signingSchemeBlocks); 746 747 mAddSigningBlockRequest = new OutputApkSigningBlockRequestImpl(apkSigningBlock, 748 padSizeBeforeApkSigningBlock); 749 return mAddSigningBlockRequest; 750 } 751 752 @Override 753 public void outputDone() { 754 checkNotClosed(); 755 checkV1SigningDoneIfEnabled(); 756 checkSigningBlockDoneIfEnabled(); 757 } 758 759 @Override 760 public void close() { 761 mClosed = true; 762 763 mAddV1SignatureRequest = null; 764 mInputJarManifestEntryDataRequest = null; 765 mOutputAndroidManifestEntryDataRequest = null; 766 mDebuggable = null; 767 mOutputJarEntryDigestRequests.clear(); 768 mOutputJarEntryDigests.clear(); 769 mEmittedSignatureJarEntryData.clear(); 770 mOutputSignatureJarEntryDataRequests.clear(); 771 772 mAddSigningBlockRequest = null; 773 } 774 775 private void invalidateV1Signature() { 776 if (mV1SigningEnabled) { 777 mV1SignaturePending = true; 778 } 779 invalidateV2Signature(); 780 } 781 782 private void invalidateV2Signature() { 783 if (mV2SigningEnabled) { 784 mV2SignaturePending = true; 785 mAddSigningBlockRequest = null; 786 } 787 } 788 789 private void invalidateV3Signature() { 790 if (mV3SigningEnabled) { 791 mV3SignaturePending = true; 792 mAddSigningBlockRequest = null; 793 } 794 } 795 796 private void checkNotClosed() { 797 if (mClosed) { 798 throw new IllegalStateException("Engine closed"); 799 } 800 } 801 802 private void checkV1SigningDoneIfEnabled() { 803 if (!mV1SignaturePending) { 804 return; 805 } 806 807 if (mAddV1SignatureRequest == null) { 808 throw new IllegalStateException( 809 "v1 signature (JAR signature) not yet generated. Skipped outputJarEntries()?"); 810 } 811 if (!mAddV1SignatureRequest.isDone()) { 812 throw new IllegalStateException( 813 "v1 signature (JAR signature) addition requested by outputJarEntries() hasn't" 814 + " been fulfilled"); 815 } 816 for (Map.Entry<String, byte[]> expectedOutputEntry 817 : mEmittedSignatureJarEntryData.entrySet()) { 818 String entryName = expectedOutputEntry.getKey(); 819 byte[] expectedData = expectedOutputEntry.getValue(); 820 GetJarEntryDataRequest actualDataRequest = 821 mOutputSignatureJarEntryDataRequests.get(entryName); 822 if (actualDataRequest == null) { 823 throw new IllegalStateException( 824 "APK entry " + entryName + " not yet output despite this having been" 825 + " requested"); 826 } else if (!actualDataRequest.isDone()) { 827 throw new IllegalStateException( 828 "Still waiting to inspect output APK's " + entryName); 829 } 830 byte[] actualData = actualDataRequest.getData(); 831 if (!Arrays.equals(expectedData, actualData)) { 832 throw new IllegalStateException( 833 "Output APK entry " + entryName + " data differs from what was requested"); 834 } 835 } 836 mV1SignaturePending = false; 837 } 838 839 private void checkSigningBlockDoneIfEnabled() { 840 if (!mV2SignaturePending && !mV3SignaturePending) { 841 return; 842 } 843 if (mAddSigningBlockRequest == null) { 844 throw new IllegalStateException( 845 "Signed APK Signing BLock not yet generated. Skipped outputZipSections()?"); 846 } 847 if (!mAddSigningBlockRequest.isDone()) { 848 throw new IllegalStateException( 849 "APK Signing Block addition of signature(s) requested by" 850 + " outputZipSections() hasn't been fulfilled yet"); 851 } 852 mAddSigningBlockRequest = null; 853 mV2SignaturePending = false; 854 mV3SignaturePending = false; 855 } 856 857 private void checkOutputApkNotDebuggableIfDebuggableMustBeRejected() 858 throws SignatureException { 859 if (mDebuggableApkPermitted) { 860 return; 861 } 862 863 try { 864 if (isOutputApkDebuggable()) { 865 throw new SignatureException( 866 "APK is debuggable (see android:debuggable attribute) and this engine is" 867 + " configured to refuse to sign debuggable APKs"); 868 } 869 } catch (ApkFormatException e) { 870 throw new SignatureException("Failed to determine whether the APK is debuggable", e); 871 } 872 } 873 874 /** 875 * Returns whether the output APK is debuggable according to its 876 * {@code android:debuggable} declaration. 877 */ 878 private boolean isOutputApkDebuggable() throws ApkFormatException { 879 if (mDebuggable != null) { 880 return mDebuggable; 881 } 882 883 if (mOutputAndroidManifestEntryDataRequest == null) { 884 throw new IllegalStateException( 885 "Cannot determine debuggable status of output APK because " 886 + ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME 887 + " entry contents have not yet been requested"); 888 } 889 890 if (!mOutputAndroidManifestEntryDataRequest.isDone()) { 891 throw new IllegalStateException( 892 "Still waiting to inspect output APK's " 893 + mOutputAndroidManifestEntryDataRequest.getEntryName()); 894 } 895 mDebuggable = 896 ApkUtils.getDebuggableFromBinaryAndroidManifest( 897 ByteBuffer.wrap(mOutputAndroidManifestEntryDataRequest.getData())); 898 return mDebuggable; 899 } 900 901 private void forgetOutputApkDebuggableStatus() { 902 mDebuggable = null; 903 } 904 905 /** 906 * Returns the output policy for the provided input JAR entry. 907 */ 908 private InputJarEntryInstructions.OutputPolicy getInputJarEntryOutputPolicy(String entryName) { 909 if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) { 910 return InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE; 911 } 912 if ((mOtherSignersSignaturesPreserved) 913 || (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName))) { 914 return InputJarEntryInstructions.OutputPolicy.OUTPUT; 915 } 916 return InputJarEntryInstructions.OutputPolicy.SKIP; 917 } 918 919 private static class OutputJarSignatureRequestImpl implements OutputJarSignatureRequest { 920 private final List<JarEntry> mAdditionalJarEntries; 921 private volatile boolean mDone; 922 923 private OutputJarSignatureRequestImpl(List<JarEntry> additionalZipEntries) { 924 mAdditionalJarEntries = 925 Collections.unmodifiableList(new ArrayList<>(additionalZipEntries)); 926 } 927 928 @Override 929 public List<JarEntry> getAdditionalJarEntries() { 930 return mAdditionalJarEntries; 931 } 932 933 @Override 934 public void done() { 935 mDone = true; 936 } 937 938 private boolean isDone() { 939 return mDone; 940 } 941 } 942 943 @SuppressWarnings("deprecation") 944 private static class OutputApkSigningBlockRequestImpl 945 implements OutputApkSigningBlockRequest, OutputApkSigningBlockRequest2 { 946 private final byte[] mApkSigningBlock; 947 private final int mPaddingBeforeApkSigningBlock; 948 private volatile boolean mDone; 949 950 private OutputApkSigningBlockRequestImpl(byte[] apkSigingBlock, int paddingBefore) { 951 mApkSigningBlock = apkSigingBlock.clone(); 952 mPaddingBeforeApkSigningBlock = paddingBefore; 953 } 954 955 @Override 956 public byte[] getApkSigningBlock() { 957 return mApkSigningBlock.clone(); 958 } 959 960 @Override 961 public void done() { 962 mDone = true; 963 } 964 965 private boolean isDone() { 966 return mDone; 967 } 968 969 @Override 970 public int getPaddingSizeBeforeApkSigningBlock() { 971 return mPaddingBeforeApkSigningBlock; 972 } 973 } 974 975 /** 976 * JAR entry inspection request which obtain the entry's uncompressed data. 977 */ 978 private static class GetJarEntryDataRequest implements InspectJarEntryRequest { 979 private final String mEntryName; 980 private final Object mLock = new Object(); 981 982 private boolean mDone; 983 private DataSink mDataSink; 984 private ByteArrayOutputStream mDataSinkBuf; 985 986 private GetJarEntryDataRequest(String entryName) { 987 mEntryName = entryName; 988 } 989 990 @Override 991 public String getEntryName() { 992 return mEntryName; 993 } 994 995 @Override 996 public DataSink getDataSink() { 997 synchronized (mLock) { 998 checkNotDone(); 999 if (mDataSinkBuf == null) { 1000 mDataSinkBuf = new ByteArrayOutputStream(); 1001 } 1002 if (mDataSink == null) { 1003 mDataSink = DataSinks.asDataSink(mDataSinkBuf); 1004 } 1005 return mDataSink; 1006 } 1007 } 1008 1009 @Override 1010 public void done() { 1011 synchronized (mLock) { 1012 if (mDone) { 1013 return; 1014 } 1015 mDone = true; 1016 } 1017 } 1018 1019 private boolean isDone() { 1020 synchronized (mLock) { 1021 return mDone; 1022 } 1023 } 1024 1025 private void checkNotDone() throws IllegalStateException { 1026 synchronized (mLock) { 1027 if (mDone) { 1028 throw new IllegalStateException("Already done"); 1029 } 1030 } 1031 } 1032 1033 private byte[] getData() { 1034 synchronized (mLock) { 1035 if (!mDone) { 1036 throw new IllegalStateException("Not yet done"); 1037 } 1038 return (mDataSinkBuf != null) ? mDataSinkBuf.toByteArray() : new byte[0]; 1039 } 1040 } 1041 } 1042 1043 /** 1044 * JAR entry inspection request which obtains the digest of the entry's uncompressed data. 1045 */ 1046 private static class GetJarEntryDataDigestRequest implements InspectJarEntryRequest { 1047 private final String mEntryName; 1048 private final String mJcaDigestAlgorithm; 1049 private final Object mLock = new Object(); 1050 1051 private boolean mDone; 1052 private DataSink mDataSink; 1053 private MessageDigest mMessageDigest; 1054 private byte[] mDigest; 1055 1056 private GetJarEntryDataDigestRequest(String entryName, String jcaDigestAlgorithm) { 1057 mEntryName = entryName; 1058 mJcaDigestAlgorithm = jcaDigestAlgorithm; 1059 } 1060 1061 @Override 1062 public String getEntryName() { 1063 return mEntryName; 1064 } 1065 1066 @Override 1067 public DataSink getDataSink() { 1068 synchronized (mLock) { 1069 checkNotDone(); 1070 if (mDataSink == null) { 1071 mDataSink = DataSinks.asDataSink(getMessageDigest()); 1072 } 1073 return mDataSink; 1074 } 1075 } 1076 1077 private MessageDigest getMessageDigest() { 1078 synchronized (mLock) { 1079 if (mMessageDigest == null) { 1080 try { 1081 mMessageDigest = MessageDigest.getInstance(mJcaDigestAlgorithm); 1082 } catch (NoSuchAlgorithmException e) { 1083 throw new RuntimeException( 1084 mJcaDigestAlgorithm + " MessageDigest not available", e); 1085 } 1086 } 1087 return mMessageDigest; 1088 } 1089 } 1090 1091 @Override 1092 public void done() { 1093 synchronized (mLock) { 1094 if (mDone) { 1095 return; 1096 } 1097 mDone = true; 1098 mDigest = getMessageDigest().digest(); 1099 mMessageDigest = null; 1100 mDataSink = null; 1101 } 1102 } 1103 1104 private boolean isDone() { 1105 synchronized (mLock) { 1106 return mDone; 1107 } 1108 } 1109 1110 private void checkNotDone() throws IllegalStateException { 1111 synchronized (mLock) { 1112 if (mDone) { 1113 throw new IllegalStateException("Already done"); 1114 } 1115 } 1116 } 1117 1118 private byte[] getDigest() { 1119 synchronized (mLock) { 1120 if (!mDone) { 1121 throw new IllegalStateException("Not yet done"); 1122 } 1123 return mDigest.clone(); 1124 } 1125 } 1126 } 1127 1128 /** 1129 * JAR entry inspection request which transparently satisfies multiple such requests. 1130 */ 1131 private static class CompoundInspectJarEntryRequest implements InspectJarEntryRequest { 1132 private final String mEntryName; 1133 private final InspectJarEntryRequest[] mRequests; 1134 private final Object mLock = new Object(); 1135 1136 private DataSink mSink; 1137 1138 private CompoundInspectJarEntryRequest( 1139 String entryName, InspectJarEntryRequest... requests) { 1140 mEntryName = entryName; 1141 mRequests = requests; 1142 } 1143 1144 @Override 1145 public String getEntryName() { 1146 return mEntryName; 1147 } 1148 1149 @Override 1150 public DataSink getDataSink() { 1151 synchronized (mLock) { 1152 if (mSink == null) { 1153 DataSink[] sinks = new DataSink[mRequests.length]; 1154 for (int i = 0; i < sinks.length; i++) { 1155 sinks[i] = mRequests[i].getDataSink(); 1156 } 1157 mSink = new TeeDataSink(sinks); 1158 } 1159 return mSink; 1160 } 1161 } 1162 1163 @Override 1164 public void done() { 1165 for (InspectJarEntryRequest request : mRequests) { 1166 request.done(); 1167 } 1168 } 1169 } 1170 1171 /** 1172 * Configuration of a signer. 1173 * 1174 * <p>Use {@link Builder} to obtain configuration instances. 1175 */ 1176 public static class SignerConfig { 1177 private final String mName; 1178 private final PrivateKey mPrivateKey; 1179 private final List<X509Certificate> mCertificates; 1180 1181 private SignerConfig( 1182 String name, 1183 PrivateKey privateKey, 1184 List<X509Certificate> certificates) { 1185 mName = name; 1186 mPrivateKey = privateKey; 1187 mCertificates = Collections.unmodifiableList(new ArrayList<>(certificates)); 1188 } 1189 1190 /** 1191 * Returns the name of this signer. 1192 */ 1193 public String getName() { 1194 return mName; 1195 } 1196 1197 /** 1198 * Returns the signing key of this signer. 1199 */ 1200 public PrivateKey getPrivateKey() { 1201 return mPrivateKey; 1202 } 1203 1204 /** 1205 * Returns the certificate(s) of this signer. The first certificate's public key corresponds 1206 * to this signer's private key. 1207 */ 1208 public List<X509Certificate> getCertificates() { 1209 return mCertificates; 1210 } 1211 1212 /** 1213 * Builder of {@link SignerConfig} instances. 1214 */ 1215 public static class Builder { 1216 private final String mName; 1217 private final PrivateKey mPrivateKey; 1218 private final List<X509Certificate> mCertificates; 1219 1220 /** 1221 * Constructs a new {@code Builder}. 1222 * 1223 * @param name signer's name. The name is reflected in the name of files comprising the 1224 * JAR signature of the APK. 1225 * @param privateKey signing key 1226 * @param certificates list of one or more X.509 certificates. The subject public key of 1227 * the first certificate must correspond to the {@code privateKey}. 1228 */ 1229 public Builder( 1230 String name, 1231 PrivateKey privateKey, 1232 List<X509Certificate> certificates) { 1233 if (name.isEmpty()) { 1234 throw new IllegalArgumentException("Empty name"); 1235 } 1236 mName = name; 1237 mPrivateKey = privateKey; 1238 mCertificates = new ArrayList<>(certificates); 1239 } 1240 1241 /** 1242 * Returns a new {@code SignerConfig} instance configured based on the configuration of 1243 * this builder. 1244 */ 1245 public SignerConfig build() { 1246 return new SignerConfig( 1247 mName, 1248 mPrivateKey, 1249 mCertificates); 1250 } 1251 } 1252 } 1253 1254 /** 1255 * Builder of {@link DefaultApkSignerEngine} instances. 1256 */ 1257 public static class Builder { 1258 private List<SignerConfig> mSignerConfigs; 1259 private final int mMinSdkVersion; 1260 1261 private boolean mV1SigningEnabled = true; 1262 private boolean mV2SigningEnabled = true; 1263 private boolean mV3SigningEnabled = true; 1264 private boolean mDebuggableApkPermitted = true; 1265 private boolean mOtherSignersSignaturesPreserved; 1266 private String mCreatedBy = "1.0 (Android)"; 1267 1268 private SigningCertificateLineage mSigningCertificateLineage; 1269 1270 // APK Signature Scheme v3 only supports a single signing certificate, so to move to v3 1271 // signing by default, but not require prior clients to update to explicitly disable v3 1272 // signing for multiple signers, we modify the mV3SigningEnabled depending on the provided 1273 // inputs (multiple signers and mSigningCertificateLineage in particular). Maintain two 1274 // extra variables to record whether or not mV3SigningEnabled has been set directly by a 1275 // client and so should override the default behavior. 1276 private boolean mV3SigningExplicitlyDisabled = false; 1277 private boolean mV3SigningExplicitlyEnabled = false; 1278 1279 /** 1280 * Constructs a new {@code Builder}. 1281 * 1282 * @param signerConfigs information about signers with which the APK will be signed. At 1283 * least one signer configuration must be provided. 1284 * @param minSdkVersion API Level of the oldest Android platform on which the APK is 1285 * supposed to be installed. See {@code minSdkVersion} attribute in the APK's 1286 * {@code AndroidManifest.xml}. The higher the version, the stronger signing features 1287 * will be enabled. 1288 */ 1289 public Builder( 1290 List<SignerConfig> signerConfigs, 1291 int minSdkVersion) { 1292 if (signerConfigs.isEmpty()) { 1293 throw new IllegalArgumentException("At least one signer config must be provided"); 1294 } 1295 if (signerConfigs.size() > 1) { 1296 // APK Signature Scheme v3 only supports single signer, unless a 1297 // SigningCertificateLineage is provided, in which case this will be reset to true, 1298 // since we don't yet have a v4 scheme about which to worry 1299 mV3SigningEnabled = false; 1300 } 1301 mSignerConfigs = new ArrayList<>(signerConfigs); 1302 mMinSdkVersion = minSdkVersion; 1303 } 1304 1305 /** 1306 * Returns a new {@code DefaultApkSignerEngine} instance configured based on the 1307 * configuration of this builder. 1308 */ 1309 public DefaultApkSignerEngine build() throws InvalidKeyException { 1310 1311 if (mV3SigningExplicitlyDisabled && mV3SigningExplicitlyEnabled) { 1312 throw new IllegalStateException("Builder configured to both enable and disable APK " 1313 + "Signature Scheme v3 signing"); 1314 } 1315 if (mV3SigningExplicitlyDisabled) { 1316 mV3SigningEnabled = false; 1317 } else if (mV3SigningExplicitlyEnabled) { 1318 mV3SigningEnabled = true; 1319 } 1320 1321 // make sure our signers are appropriately setup 1322 if (mSigningCertificateLineage != null) { 1323 try { 1324 mSignerConfigs = mSigningCertificateLineage.sortSignerConfigs(mSignerConfigs); 1325 if (!mV3SigningEnabled && mSignerConfigs.size() > 1) { 1326 1327 // this is a strange situation: we've provided a valid rotation history, but 1328 // are only signing with v1/v2. blow up, since we don't know for sure with 1329 // which signer the user intended to sign 1330 throw new IllegalStateException("Provided multiple signers which are part " 1331 + "of the SigningCertificateLineage, but not signing with APK " 1332 + "Signature Scheme v3"); 1333 } 1334 } catch (IllegalArgumentException e) { 1335 throw new IllegalStateException("Provided signer configs do not match the " 1336 + "provided SigningCertificateLineage", e); 1337 } 1338 } else if (mV3SigningEnabled && mSignerConfigs.size() > 1) { 1339 throw new IllegalStateException("Multiple signing certificates provided for use " 1340 + "with APK Signature Scheme v3 without an accompanying SigningCertificateLineage"); 1341 } 1342 1343 return new DefaultApkSignerEngine( 1344 mSignerConfigs, 1345 mMinSdkVersion, 1346 mV1SigningEnabled, 1347 mV2SigningEnabled, 1348 mV3SigningEnabled, 1349 mDebuggableApkPermitted, 1350 mOtherSignersSignaturesPreserved, 1351 mCreatedBy, 1352 mSigningCertificateLineage); 1353 } 1354 1355 /** 1356 * Sets whether the APK should be signed using JAR signing (aka v1 signature scheme). 1357 * 1358 * <p>By default, the APK will be signed using this scheme. 1359 */ 1360 public Builder setV1SigningEnabled(boolean enabled) { 1361 mV1SigningEnabled = enabled; 1362 return this; 1363 } 1364 1365 /** 1366 * Sets whether the APK should be signed using APK Signature Scheme v2 (aka v2 signature 1367 * scheme). 1368 * 1369 * <p>By default, the APK will be signed using this scheme. 1370 */ 1371 public Builder setV2SigningEnabled(boolean enabled) { 1372 mV2SigningEnabled = enabled; 1373 return this; 1374 } 1375 1376 /** 1377 * Sets whether the APK should be signed using APK Signature Scheme v3 (aka v3 signature 1378 * scheme). 1379 * 1380 * <p>By default, the APK will be signed using this scheme. 1381 */ 1382 public Builder setV3SigningEnabled(boolean enabled) { 1383 mV3SigningEnabled = enabled; 1384 if (enabled) { 1385 mV3SigningExplicitlyEnabled = true; 1386 } else { 1387 mV3SigningExplicitlyDisabled = true; 1388 } 1389 return this; 1390 } 1391 1392 /** 1393 * Sets whether the APK should be signed even if it is marked as debuggable 1394 * ({@code android:debuggable="true"} in its {@code AndroidManifest.xml}). For backward 1395 * compatibility reasons, the default value of this setting is {@code true}. 1396 * 1397 * <p>It is dangerous to sign debuggable APKs with production/release keys because Android 1398 * platform loosens security checks for such APKs. For example, arbitrary unauthorized code 1399 * may be executed in the context of such an app by anybody with ADB shell access. 1400 */ 1401 public Builder setDebuggableApkPermitted(boolean permitted) { 1402 mDebuggableApkPermitted = permitted; 1403 return this; 1404 } 1405 1406 /** 1407 * Sets whether signatures produced by signers other than the ones configured in this engine 1408 * should be copied from the input APK to the output APK. 1409 * 1410 * <p>By default, signatures of other signers are omitted from the output APK. 1411 */ 1412 public Builder setOtherSignersSignaturesPreserved(boolean preserved) { 1413 mOtherSignersSignaturesPreserved = preserved; 1414 return this; 1415 } 1416 1417 /** 1418 * Sets the value of the {@code Created-By} field in JAR signature files. 1419 */ 1420 public Builder setCreatedBy(String createdBy) { 1421 if (createdBy == null) { 1422 throw new NullPointerException(); 1423 } 1424 mCreatedBy = createdBy; 1425 return this; 1426 } 1427 1428 /** 1429 * Sets the {@link SigningCertificateLineage} to use with the v3 signature scheme. This 1430 * structure provides proof of signing certificate rotation linking {@link SignerConfig} 1431 * objects to previous ones. 1432 */ 1433 public Builder setSigningCertificateLineage( 1434 SigningCertificateLineage signingCertificateLineage) { 1435 if (signingCertificateLineage != null) { 1436 mV3SigningEnabled = true; 1437 mSigningCertificateLineage = signingCertificateLineage; 1438 } 1439 return this; 1440 } 1441 } 1442 } 1443