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 static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME; 20 import static com.android.apksig.apk.ApkUtils.computeSha256DigestBytes; 21 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERITY_PADDING_BLOCK_ID; 22 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2; 23 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3; 24 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_JAR_SIGNATURE_SCHEME; 25 26 import com.android.apksig.apk.ApkFormatException; 27 import com.android.apksig.apk.ApkUtils; 28 import com.android.apksig.internal.apk.ApkSigningBlockUtils; 29 import com.android.apksig.internal.apk.ContentDigestAlgorithm; 30 import com.android.apksig.internal.apk.SignatureAlgorithm; 31 import com.android.apksig.internal.apk.stamp.V2SourceStampSigner; 32 import com.android.apksig.internal.apk.v1.DigestAlgorithm; 33 import com.android.apksig.internal.apk.v1.V1SchemeConstants; 34 import com.android.apksig.internal.apk.v1.V1SchemeSigner; 35 import com.android.apksig.internal.apk.v1.V1SchemeVerifier; 36 import com.android.apksig.internal.apk.v2.V2SchemeSigner; 37 import com.android.apksig.internal.apk.v3.V3SchemeSigner; 38 import com.android.apksig.internal.apk.v4.V4SchemeSigner; 39 import com.android.apksig.internal.apk.v4.V4Signature; 40 import com.android.apksig.internal.jar.ManifestParser; 41 import com.android.apksig.internal.util.AndroidSdkVersion; 42 import com.android.apksig.internal.util.Pair; 43 import com.android.apksig.internal.util.TeeDataSink; 44 import com.android.apksig.util.DataSink; 45 import com.android.apksig.util.DataSinks; 46 import com.android.apksig.util.DataSource; 47 import com.android.apksig.util.RunnablesExecutor; 48 49 import java.io.ByteArrayOutputStream; 50 import java.io.File; 51 import java.io.IOException; 52 import java.io.OutputStream; 53 import java.nio.ByteBuffer; 54 import java.security.InvalidKeyException; 55 import java.security.MessageDigest; 56 import java.security.NoSuchAlgorithmException; 57 import java.security.PrivateKey; 58 import java.security.PublicKey; 59 import java.security.SignatureException; 60 import java.security.cert.CertificateEncodingException; 61 import java.security.cert.CertificateException; 62 import java.security.cert.X509Certificate; 63 import java.util.ArrayList; 64 import java.util.Arrays; 65 import java.util.Collection; 66 import java.util.Collections; 67 import java.util.HashMap; 68 import java.util.HashSet; 69 import java.util.List; 70 import java.util.Map; 71 import java.util.Set; 72 73 /** 74 * Default implementation of {@link ApkSignerEngine}. 75 * 76 * <p>Use {@link Builder} to obtain instances of this engine. 77 */ 78 public class DefaultApkSignerEngine implements ApkSignerEngine { 79 80 // IMPLEMENTATION NOTE: This engine generates a signed APK as follows: 81 // 1. The engine asks its client to output input JAR entries which are not part of JAR 82 // signature. 83 // 2. If JAR signing (v1 signing) is enabled, the engine inspects the output JAR entries to 84 // compute their digests, to be placed into output META-INF/MANIFEST.MF. It also inspects 85 // the contents of input and output META-INF/MANIFEST.MF to borrow the main section of the 86 // file. It does not care about individual (i.e., JAR entry-specific) sections. It then 87 // emits the v1 signature (a set of JAR entries) and asks the client to output them. 88 // 3. If APK Signature Scheme v2 (v2 signing) is enabled, the engine emits an APK Signing Block 89 // from outputZipSections() and asks its client to insert this block into the output. 90 // 4. If APK Signature Scheme v3 (v3 signing) is enabled, the engine includes it in the APK 91 // Signing BLock output from outputZipSections() and asks its client to insert this block 92 // into the output. If both v2 and v3 signing is enabled, they are both added to the APK 93 // Signing Block before asking the client to insert it into the output. 94 95 private final boolean mV1SigningEnabled; 96 private final boolean mV2SigningEnabled; 97 private final boolean mV3SigningEnabled; 98 private final boolean mVerityEnabled; 99 private final boolean mDebuggableApkPermitted; 100 private final boolean mOtherSignersSignaturesPreserved; 101 private final String mCreatedBy; 102 private final List<SignerConfig> mSignerConfigs; 103 private final SignerConfig mSourceStampSignerConfig; 104 private final SigningCertificateLineage mSourceStampSigningCertificateLineage; 105 private final int mMinSdkVersion; 106 private final SigningCertificateLineage mSigningCertificateLineage; 107 108 private List<byte[]> mPreservedV2Signers = Collections.emptyList(); 109 private List<Pair<byte[], Integer>> mPreservedSignatureBlocks = Collections.emptyList(); 110 111 private List<V1SchemeSigner.SignerConfig> mV1SignerConfigs = Collections.emptyList(); 112 private DigestAlgorithm mV1ContentDigestAlgorithm; 113 114 private boolean mClosed; 115 116 private boolean mV1SignaturePending; 117 118 /** Names of JAR entries which this engine is expected to output as part of v1 signing. */ 119 private Set<String> mSignatureExpectedOutputJarEntryNames = Collections.emptySet(); 120 121 /** Requests for digests of output JAR entries. */ 122 private final Map<String, GetJarEntryDataDigestRequest> mOutputJarEntryDigestRequests = 123 new HashMap<>(); 124 125 /** Digests of output JAR entries. */ 126 private final Map<String, byte[]> mOutputJarEntryDigests = new HashMap<>(); 127 128 /** Data of JAR entries emitted by this engine as v1 signature. */ 129 private final Map<String, byte[]> mEmittedSignatureJarEntryData = new HashMap<>(); 130 131 /** Requests for data of output JAR entries which comprise the v1 signature. */ 132 private final Map<String, GetJarEntryDataRequest> mOutputSignatureJarEntryDataRequests = 133 new HashMap<>(); 134 /** 135 * Request to obtain the data of MANIFEST.MF or {@code null} if the request hasn't been issued. 136 */ 137 private GetJarEntryDataRequest mInputJarManifestEntryDataRequest; 138 139 /** 140 * Request to obtain the data of AndroidManifest.xml or {@code null} if the request hasn't been 141 * issued. 142 */ 143 private GetJarEntryDataRequest mOutputAndroidManifestEntryDataRequest; 144 145 /** 146 * Whether the package being signed is marked as {@code android:debuggable} or {@code null} if 147 * this is not yet known. 148 */ 149 private Boolean mDebuggable; 150 151 /** 152 * Request to output the emitted v1 signature or {@code null} if the request hasn't been issued. 153 */ 154 private OutputJarSignatureRequestImpl mAddV1SignatureRequest; 155 156 private boolean mV2SignaturePending; 157 private boolean mV3SignaturePending; 158 159 /** 160 * Request to output the emitted v2 and/or v3 signature(s) {@code null} if the request hasn't 161 * been issued. 162 */ 163 private OutputApkSigningBlockRequestImpl mAddSigningBlockRequest; 164 165 private RunnablesExecutor mExecutor = RunnablesExecutor.MULTI_THREADED; 166 167 /** 168 * A Set of block IDs to be discarded when requesting to preserve the original signatures. 169 */ 170 private static final Set<Integer> DISCARDED_SIGNATURE_BLOCK_IDS; 171 static { 172 DISCARDED_SIGNATURE_BLOCK_IDS = new HashSet<>(3); 173 // The verity padding block is recomputed on an 174 // ApkSigningBlockUtils.ANDROID_COMMON_PAGE_ALIGNMENT_BYTES boundary. 175 DISCARDED_SIGNATURE_BLOCK_IDS.add(VERITY_PADDING_BLOCK_ID); 176 // The source stamp block is not currently preserved; appending a new signature scheme 177 // block will invalidate the previous source stamp. 178 DISCARDED_SIGNATURE_BLOCK_IDS.add(Constants.V1_SOURCE_STAMP_BLOCK_ID); 179 DISCARDED_SIGNATURE_BLOCK_IDS.add(Constants.V2_SOURCE_STAMP_BLOCK_ID); 180 } 181 DefaultApkSignerEngine( List<SignerConfig> signerConfigs, SignerConfig sourceStampSignerConfig, SigningCertificateLineage sourceStampSigningCertificateLineage, int minSdkVersion, boolean v1SigningEnabled, boolean v2SigningEnabled, boolean v3SigningEnabled, boolean verityEnabled, boolean debuggableApkPermitted, boolean otherSignersSignaturesPreserved, String createdBy, SigningCertificateLineage signingCertificateLineage)182 private DefaultApkSignerEngine( 183 List<SignerConfig> signerConfigs, 184 SignerConfig sourceStampSignerConfig, 185 SigningCertificateLineage sourceStampSigningCertificateLineage, 186 int minSdkVersion, 187 boolean v1SigningEnabled, 188 boolean v2SigningEnabled, 189 boolean v3SigningEnabled, 190 boolean verityEnabled, 191 boolean debuggableApkPermitted, 192 boolean otherSignersSignaturesPreserved, 193 String createdBy, 194 SigningCertificateLineage signingCertificateLineage) 195 throws InvalidKeyException { 196 if (signerConfigs.isEmpty()) { 197 throw new IllegalArgumentException("At least one signer config must be provided"); 198 } 199 200 mV1SigningEnabled = v1SigningEnabled; 201 mV2SigningEnabled = v2SigningEnabled; 202 mV3SigningEnabled = v3SigningEnabled; 203 mVerityEnabled = verityEnabled; 204 mV1SignaturePending = v1SigningEnabled; 205 mV2SignaturePending = v2SigningEnabled; 206 mV3SignaturePending = v3SigningEnabled; 207 mDebuggableApkPermitted = debuggableApkPermitted; 208 mOtherSignersSignaturesPreserved = otherSignersSignaturesPreserved; 209 mCreatedBy = createdBy; 210 mSignerConfigs = signerConfigs; 211 mSourceStampSignerConfig = sourceStampSignerConfig; 212 mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage; 213 mMinSdkVersion = minSdkVersion; 214 mSigningCertificateLineage = signingCertificateLineage; 215 216 if (v1SigningEnabled) { 217 if (v3SigningEnabled) { 218 219 // v3 signing only supports single signers, of which the oldest (first) will be the 220 // one to use for v1 and v2 signing 221 SignerConfig oldestConfig = signerConfigs.get(0); 222 223 // in the event of signing certificate changes, make sure we have the oldest in the 224 // signing history to sign with v1 225 if (signingCertificateLineage != null) { 226 SigningCertificateLineage subLineage = 227 signingCertificateLineage.getSubLineage( 228 oldestConfig.mCertificates.get(0)); 229 if (subLineage.size() != 1) { 230 throw new IllegalArgumentException( 231 "v1 signing enabled but the oldest signer in the" 232 + " SigningCertificateLineage is missing. Please provide the" 233 + " oldest signer to enable v1 signing"); 234 } 235 } 236 createV1SignerConfigs(Collections.singletonList(oldestConfig), minSdkVersion); 237 } else { 238 createV1SignerConfigs(signerConfigs, minSdkVersion); 239 } 240 } 241 } 242 createV1SignerConfigs(List<SignerConfig> signerConfigs, int minSdkVersion)243 private void createV1SignerConfigs(List<SignerConfig> signerConfigs, int minSdkVersion) 244 throws InvalidKeyException { 245 mV1SignerConfigs = new ArrayList<>(signerConfigs.size()); 246 Map<String, Integer> v1SignerNameToSignerIndex = new HashMap<>(signerConfigs.size()); 247 DigestAlgorithm v1ContentDigestAlgorithm = null; 248 for (int i = 0; i < signerConfigs.size(); i++) { 249 SignerConfig signerConfig = signerConfigs.get(i); 250 List<X509Certificate> certificates = signerConfig.getCertificates(); 251 PublicKey publicKey = certificates.get(0).getPublicKey(); 252 253 String v1SignerName = V1SchemeSigner.getSafeSignerName(signerConfig.getName()); 254 // Check whether the signer's name is unique among all v1 signers 255 Integer indexOfOtherSignerWithSameName = v1SignerNameToSignerIndex.put(v1SignerName, i); 256 if (indexOfOtherSignerWithSameName != null) { 257 throw new IllegalArgumentException( 258 "Signers #" 259 + (indexOfOtherSignerWithSameName + 1) 260 + " and #" 261 + (i + 1) 262 + " have the same name: " 263 + v1SignerName 264 + ". v1 signer names must be unique"); 265 } 266 267 DigestAlgorithm v1SignatureDigestAlgorithm = 268 V1SchemeSigner.getSuggestedSignatureDigestAlgorithm(publicKey, minSdkVersion); 269 V1SchemeSigner.SignerConfig v1SignerConfig = new V1SchemeSigner.SignerConfig(); 270 v1SignerConfig.name = v1SignerName; 271 v1SignerConfig.privateKey = signerConfig.getPrivateKey(); 272 v1SignerConfig.certificates = certificates; 273 v1SignerConfig.signatureDigestAlgorithm = v1SignatureDigestAlgorithm; 274 v1SignerConfig.deterministicDsaSigning = signerConfig.getDeterministicDsaSigning(); 275 // For digesting contents of APK entries and of MANIFEST.MF, pick the algorithm 276 // of comparable strength to the digest algorithm used for computing the signature. 277 // When there are multiple signers, pick the strongest digest algorithm out of their 278 // signature digest algorithms. This avoids reducing the digest strength used by any 279 // of the signers to protect APK contents. 280 if (v1ContentDigestAlgorithm == null) { 281 v1ContentDigestAlgorithm = v1SignatureDigestAlgorithm; 282 } else { 283 if (DigestAlgorithm.BY_STRENGTH_COMPARATOR.compare( 284 v1SignatureDigestAlgorithm, v1ContentDigestAlgorithm) 285 > 0) { 286 v1ContentDigestAlgorithm = v1SignatureDigestAlgorithm; 287 } 288 } 289 mV1SignerConfigs.add(v1SignerConfig); 290 } 291 mV1ContentDigestAlgorithm = v1ContentDigestAlgorithm; 292 mSignatureExpectedOutputJarEntryNames = 293 V1SchemeSigner.getOutputEntryNames(mV1SignerConfigs); 294 } 295 createV2SignerConfigs( boolean apkSigningBlockPaddingSupported)296 private List<ApkSigningBlockUtils.SignerConfig> createV2SignerConfigs( 297 boolean apkSigningBlockPaddingSupported) throws InvalidKeyException { 298 if (mV3SigningEnabled) { 299 300 // v3 signing only supports single signers, of which the oldest (first) will be the one 301 // to use for v1 and v2 signing 302 List<ApkSigningBlockUtils.SignerConfig> signerConfig = new ArrayList<>(); 303 304 SignerConfig oldestConfig = mSignerConfigs.get(0); 305 306 // first make sure that if we have signing certificate history that the oldest signer 307 // corresponds to the oldest ancestor 308 if (mSigningCertificateLineage != null) { 309 SigningCertificateLineage subLineage = 310 mSigningCertificateLineage.getSubLineage(oldestConfig.mCertificates.get(0)); 311 if (subLineage.size() != 1) { 312 throw new IllegalArgumentException( 313 "v2 signing enabled but the oldest signer in" 314 + " the SigningCertificateLineage is missing. Please provide" 315 + " the oldest signer to enable v2 signing."); 316 } 317 } 318 signerConfig.add( 319 createSigningBlockSignerConfig( 320 mSignerConfigs.get(0), 321 apkSigningBlockPaddingSupported, 322 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2)); 323 return signerConfig; 324 } else { 325 return createSigningBlockSignerConfigs( 326 apkSigningBlockPaddingSupported, 327 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); 328 } 329 } 330 processV3Configs( List<ApkSigningBlockUtils.SignerConfig> rawConfigs)331 private List<ApkSigningBlockUtils.SignerConfig> processV3Configs( 332 List<ApkSigningBlockUtils.SignerConfig> rawConfigs) throws InvalidKeyException { 333 List<ApkSigningBlockUtils.SignerConfig> processedConfigs = new ArrayList<>(); 334 335 // we have our configs, now touch them up to appropriately cover all SDK levels since APK 336 // signature scheme v3 was introduced 337 int currentMinSdk = Integer.MAX_VALUE; 338 for (int i = rawConfigs.size() - 1; i >= 0; i--) { 339 ApkSigningBlockUtils.SignerConfig config = rawConfigs.get(i); 340 if (config.signatureAlgorithms == null) { 341 // no valid algorithm was found for this signer, and we haven't yet covered all 342 // platform versions, something's wrong 343 String keyAlgorithm = config.certificates.get(0).getPublicKey().getAlgorithm(); 344 throw new InvalidKeyException( 345 "Unsupported key algorithm " 346 + keyAlgorithm 347 + " is " 348 + "not supported for APK Signature Scheme v3 signing"); 349 } 350 if (i == rawConfigs.size() - 1) { 351 // first go through the loop, config should support all future platform versions. 352 // this assumes we don't deprecate support for signers in the future. If we do, 353 // this needs to change 354 config.maxSdkVersion = Integer.MAX_VALUE; 355 } else { 356 // otherwise, we only want to use this signer up to the minimum platform version 357 // on which a newer one is acceptable 358 config.maxSdkVersion = currentMinSdk - 1; 359 } 360 config.minSdkVersion = getMinSdkFromV3SignatureAlgorithms(config.signatureAlgorithms); 361 if (mSigningCertificateLineage != null) { 362 config.mSigningCertificateLineage = 363 mSigningCertificateLineage.getSubLineage(config.certificates.get(0)); 364 } 365 // we know that this config will be used, so add it to our result, order doesn't matter 366 // at this point (and likely only one will be needed 367 processedConfigs.add(config); 368 currentMinSdk = config.minSdkVersion; 369 if (currentMinSdk <= mMinSdkVersion || currentMinSdk <= AndroidSdkVersion.P) { 370 // this satisfies all we need, stop here 371 break; 372 } 373 } 374 if (currentMinSdk > AndroidSdkVersion.P && currentMinSdk > mMinSdkVersion) { 375 // we can't cover all desired SDK versions, abort 376 throw new InvalidKeyException( 377 "Provided key algorithms not supported on all desired " 378 + "Android SDK versions"); 379 } 380 381 return processedConfigs; 382 } 383 createV3SignerConfigs( boolean apkSigningBlockPaddingSupported)384 private List<ApkSigningBlockUtils.SignerConfig> createV3SignerConfigs( 385 boolean apkSigningBlockPaddingSupported) throws InvalidKeyException { 386 return processV3Configs(createSigningBlockSignerConfigs(apkSigningBlockPaddingSupported, 387 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3)); 388 } 389 createV4SignerConfig()390 private ApkSigningBlockUtils.SignerConfig createV4SignerConfig() throws InvalidKeyException { 391 List<ApkSigningBlockUtils.SignerConfig> configs = createSigningBlockSignerConfigs(true, 392 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4); 393 if (configs.size() != 1) { 394 // V4 only uses signer config to connect back to v3. Use the same filtering logic. 395 configs = processV3Configs(configs); 396 } 397 if (configs.size() != 1) { 398 throw new InvalidKeyException("Only accepting one signer config for V4 Signature."); 399 } 400 return configs.get(0); 401 } 402 createSourceStampSignerConfig()403 private ApkSigningBlockUtils.SignerConfig createSourceStampSignerConfig() 404 throws InvalidKeyException { 405 ApkSigningBlockUtils.SignerConfig config = createSigningBlockSignerConfig( 406 mSourceStampSignerConfig, 407 /* apkSigningBlockPaddingSupported= */ false, 408 ApkSigningBlockUtils.VERSION_SOURCE_STAMP); 409 if (mSourceStampSigningCertificateLineage != null) { 410 config.mSigningCertificateLineage = mSourceStampSigningCertificateLineage.getSubLineage( 411 config.certificates.get(0)); 412 } 413 return config; 414 } 415 getMinSdkFromV3SignatureAlgorithms(List<SignatureAlgorithm> algorithms)416 private int getMinSdkFromV3SignatureAlgorithms(List<SignatureAlgorithm> algorithms) { 417 int min = Integer.MAX_VALUE; 418 for (SignatureAlgorithm algorithm : algorithms) { 419 int current = algorithm.getMinSdkVersion(); 420 if (current < min) { 421 if (current <= mMinSdkVersion || current <= AndroidSdkVersion.P) { 422 // this algorithm satisfies all of our needs, no need to keep looking 423 return current; 424 } else { 425 min = current; 426 } 427 } 428 } 429 return min; 430 } 431 createSigningBlockSignerConfigs( boolean apkSigningBlockPaddingSupported, int schemeId)432 private List<ApkSigningBlockUtils.SignerConfig> createSigningBlockSignerConfigs( 433 boolean apkSigningBlockPaddingSupported, int schemeId) throws InvalidKeyException { 434 List<ApkSigningBlockUtils.SignerConfig> signerConfigs = 435 new ArrayList<>(mSignerConfigs.size()); 436 for (int i = 0; i < mSignerConfigs.size(); i++) { 437 SignerConfig signerConfig = mSignerConfigs.get(i); 438 signerConfigs.add( 439 createSigningBlockSignerConfig( 440 signerConfig, apkSigningBlockPaddingSupported, schemeId)); 441 } 442 return signerConfigs; 443 } 444 createSigningBlockSignerConfig( SignerConfig signerConfig, boolean apkSigningBlockPaddingSupported, int schemeId)445 private ApkSigningBlockUtils.SignerConfig createSigningBlockSignerConfig( 446 SignerConfig signerConfig, boolean apkSigningBlockPaddingSupported, int schemeId) 447 throws InvalidKeyException { 448 List<X509Certificate> certificates = signerConfig.getCertificates(); 449 PublicKey publicKey = certificates.get(0).getPublicKey(); 450 451 ApkSigningBlockUtils.SignerConfig newSignerConfig = new ApkSigningBlockUtils.SignerConfig(); 452 newSignerConfig.privateKey = signerConfig.getPrivateKey(); 453 newSignerConfig.certificates = certificates; 454 455 switch (schemeId) { 456 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2: 457 newSignerConfig.signatureAlgorithms = 458 V2SchemeSigner.getSuggestedSignatureAlgorithms( 459 publicKey, 460 mMinSdkVersion, 461 apkSigningBlockPaddingSupported && mVerityEnabled, 462 signerConfig.getDeterministicDsaSigning()); 463 break; 464 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3: 465 try { 466 newSignerConfig.signatureAlgorithms = 467 V3SchemeSigner.getSuggestedSignatureAlgorithms( 468 publicKey, 469 mMinSdkVersion, 470 apkSigningBlockPaddingSupported && mVerityEnabled, 471 signerConfig.getDeterministicDsaSigning()); 472 } catch (InvalidKeyException e) { 473 474 // It is possible for a signer used for v1/v2 signing to not be allowed for use 475 // with v3 signing. This is ok as long as there exists a more recent v3 signer 476 // that covers all supported platform versions. Populate signatureAlgorithm 477 // with null, it will be cleaned-up in a later step. 478 newSignerConfig.signatureAlgorithms = null; 479 } 480 break; 481 case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4: 482 try { 483 newSignerConfig.signatureAlgorithms = 484 V4SchemeSigner.getSuggestedSignatureAlgorithms( 485 publicKey, mMinSdkVersion, apkSigningBlockPaddingSupported, 486 signerConfig.getDeterministicDsaSigning()); 487 } catch (InvalidKeyException e) { 488 // V4 is an optional signing schema, ok to proceed without. 489 newSignerConfig.signatureAlgorithms = null; 490 } 491 break; 492 case ApkSigningBlockUtils.VERSION_SOURCE_STAMP: 493 newSignerConfig.signatureAlgorithms = 494 Collections.singletonList( 495 SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA256); 496 break; 497 default: 498 throw new IllegalArgumentException("Unknown APK Signature Scheme ID requested"); 499 } 500 return newSignerConfig; 501 } 502 isDebuggable(String entryName)503 private boolean isDebuggable(String entryName) { 504 return mDebuggableApkPermitted 505 || !ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME.equals(entryName); 506 } 507 508 /** 509 * Initializes DefaultApkSignerEngine with the existing MANIFEST.MF. This reads existing digests 510 * from the MANIFEST.MF file (they are assumed correct) and stores them for the final signature 511 * without recalculation. This step has a significant performance benefit in case of incremental 512 * build. 513 * 514 * <p>This method extracts and stored computed digest for every entry that it would compute it 515 * for in the {@link #outputJarEntry(String)} method 516 * 517 * @param manifestBytes raw representation of MANIFEST.MF file 518 * @param entryNames a set of expected entries names 519 * @return set of entry names which were processed by the engine during the initialization, a 520 * subset of entryNames 521 */ 522 @Override 523 @SuppressWarnings("AndroidJdkLibsChecker") initWith(byte[] manifestBytes, Set<String> entryNames)524 public Set<String> initWith(byte[] manifestBytes, Set<String> entryNames) { 525 V1SchemeVerifier.Result result = new V1SchemeVerifier.Result(); 526 Pair<ManifestParser.Section, Map<String, ManifestParser.Section>> sections = 527 V1SchemeVerifier.parseManifest(manifestBytes, entryNames, result); 528 String alg = V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm); 529 for (Map.Entry<String, ManifestParser.Section> entry : sections.getSecond().entrySet()) { 530 String entryName = entry.getKey(); 531 if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entry.getKey()) 532 && isDebuggable(entryName)) { 533 534 V1SchemeVerifier.NamedDigest extractedDigest = null; 535 Collection<V1SchemeVerifier.NamedDigest> digestsToVerify = 536 V1SchemeVerifier.getDigestsToVerify( 537 entry.getValue(), "-Digest", mMinSdkVersion, Integer.MAX_VALUE); 538 for (V1SchemeVerifier.NamedDigest digestToVerify : digestsToVerify) { 539 if (digestToVerify.jcaDigestAlgorithm.equals(alg)) { 540 extractedDigest = digestToVerify; 541 break; 542 } 543 } 544 if (extractedDigest != null) { 545 mOutputJarEntryDigests.put(entryName, extractedDigest.digest); 546 } 547 } 548 } 549 return mOutputJarEntryDigests.keySet(); 550 } 551 552 @Override setExecutor(RunnablesExecutor executor)553 public void setExecutor(RunnablesExecutor executor) { 554 mExecutor = executor; 555 } 556 557 @Override inputApkSigningBlock(DataSource apkSigningBlock)558 public void inputApkSigningBlock(DataSource apkSigningBlock) { 559 checkNotClosed(); 560 561 if ((apkSigningBlock == null) || (apkSigningBlock.size() == 0)) { 562 return; 563 } 564 565 if (mOtherSignersSignaturesPreserved) { 566 boolean schemeSignatureBlockPreserved = false; 567 mPreservedSignatureBlocks = new ArrayList<>(); 568 try { 569 List<Pair<byte[], Integer>> signatureBlocks = 570 ApkSigningBlockUtils.getApkSignatureBlocks(apkSigningBlock); 571 for (Pair<byte[], Integer> signatureBlock : signatureBlocks) { 572 if (signatureBlock.getSecond() == Constants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID) { 573 // If a V2 signature block is found and the engine is configured to use V2 574 // then save any of the previous signers that are not part of the current 575 // signing request. 576 if (mV2SigningEnabled) { 577 List<Pair<List<X509Certificate>, byte[]>> v2Signers = 578 ApkSigningBlockUtils.getApkSignatureBlockSigners( 579 signatureBlock.getFirst()); 580 mPreservedV2Signers = new ArrayList<>(v2Signers.size()); 581 for (Pair<List<X509Certificate>, byte[]> v2Signer : v2Signers) { 582 if (!isConfiguredWithSigner(v2Signer.getFirst())) { 583 mPreservedV2Signers.add(v2Signer.getSecond()); 584 schemeSignatureBlockPreserved = true; 585 } 586 } 587 } else { 588 // else V2 signing is not enabled; save the entire signature block to be 589 // added to the final APK signing block. 590 mPreservedSignatureBlocks.add(signatureBlock); 591 schemeSignatureBlockPreserved = true; 592 } 593 } else if (signatureBlock.getSecond() 594 == Constants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID) { 595 // Preserving other signers in the presence of a V3 signature block is only 596 // supported if the engine is configured to resign the APK with the V3 597 // signature scheme, and the V3 signer in the signature block is the same 598 // as the engine is configured to use. 599 if (!mV3SigningEnabled) { 600 throw new IllegalStateException( 601 "Preserving an existing V3 signature is not supported"); 602 } 603 List<Pair<List<X509Certificate>, byte[]>> v3Signers = 604 ApkSigningBlockUtils.getApkSignatureBlockSigners( 605 signatureBlock.getFirst()); 606 if (v3Signers.size() > 1) { 607 throw new IllegalArgumentException( 608 "The provided APK signing block contains " + v3Signers.size() 609 + " V3 signers; the V3 signature scheme only supports" 610 + " one signer"); 611 } 612 // If there is only a single V3 signer then ensure it is the signer 613 // configured to sign the APK. 614 if (v3Signers.size() == 1 615 && !isConfiguredWithSigner(v3Signers.get(0).getFirst())) { 616 throw new IllegalStateException( 617 "The V3 signature scheme only supports one signer; a request " 618 + "was made to preserve the existing V3 signature, " 619 + "but the engine is configured to sign with a " 620 + "different signer"); 621 } 622 } else if (!DISCARDED_SIGNATURE_BLOCK_IDS.contains( 623 signatureBlock.getSecond())) { 624 mPreservedSignatureBlocks.add(signatureBlock); 625 } 626 } 627 } catch (ApkFormatException | CertificateException | IOException e) { 628 throw new IllegalArgumentException("Unable to parse the provided signing block", e); 629 } 630 // Signature scheme V3+ only support a single signer; if the engine is configured to 631 // sign with V3+ then ensure no scheme signature blocks have been preserved. 632 if (mV3SigningEnabled && schemeSignatureBlockPreserved) { 633 throw new IllegalStateException( 634 "Signature scheme V3+ only supports a single signer and cannot be " 635 + "appended to the existing signature scheme blocks"); 636 } 637 return; 638 } 639 } 640 641 /** 642 * Returns whether the engine is configured to sign the APK with a signer using the specified 643 * {@code signerCerts}. 644 */ isConfiguredWithSigner(List<X509Certificate> signerCerts)645 private boolean isConfiguredWithSigner(List<X509Certificate> signerCerts) { 646 for (SignerConfig signerConfig : mSignerConfigs) { 647 if (signerCerts.containsAll(signerConfig.getCertificates())) { 648 return true; 649 } 650 } 651 return false; 652 } 653 654 @Override inputJarEntry(String entryName)655 public InputJarEntryInstructions inputJarEntry(String entryName) { 656 checkNotClosed(); 657 658 InputJarEntryInstructions.OutputPolicy outputPolicy = 659 getInputJarEntryOutputPolicy(entryName); 660 switch (outputPolicy) { 661 case SKIP: 662 return new InputJarEntryInstructions(InputJarEntryInstructions.OutputPolicy.SKIP); 663 case OUTPUT: 664 return new InputJarEntryInstructions(InputJarEntryInstructions.OutputPolicy.OUTPUT); 665 case OUTPUT_BY_ENGINE: 666 if (V1SchemeConstants.MANIFEST_ENTRY_NAME.equals(entryName)) { 667 // We copy the main section of the JAR manifest from input to output. Thus, this 668 // invalidates v1 signature and we need to see the entry's data. 669 mInputJarManifestEntryDataRequest = new GetJarEntryDataRequest(entryName); 670 return new InputJarEntryInstructions( 671 InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE, 672 mInputJarManifestEntryDataRequest); 673 } 674 return new InputJarEntryInstructions( 675 InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE); 676 default: 677 throw new RuntimeException("Unsupported output policy: " + outputPolicy); 678 } 679 } 680 681 @Override outputJarEntry(String entryName)682 public InspectJarEntryRequest outputJarEntry(String entryName) { 683 checkNotClosed(); 684 invalidateV2Signature(); 685 686 if (!isDebuggable(entryName)) { 687 forgetOutputApkDebuggableStatus(); 688 } 689 690 if (!mV1SigningEnabled) { 691 // No need to inspect JAR entries when v1 signing is not enabled. 692 if (!isDebuggable(entryName)) { 693 // To reject debuggable APKs we need to inspect the APK's AndroidManifest.xml to 694 // check whether it declares that the APK is debuggable 695 mOutputAndroidManifestEntryDataRequest = new GetJarEntryDataRequest(entryName); 696 return mOutputAndroidManifestEntryDataRequest; 697 } 698 return null; 699 } 700 // v1 signing is enabled 701 702 if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName)) { 703 // This entry is covered by v1 signature. We thus need to inspect the entry's data to 704 // compute its digest(s) for v1 signature. 705 706 // TODO: Handle the case where other signer's v1 signatures are present and need to be 707 // preserved. In that scenario we can't modify MANIFEST.MF and add/remove JAR entries 708 // covered by v1 signature. 709 invalidateV1Signature(); 710 GetJarEntryDataDigestRequest dataDigestRequest = 711 new GetJarEntryDataDigestRequest( 712 entryName, 713 V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm)); 714 mOutputJarEntryDigestRequests.put(entryName, dataDigestRequest); 715 mOutputJarEntryDigests.remove(entryName); 716 717 if ((!mDebuggableApkPermitted) 718 && (ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME.equals(entryName))) { 719 // To reject debuggable APKs we need to inspect the APK's AndroidManifest.xml to 720 // check whether it declares that the APK is debuggable 721 mOutputAndroidManifestEntryDataRequest = new GetJarEntryDataRequest(entryName); 722 return new CompoundInspectJarEntryRequest( 723 entryName, mOutputAndroidManifestEntryDataRequest, dataDigestRequest); 724 } 725 726 return dataDigestRequest; 727 } 728 729 if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) { 730 // This entry is part of v1 signature generated by this engine. We need to check whether 731 // the entry's data is as output by the engine. 732 invalidateV1Signature(); 733 GetJarEntryDataRequest dataRequest; 734 if (V1SchemeConstants.MANIFEST_ENTRY_NAME.equals(entryName)) { 735 dataRequest = new GetJarEntryDataRequest(entryName); 736 mInputJarManifestEntryDataRequest = dataRequest; 737 } else { 738 // If this entry is part of v1 signature which has been emitted by this engine, 739 // check whether the output entry's data matches what the engine emitted. 740 dataRequest = 741 (mEmittedSignatureJarEntryData.containsKey(entryName)) 742 ? new GetJarEntryDataRequest(entryName) 743 : null; 744 } 745 746 if (dataRequest != null) { 747 mOutputSignatureJarEntryDataRequests.put(entryName, dataRequest); 748 } 749 return dataRequest; 750 } 751 752 // This entry is not covered by v1 signature and isn't part of v1 signature. 753 return null; 754 } 755 756 @Override inputJarEntryRemoved(String entryName)757 public InputJarEntryInstructions.OutputPolicy inputJarEntryRemoved(String entryName) { 758 checkNotClosed(); 759 return getInputJarEntryOutputPolicy(entryName); 760 } 761 762 @Override outputJarEntryRemoved(String entryName)763 public void outputJarEntryRemoved(String entryName) { 764 checkNotClosed(); 765 invalidateV2Signature(); 766 if (!mV1SigningEnabled) { 767 return; 768 } 769 770 if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName)) { 771 // This entry is covered by v1 signature. 772 invalidateV1Signature(); 773 mOutputJarEntryDigests.remove(entryName); 774 mOutputJarEntryDigestRequests.remove(entryName); 775 mOutputSignatureJarEntryDataRequests.remove(entryName); 776 return; 777 } 778 779 if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) { 780 // This entry is part of the v1 signature generated by this engine. 781 invalidateV1Signature(); 782 return; 783 } 784 } 785 786 @Override outputJarEntries()787 public OutputJarSignatureRequest outputJarEntries() 788 throws ApkFormatException, InvalidKeyException, SignatureException, 789 NoSuchAlgorithmException { 790 checkNotClosed(); 791 792 if (!mV1SignaturePending) { 793 return null; 794 } 795 796 if ((mInputJarManifestEntryDataRequest != null) 797 && (!mInputJarManifestEntryDataRequest.isDone())) { 798 throw new IllegalStateException( 799 "Still waiting to inspect input APK's " 800 + mInputJarManifestEntryDataRequest.getEntryName()); 801 } 802 803 for (GetJarEntryDataDigestRequest digestRequest : mOutputJarEntryDigestRequests.values()) { 804 String entryName = digestRequest.getEntryName(); 805 if (!digestRequest.isDone()) { 806 throw new IllegalStateException( 807 "Still waiting to inspect output APK's " + entryName); 808 } 809 mOutputJarEntryDigests.put(entryName, digestRequest.getDigest()); 810 } 811 if (isEligibleForSourceStamp()) { 812 MessageDigest messageDigest = 813 MessageDigest.getInstance( 814 V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm)); 815 messageDigest.update(generateSourceStampCertificateDigest()); 816 mOutputJarEntryDigests.put( 817 SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME, messageDigest.digest()); 818 } 819 mOutputJarEntryDigestRequests.clear(); 820 821 for (GetJarEntryDataRequest dataRequest : mOutputSignatureJarEntryDataRequests.values()) { 822 if (!dataRequest.isDone()) { 823 throw new IllegalStateException( 824 "Still waiting to inspect output APK's " + dataRequest.getEntryName()); 825 } 826 } 827 828 List<Integer> apkSigningSchemeIds = new ArrayList<>(); 829 if (mV2SigningEnabled) { 830 apkSigningSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); 831 } 832 if (mV3SigningEnabled) { 833 apkSigningSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); 834 } 835 byte[] inputJarManifest = 836 (mInputJarManifestEntryDataRequest != null) 837 ? mInputJarManifestEntryDataRequest.getData() 838 : null; 839 if (isEligibleForSourceStamp()) { 840 inputJarManifest = 841 V1SchemeSigner.generateManifestFile( 842 mV1ContentDigestAlgorithm, 843 mOutputJarEntryDigests, 844 inputJarManifest) 845 .contents; 846 } 847 848 // Check whether the most recently used signature (if present) is still fine. 849 checkOutputApkNotDebuggableIfDebuggableMustBeRejected(); 850 List<Pair<String, byte[]>> signatureZipEntries; 851 if ((mAddV1SignatureRequest == null) || (!mAddV1SignatureRequest.isDone())) { 852 try { 853 signatureZipEntries = 854 V1SchemeSigner.sign( 855 mV1SignerConfigs, 856 mV1ContentDigestAlgorithm, 857 mOutputJarEntryDigests, 858 apkSigningSchemeIds, 859 inputJarManifest, 860 mCreatedBy); 861 } catch (CertificateException e) { 862 throw new SignatureException("Failed to generate v1 signature", e); 863 } 864 } else { 865 V1SchemeSigner.OutputManifestFile newManifest = 866 V1SchemeSigner.generateManifestFile( 867 mV1ContentDigestAlgorithm, mOutputJarEntryDigests, inputJarManifest); 868 byte[] emittedSignatureManifest = 869 mEmittedSignatureJarEntryData.get(V1SchemeConstants.MANIFEST_ENTRY_NAME); 870 if (!Arrays.equals(newManifest.contents, emittedSignatureManifest)) { 871 // Emitted v1 signature is no longer valid. 872 try { 873 signatureZipEntries = 874 V1SchemeSigner.signManifest( 875 mV1SignerConfigs, 876 mV1ContentDigestAlgorithm, 877 apkSigningSchemeIds, 878 mCreatedBy, 879 newManifest); 880 } catch (CertificateException e) { 881 throw new SignatureException("Failed to generate v1 signature", e); 882 } 883 } else { 884 // Emitted v1 signature is still valid. Check whether the signature is there in the 885 // output. 886 signatureZipEntries = new ArrayList<>(); 887 for (Map.Entry<String, byte[]> expectedOutputEntry : 888 mEmittedSignatureJarEntryData.entrySet()) { 889 String entryName = expectedOutputEntry.getKey(); 890 byte[] expectedData = expectedOutputEntry.getValue(); 891 GetJarEntryDataRequest actualDataRequest = 892 mOutputSignatureJarEntryDataRequests.get(entryName); 893 if (actualDataRequest == null) { 894 // This signature entry hasn't been output. 895 signatureZipEntries.add(Pair.of(entryName, expectedData)); 896 continue; 897 } 898 byte[] actualData = actualDataRequest.getData(); 899 if (!Arrays.equals(expectedData, actualData)) { 900 signatureZipEntries.add(Pair.of(entryName, expectedData)); 901 } 902 } 903 if (signatureZipEntries.isEmpty()) { 904 // v1 signature in the output is valid 905 return null; 906 } 907 // v1 signature in the output is not valid. 908 } 909 } 910 911 if (signatureZipEntries.isEmpty()) { 912 // v1 signature in the output is valid 913 mV1SignaturePending = false; 914 return null; 915 } 916 917 List<OutputJarSignatureRequest.JarEntry> sigEntries = 918 new ArrayList<>(signatureZipEntries.size()); 919 for (Pair<String, byte[]> entry : signatureZipEntries) { 920 String entryName = entry.getFirst(); 921 byte[] entryData = entry.getSecond(); 922 sigEntries.add(new OutputJarSignatureRequest.JarEntry(entryName, entryData)); 923 mEmittedSignatureJarEntryData.put(entryName, entryData); 924 } 925 mAddV1SignatureRequest = new OutputJarSignatureRequestImpl(sigEntries); 926 return mAddV1SignatureRequest; 927 } 928 929 @Deprecated 930 @Override outputZipSections( DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd)931 public OutputApkSigningBlockRequest outputZipSections( 932 DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd) 933 throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException { 934 return outputZipSectionsInternal(zipEntries, zipCentralDirectory, zipEocd, false); 935 } 936 937 @Override outputZipSections2( DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd)938 public OutputApkSigningBlockRequest2 outputZipSections2( 939 DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd) 940 throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException { 941 return outputZipSectionsInternal(zipEntries, zipCentralDirectory, zipEocd, true); 942 } 943 outputZipSectionsInternal( DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd, boolean apkSigningBlockPaddingSupported)944 private OutputApkSigningBlockRequestImpl outputZipSectionsInternal( 945 DataSource zipEntries, 946 DataSource zipCentralDirectory, 947 DataSource zipEocd, 948 boolean apkSigningBlockPaddingSupported) 949 throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException { 950 checkNotClosed(); 951 checkV1SigningDoneIfEnabled(); 952 if (!mV2SigningEnabled && !mV3SigningEnabled && !isEligibleForSourceStamp()) { 953 return null; 954 } 955 checkOutputApkNotDebuggableIfDebuggableMustBeRejected(); 956 957 // adjust to proper padding 958 Pair<DataSource, Integer> paddingPair = 959 ApkSigningBlockUtils.generateApkSigningBlockPadding( 960 zipEntries, apkSigningBlockPaddingSupported); 961 DataSource beforeCentralDir = paddingPair.getFirst(); 962 int padSizeBeforeApkSigningBlock = paddingPair.getSecond(); 963 DataSource eocd = ApkSigningBlockUtils.copyWithModifiedCDOffset(beforeCentralDir, zipEocd); 964 965 List<Pair<byte[], Integer>> signingSchemeBlocks = new ArrayList<>(); 966 ApkSigningBlockUtils.SigningSchemeBlockAndDigests v2SigningSchemeBlockAndDigests = null; 967 ApkSigningBlockUtils.SigningSchemeBlockAndDigests v3SigningSchemeBlockAndDigests = null; 968 // If the engine is configured to preserve previous signature blocks and any were found in 969 // the existing APK signing block then add them to the list to be used to generate the 970 // new APK signing block. 971 if (mOtherSignersSignaturesPreserved && mPreservedSignatureBlocks != null 972 && !mPreservedSignatureBlocks.isEmpty()) { 973 signingSchemeBlocks.addAll(mPreservedSignatureBlocks); 974 } 975 976 // create APK Signature Scheme V2 Signature if requested 977 if (mV2SigningEnabled) { 978 invalidateV2Signature(); 979 List<ApkSigningBlockUtils.SignerConfig> v2SignerConfigs = 980 createV2SignerConfigs(apkSigningBlockPaddingSupported); 981 v2SigningSchemeBlockAndDigests = 982 V2SchemeSigner.generateApkSignatureSchemeV2Block( 983 mExecutor, 984 beforeCentralDir, 985 zipCentralDirectory, 986 eocd, 987 v2SignerConfigs, 988 mV3SigningEnabled, 989 mOtherSignersSignaturesPreserved ? mPreservedV2Signers : null); 990 signingSchemeBlocks.add(v2SigningSchemeBlockAndDigests.signingSchemeBlock); 991 } 992 if (mV3SigningEnabled) { 993 invalidateV3Signature(); 994 List<ApkSigningBlockUtils.SignerConfig> v3SignerConfigs = 995 createV3SignerConfigs(apkSigningBlockPaddingSupported); 996 v3SigningSchemeBlockAndDigests = 997 V3SchemeSigner.generateApkSignatureSchemeV3Block( 998 mExecutor, 999 beforeCentralDir, 1000 zipCentralDirectory, 1001 eocd, 1002 v3SignerConfigs); 1003 signingSchemeBlocks.add(v3SigningSchemeBlockAndDigests.signingSchemeBlock); 1004 } 1005 if (isEligibleForSourceStamp()) { 1006 ApkSigningBlockUtils.SignerConfig sourceStampSignerConfig = 1007 createSourceStampSignerConfig(); 1008 Map<Integer, Map<ContentDigestAlgorithm, byte[]>> signatureSchemeDigestInfos = 1009 new HashMap<>(); 1010 if (mV3SigningEnabled) { 1011 signatureSchemeDigestInfos.put( 1012 VERSION_APK_SIGNATURE_SCHEME_V3, v3SigningSchemeBlockAndDigests.digestInfo); 1013 } 1014 if (mV2SigningEnabled) { 1015 signatureSchemeDigestInfos.put( 1016 VERSION_APK_SIGNATURE_SCHEME_V2, v2SigningSchemeBlockAndDigests.digestInfo); 1017 } 1018 if (mV1SigningEnabled) { 1019 Map<ContentDigestAlgorithm, byte[]> v1SigningSchemeDigests = new HashMap<>(); 1020 try { 1021 // Jar signing related variables must have been already populated at this point 1022 // if V1 signing is enabled since it is happening before computations on the APK 1023 // signing block (V2/V3/V4/SourceStamp signing). 1024 byte[] inputJarManifest = 1025 (mInputJarManifestEntryDataRequest != null) 1026 ? mInputJarManifestEntryDataRequest.getData() 1027 : null; 1028 byte[] jarManifest = 1029 V1SchemeSigner.generateManifestFile( 1030 mV1ContentDigestAlgorithm, 1031 mOutputJarEntryDigests, 1032 inputJarManifest) 1033 .contents; 1034 // The digest of the jar manifest does not need to be computed in chunks due to 1035 // the small size of the manifest. 1036 v1SigningSchemeDigests.put( 1037 ContentDigestAlgorithm.SHA256, computeSha256DigestBytes(jarManifest)); 1038 } catch (ApkFormatException e) { 1039 throw new RuntimeException("Failed to generate manifest file", e); 1040 } 1041 signatureSchemeDigestInfos.put( 1042 VERSION_JAR_SIGNATURE_SCHEME, v1SigningSchemeDigests); 1043 } 1044 signingSchemeBlocks.add( 1045 V2SourceStampSigner.generateSourceStampBlock( 1046 sourceStampSignerConfig, signatureSchemeDigestInfos)); 1047 } 1048 1049 // create APK Signing Block with v2 and/or v3 and/or SourceStamp blocks 1050 byte[] apkSigningBlock = ApkSigningBlockUtils.generateApkSigningBlock(signingSchemeBlocks); 1051 1052 mAddSigningBlockRequest = 1053 new OutputApkSigningBlockRequestImpl(apkSigningBlock, padSizeBeforeApkSigningBlock); 1054 return mAddSigningBlockRequest; 1055 } 1056 1057 @Override outputDone()1058 public void outputDone() { 1059 checkNotClosed(); 1060 checkV1SigningDoneIfEnabled(); 1061 checkSigningBlockDoneIfEnabled(); 1062 } 1063 1064 @Override signV4(DataSource dataSource, File outputFile, boolean ignoreFailures)1065 public void signV4(DataSource dataSource, File outputFile, boolean ignoreFailures) 1066 throws SignatureException { 1067 if (outputFile == null) { 1068 if (ignoreFailures) { 1069 return; 1070 } 1071 throw new SignatureException("Missing V4 output file."); 1072 } 1073 try { 1074 ApkSigningBlockUtils.SignerConfig v4SignerConfig = createV4SignerConfig(); 1075 V4SchemeSigner.generateV4Signature(dataSource, v4SignerConfig, outputFile); 1076 } catch (InvalidKeyException | IOException | NoSuchAlgorithmException e) { 1077 if (ignoreFailures) { 1078 return; 1079 } 1080 throw new SignatureException("V4 signing failed", e); 1081 } 1082 } 1083 1084 /** For external use only to generate V4 & tree separately. */ produceV4Signature(DataSource dataSource, OutputStream sigOutput)1085 public byte[] produceV4Signature(DataSource dataSource, OutputStream sigOutput) 1086 throws SignatureException { 1087 if (sigOutput == null) { 1088 throw new SignatureException("Missing V4 output streams."); 1089 } 1090 try { 1091 ApkSigningBlockUtils.SignerConfig v4SignerConfig = createV4SignerConfig(); 1092 Pair<V4Signature, byte[]> pair = 1093 V4SchemeSigner.generateV4Signature(dataSource, v4SignerConfig); 1094 pair.getFirst().writeTo(sigOutput); 1095 return pair.getSecond(); 1096 } catch (InvalidKeyException | IOException | NoSuchAlgorithmException e) { 1097 throw new SignatureException("V4 signing failed", e); 1098 } 1099 } 1100 1101 @Override isEligibleForSourceStamp()1102 public boolean isEligibleForSourceStamp() { 1103 return mSourceStampSignerConfig != null 1104 && (mV2SigningEnabled || mV3SigningEnabled || mV1SigningEnabled); 1105 } 1106 1107 @Override generateSourceStampCertificateDigest()1108 public byte[] generateSourceStampCertificateDigest() throws SignatureException { 1109 if (mSourceStampSignerConfig.getCertificates().isEmpty()) { 1110 throw new SignatureException("No certificates configured for stamp"); 1111 } 1112 try { 1113 return computeSha256DigestBytes( 1114 mSourceStampSignerConfig.getCertificates().get(0).getEncoded()); 1115 } catch (CertificateEncodingException e) { 1116 throw new SignatureException("Failed to encode source stamp certificate", e); 1117 } 1118 } 1119 1120 @Override close()1121 public void close() { 1122 mClosed = true; 1123 1124 mAddV1SignatureRequest = null; 1125 mInputJarManifestEntryDataRequest = null; 1126 mOutputAndroidManifestEntryDataRequest = null; 1127 mDebuggable = null; 1128 mOutputJarEntryDigestRequests.clear(); 1129 mOutputJarEntryDigests.clear(); 1130 mEmittedSignatureJarEntryData.clear(); 1131 mOutputSignatureJarEntryDataRequests.clear(); 1132 1133 mAddSigningBlockRequest = null; 1134 } 1135 invalidateV1Signature()1136 private void invalidateV1Signature() { 1137 if (mV1SigningEnabled) { 1138 mV1SignaturePending = true; 1139 } 1140 invalidateV2Signature(); 1141 } 1142 invalidateV2Signature()1143 private void invalidateV2Signature() { 1144 if (mV2SigningEnabled) { 1145 mV2SignaturePending = true; 1146 mAddSigningBlockRequest = null; 1147 } 1148 } 1149 invalidateV3Signature()1150 private void invalidateV3Signature() { 1151 if (mV3SigningEnabled) { 1152 mV3SignaturePending = true; 1153 mAddSigningBlockRequest = null; 1154 } 1155 } 1156 checkNotClosed()1157 private void checkNotClosed() { 1158 if (mClosed) { 1159 throw new IllegalStateException("Engine closed"); 1160 } 1161 } 1162 checkV1SigningDoneIfEnabled()1163 private void checkV1SigningDoneIfEnabled() { 1164 if (!mV1SignaturePending) { 1165 return; 1166 } 1167 1168 if (mAddV1SignatureRequest == null) { 1169 throw new IllegalStateException( 1170 "v1 signature (JAR signature) not yet generated. Skipped outputJarEntries()?"); 1171 } 1172 if (!mAddV1SignatureRequest.isDone()) { 1173 throw new IllegalStateException( 1174 "v1 signature (JAR signature) addition requested by outputJarEntries() hasn't" 1175 + " been fulfilled"); 1176 } 1177 for (Map.Entry<String, byte[]> expectedOutputEntry : 1178 mEmittedSignatureJarEntryData.entrySet()) { 1179 String entryName = expectedOutputEntry.getKey(); 1180 byte[] expectedData = expectedOutputEntry.getValue(); 1181 GetJarEntryDataRequest actualDataRequest = 1182 mOutputSignatureJarEntryDataRequests.get(entryName); 1183 if (actualDataRequest == null) { 1184 throw new IllegalStateException( 1185 "APK entry " 1186 + entryName 1187 + " not yet output despite this having been" 1188 + " requested"); 1189 } else if (!actualDataRequest.isDone()) { 1190 throw new IllegalStateException( 1191 "Still waiting to inspect output APK's " + entryName); 1192 } 1193 byte[] actualData = actualDataRequest.getData(); 1194 if (!Arrays.equals(expectedData, actualData)) { 1195 throw new IllegalStateException( 1196 "Output APK entry " + entryName + " data differs from what was requested"); 1197 } 1198 } 1199 mV1SignaturePending = false; 1200 } 1201 checkSigningBlockDoneIfEnabled()1202 private void checkSigningBlockDoneIfEnabled() { 1203 if (!mV2SignaturePending && !mV3SignaturePending) { 1204 return; 1205 } 1206 if (mAddSigningBlockRequest == null) { 1207 throw new IllegalStateException( 1208 "Signed APK Signing BLock not yet generated. Skipped outputZipSections()?"); 1209 } 1210 if (!mAddSigningBlockRequest.isDone()) { 1211 throw new IllegalStateException( 1212 "APK Signing Block addition of signature(s) requested by" 1213 + " outputZipSections() hasn't been fulfilled yet"); 1214 } 1215 mAddSigningBlockRequest = null; 1216 mV2SignaturePending = false; 1217 mV3SignaturePending = false; 1218 } 1219 checkOutputApkNotDebuggableIfDebuggableMustBeRejected()1220 private void checkOutputApkNotDebuggableIfDebuggableMustBeRejected() throws SignatureException { 1221 if (mDebuggableApkPermitted) { 1222 return; 1223 } 1224 1225 try { 1226 if (isOutputApkDebuggable()) { 1227 throw new SignatureException( 1228 "APK is debuggable (see android:debuggable attribute) and this engine is" 1229 + " configured to refuse to sign debuggable APKs"); 1230 } 1231 } catch (ApkFormatException e) { 1232 throw new SignatureException("Failed to determine whether the APK is debuggable", e); 1233 } 1234 } 1235 1236 /** 1237 * Returns whether the output APK is debuggable according to its {@code android:debuggable} 1238 * declaration. 1239 */ isOutputApkDebuggable()1240 private boolean isOutputApkDebuggable() throws ApkFormatException { 1241 if (mDebuggable != null) { 1242 return mDebuggable; 1243 } 1244 1245 if (mOutputAndroidManifestEntryDataRequest == null) { 1246 throw new IllegalStateException( 1247 "Cannot determine debuggable status of output APK because " 1248 + ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME 1249 + " entry contents have not yet been requested"); 1250 } 1251 1252 if (!mOutputAndroidManifestEntryDataRequest.isDone()) { 1253 throw new IllegalStateException( 1254 "Still waiting to inspect output APK's " 1255 + mOutputAndroidManifestEntryDataRequest.getEntryName()); 1256 } 1257 mDebuggable = 1258 ApkUtils.getDebuggableFromBinaryAndroidManifest( 1259 ByteBuffer.wrap(mOutputAndroidManifestEntryDataRequest.getData())); 1260 return mDebuggable; 1261 } 1262 forgetOutputApkDebuggableStatus()1263 private void forgetOutputApkDebuggableStatus() { 1264 mDebuggable = null; 1265 } 1266 1267 /** Returns the output policy for the provided input JAR entry. */ getInputJarEntryOutputPolicy(String entryName)1268 private InputJarEntryInstructions.OutputPolicy getInputJarEntryOutputPolicy(String entryName) { 1269 if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) { 1270 return InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE; 1271 } 1272 if ((mOtherSignersSignaturesPreserved) 1273 || (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName))) { 1274 return InputJarEntryInstructions.OutputPolicy.OUTPUT; 1275 } 1276 return InputJarEntryInstructions.OutputPolicy.SKIP; 1277 } 1278 1279 private static class OutputJarSignatureRequestImpl implements OutputJarSignatureRequest { 1280 private final List<JarEntry> mAdditionalJarEntries; 1281 private volatile boolean mDone; 1282 OutputJarSignatureRequestImpl(List<JarEntry> additionalZipEntries)1283 private OutputJarSignatureRequestImpl(List<JarEntry> additionalZipEntries) { 1284 mAdditionalJarEntries = 1285 Collections.unmodifiableList(new ArrayList<>(additionalZipEntries)); 1286 } 1287 1288 @Override getAdditionalJarEntries()1289 public List<JarEntry> getAdditionalJarEntries() { 1290 return mAdditionalJarEntries; 1291 } 1292 1293 @Override done()1294 public void done() { 1295 mDone = true; 1296 } 1297 isDone()1298 private boolean isDone() { 1299 return mDone; 1300 } 1301 } 1302 1303 @SuppressWarnings("deprecation") 1304 private static class OutputApkSigningBlockRequestImpl 1305 implements OutputApkSigningBlockRequest, OutputApkSigningBlockRequest2 { 1306 private final byte[] mApkSigningBlock; 1307 private final int mPaddingBeforeApkSigningBlock; 1308 private volatile boolean mDone; 1309 OutputApkSigningBlockRequestImpl(byte[] apkSigingBlock, int paddingBefore)1310 private OutputApkSigningBlockRequestImpl(byte[] apkSigingBlock, int paddingBefore) { 1311 mApkSigningBlock = apkSigingBlock.clone(); 1312 mPaddingBeforeApkSigningBlock = paddingBefore; 1313 } 1314 1315 @Override getApkSigningBlock()1316 public byte[] getApkSigningBlock() { 1317 return mApkSigningBlock.clone(); 1318 } 1319 1320 @Override done()1321 public void done() { 1322 mDone = true; 1323 } 1324 isDone()1325 private boolean isDone() { 1326 return mDone; 1327 } 1328 1329 @Override getPaddingSizeBeforeApkSigningBlock()1330 public int getPaddingSizeBeforeApkSigningBlock() { 1331 return mPaddingBeforeApkSigningBlock; 1332 } 1333 } 1334 1335 /** JAR entry inspection request which obtain the entry's uncompressed data. */ 1336 private static class GetJarEntryDataRequest implements InspectJarEntryRequest { 1337 private final String mEntryName; 1338 private final Object mLock = new Object(); 1339 1340 private boolean mDone; 1341 private DataSink mDataSink; 1342 private ByteArrayOutputStream mDataSinkBuf; 1343 GetJarEntryDataRequest(String entryName)1344 private GetJarEntryDataRequest(String entryName) { 1345 mEntryName = entryName; 1346 } 1347 1348 @Override getEntryName()1349 public String getEntryName() { 1350 return mEntryName; 1351 } 1352 1353 @Override getDataSink()1354 public DataSink getDataSink() { 1355 synchronized (mLock) { 1356 checkNotDone(); 1357 if (mDataSinkBuf == null) { 1358 mDataSinkBuf = new ByteArrayOutputStream(); 1359 } 1360 if (mDataSink == null) { 1361 mDataSink = DataSinks.asDataSink(mDataSinkBuf); 1362 } 1363 return mDataSink; 1364 } 1365 } 1366 1367 @Override done()1368 public void done() { 1369 synchronized (mLock) { 1370 if (mDone) { 1371 return; 1372 } 1373 mDone = true; 1374 } 1375 } 1376 isDone()1377 private boolean isDone() { 1378 synchronized (mLock) { 1379 return mDone; 1380 } 1381 } 1382 checkNotDone()1383 private void checkNotDone() throws IllegalStateException { 1384 synchronized (mLock) { 1385 if (mDone) { 1386 throw new IllegalStateException("Already done"); 1387 } 1388 } 1389 } 1390 getData()1391 private byte[] getData() { 1392 synchronized (mLock) { 1393 if (!mDone) { 1394 throw new IllegalStateException("Not yet done"); 1395 } 1396 return (mDataSinkBuf != null) ? mDataSinkBuf.toByteArray() : new byte[0]; 1397 } 1398 } 1399 } 1400 1401 /** JAR entry inspection request which obtains the digest of the entry's uncompressed data. */ 1402 private static class GetJarEntryDataDigestRequest implements InspectJarEntryRequest { 1403 private final String mEntryName; 1404 private final String mJcaDigestAlgorithm; 1405 private final Object mLock = new Object(); 1406 1407 private boolean mDone; 1408 private DataSink mDataSink; 1409 private MessageDigest mMessageDigest; 1410 private byte[] mDigest; 1411 GetJarEntryDataDigestRequest(String entryName, String jcaDigestAlgorithm)1412 private GetJarEntryDataDigestRequest(String entryName, String jcaDigestAlgorithm) { 1413 mEntryName = entryName; 1414 mJcaDigestAlgorithm = jcaDigestAlgorithm; 1415 } 1416 1417 @Override getEntryName()1418 public String getEntryName() { 1419 return mEntryName; 1420 } 1421 1422 @Override getDataSink()1423 public DataSink getDataSink() { 1424 synchronized (mLock) { 1425 checkNotDone(); 1426 if (mDataSink == null) { 1427 mDataSink = DataSinks.asDataSink(getMessageDigest()); 1428 } 1429 return mDataSink; 1430 } 1431 } 1432 getMessageDigest()1433 private MessageDigest getMessageDigest() { 1434 synchronized (mLock) { 1435 if (mMessageDigest == null) { 1436 try { 1437 mMessageDigest = MessageDigest.getInstance(mJcaDigestAlgorithm); 1438 } catch (NoSuchAlgorithmException e) { 1439 throw new RuntimeException( 1440 mJcaDigestAlgorithm + " MessageDigest not available", e); 1441 } 1442 } 1443 return mMessageDigest; 1444 } 1445 } 1446 1447 @Override done()1448 public void done() { 1449 synchronized (mLock) { 1450 if (mDone) { 1451 return; 1452 } 1453 mDone = true; 1454 mDigest = getMessageDigest().digest(); 1455 mMessageDigest = null; 1456 mDataSink = null; 1457 } 1458 } 1459 isDone()1460 private boolean isDone() { 1461 synchronized (mLock) { 1462 return mDone; 1463 } 1464 } 1465 checkNotDone()1466 private void checkNotDone() throws IllegalStateException { 1467 synchronized (mLock) { 1468 if (mDone) { 1469 throw new IllegalStateException("Already done"); 1470 } 1471 } 1472 } 1473 getDigest()1474 private byte[] getDigest() { 1475 synchronized (mLock) { 1476 if (!mDone) { 1477 throw new IllegalStateException("Not yet done"); 1478 } 1479 return mDigest.clone(); 1480 } 1481 } 1482 } 1483 1484 /** JAR entry inspection request which transparently satisfies multiple such requests. */ 1485 private static class CompoundInspectJarEntryRequest implements InspectJarEntryRequest { 1486 private final String mEntryName; 1487 private final InspectJarEntryRequest[] mRequests; 1488 private final Object mLock = new Object(); 1489 1490 private DataSink mSink; 1491 CompoundInspectJarEntryRequest( String entryName, InspectJarEntryRequest... requests)1492 private CompoundInspectJarEntryRequest( 1493 String entryName, InspectJarEntryRequest... requests) { 1494 mEntryName = entryName; 1495 mRequests = requests; 1496 } 1497 1498 @Override getEntryName()1499 public String getEntryName() { 1500 return mEntryName; 1501 } 1502 1503 @Override getDataSink()1504 public DataSink getDataSink() { 1505 synchronized (mLock) { 1506 if (mSink == null) { 1507 DataSink[] sinks = new DataSink[mRequests.length]; 1508 for (int i = 0; i < sinks.length; i++) { 1509 sinks[i] = mRequests[i].getDataSink(); 1510 } 1511 mSink = new TeeDataSink(sinks); 1512 } 1513 return mSink; 1514 } 1515 } 1516 1517 @Override done()1518 public void done() { 1519 for (InspectJarEntryRequest request : mRequests) { 1520 request.done(); 1521 } 1522 } 1523 } 1524 1525 /** 1526 * Configuration of a signer. 1527 * 1528 * <p>Use {@link Builder} to obtain configuration instances. 1529 */ 1530 public static class SignerConfig { 1531 private final String mName; 1532 private final PrivateKey mPrivateKey; 1533 private final List<X509Certificate> mCertificates; 1534 private final boolean mDeterministicDsaSigning; 1535 SignerConfig( String name, PrivateKey privateKey, List<X509Certificate> certificates, boolean deterministicDsaSigning)1536 private SignerConfig( 1537 String name, PrivateKey privateKey, List<X509Certificate> certificates, 1538 boolean deterministicDsaSigning) { 1539 mName = name; 1540 mPrivateKey = privateKey; 1541 mCertificates = Collections.unmodifiableList(new ArrayList<>(certificates)); 1542 mDeterministicDsaSigning = deterministicDsaSigning; 1543 } 1544 1545 /** Returns the name of this signer. */ getName()1546 public String getName() { 1547 return mName; 1548 } 1549 1550 /** Returns the signing key of this signer. */ getPrivateKey()1551 public PrivateKey getPrivateKey() { 1552 return mPrivateKey; 1553 } 1554 1555 /** 1556 * Returns the certificate(s) of this signer. The first certificate's public key corresponds 1557 * to this signer's private key. 1558 */ getCertificates()1559 public List<X509Certificate> getCertificates() { 1560 return mCertificates; 1561 } 1562 1563 /** 1564 * If this signer is a DSA signer, whether or not the signing is done deterministically. 1565 */ getDeterministicDsaSigning()1566 public boolean getDeterministicDsaSigning() { 1567 return mDeterministicDsaSigning; 1568 } 1569 1570 /** Builder of {@link SignerConfig} instances. */ 1571 public static class Builder { 1572 private final String mName; 1573 private final PrivateKey mPrivateKey; 1574 private final List<X509Certificate> mCertificates; 1575 private final boolean mDeterministicDsaSigning; 1576 1577 /** 1578 * Constructs a new {@code Builder}. 1579 * 1580 * @param name signer's name. The name is reflected in the name of files comprising the 1581 * JAR signature of the APK. 1582 * @param privateKey signing key 1583 * @param certificates list of one or more X.509 certificates. The subject public key of 1584 * the first certificate must correspond to the {@code privateKey}. 1585 */ Builder(String name, PrivateKey privateKey, List<X509Certificate> certificates)1586 public Builder(String name, PrivateKey privateKey, List<X509Certificate> certificates) { 1587 this(name, privateKey, certificates, false); 1588 } 1589 1590 /** 1591 * Constructs a new {@code Builder}. 1592 * 1593 * @param name signer's name. The name is reflected in the name of files comprising the 1594 * JAR signature of the APK. 1595 * @param privateKey signing key 1596 * @param certificates list of one or more X.509 certificates. The subject public key of 1597 * the first certificate must correspond to the {@code privateKey}. 1598 * @param deterministicDsaSigning When signing using DSA, whether or not the 1599 * deterministic signing algorithm variant (RFC6979) should be used. 1600 */ Builder(String name, PrivateKey privateKey, List<X509Certificate> certificates, boolean deterministicDsaSigning)1601 public Builder(String name, PrivateKey privateKey, List<X509Certificate> certificates, 1602 boolean deterministicDsaSigning) { 1603 if (name.isEmpty()) { 1604 throw new IllegalArgumentException("Empty name"); 1605 } 1606 mName = name; 1607 mPrivateKey = privateKey; 1608 mCertificates = new ArrayList<>(certificates); 1609 mDeterministicDsaSigning = deterministicDsaSigning; 1610 } 1611 1612 /** 1613 * Returns a new {@code SignerConfig} instance configured based on the configuration of 1614 * this builder. 1615 */ build()1616 public SignerConfig build() { 1617 return new SignerConfig(mName, mPrivateKey, mCertificates, 1618 mDeterministicDsaSigning); 1619 } 1620 } 1621 } 1622 1623 /** Builder of {@link DefaultApkSignerEngine} instances. */ 1624 public static class Builder { 1625 private List<SignerConfig> mSignerConfigs; 1626 private SignerConfig mStampSignerConfig; 1627 private SigningCertificateLineage mSourceStampSigningCertificateLineage; 1628 private final int mMinSdkVersion; 1629 1630 private boolean mV1SigningEnabled = true; 1631 private boolean mV2SigningEnabled = true; 1632 private boolean mV3SigningEnabled = true; 1633 private boolean mVerityEnabled = false; 1634 private boolean mDebuggableApkPermitted = true; 1635 private boolean mOtherSignersSignaturesPreserved; 1636 private String mCreatedBy = "1.0 (Android)"; 1637 1638 private SigningCertificateLineage mSigningCertificateLineage; 1639 1640 // APK Signature Scheme v3 only supports a single signing certificate, so to move to v3 1641 // signing by default, but not require prior clients to update to explicitly disable v3 1642 // signing for multiple signers, we modify the mV3SigningEnabled depending on the provided 1643 // inputs (multiple signers and mSigningCertificateLineage in particular). Maintain two 1644 // extra variables to record whether or not mV3SigningEnabled has been set directly by a 1645 // client and so should override the default behavior. 1646 private boolean mV3SigningExplicitlyDisabled = false; 1647 private boolean mV3SigningExplicitlyEnabled = false; 1648 1649 /** 1650 * Constructs a new {@code Builder}. 1651 * 1652 * @param signerConfigs information about signers with which the APK will be signed. At 1653 * least one signer configuration must be provided. 1654 * @param minSdkVersion API Level of the oldest Android platform on which the APK is 1655 * supposed to be installed. See {@code minSdkVersion} attribute in the APK's {@code 1656 * AndroidManifest.xml}. The higher the version, the stronger signing features will be 1657 * enabled. 1658 */ Builder(List<SignerConfig> signerConfigs, int minSdkVersion)1659 public Builder(List<SignerConfig> signerConfigs, int minSdkVersion) { 1660 if (signerConfigs.isEmpty()) { 1661 throw new IllegalArgumentException("At least one signer config must be provided"); 1662 } 1663 if (signerConfigs.size() > 1) { 1664 // APK Signature Scheme v3 only supports single signer, unless a 1665 // SigningCertificateLineage is provided, in which case this will be reset to true, 1666 // since we don't yet have a v4 scheme about which to worry 1667 mV3SigningEnabled = false; 1668 } 1669 mSignerConfigs = new ArrayList<>(signerConfigs); 1670 mMinSdkVersion = minSdkVersion; 1671 } 1672 1673 /** 1674 * Returns a new {@code DefaultApkSignerEngine} instance configured based on the 1675 * configuration of this builder. 1676 */ build()1677 public DefaultApkSignerEngine build() throws InvalidKeyException { 1678 1679 if (mV3SigningExplicitlyDisabled && mV3SigningExplicitlyEnabled) { 1680 throw new IllegalStateException( 1681 "Builder configured to both enable and disable APK " 1682 + "Signature Scheme v3 signing"); 1683 } 1684 if (mV3SigningExplicitlyDisabled) { 1685 mV3SigningEnabled = false; 1686 } else if (mV3SigningExplicitlyEnabled) { 1687 mV3SigningEnabled = true; 1688 } 1689 1690 // make sure our signers are appropriately setup 1691 if (mSigningCertificateLineage != null) { 1692 try { 1693 mSignerConfigs = mSigningCertificateLineage.sortSignerConfigs(mSignerConfigs); 1694 if (!mV3SigningEnabled && mSignerConfigs.size() > 1) { 1695 1696 // this is a strange situation: we've provided a valid rotation history, but 1697 // are only signing with v1/v2. blow up, since we don't know for sure with 1698 // which signer the user intended to sign 1699 throw new IllegalStateException( 1700 "Provided multiple signers which are part of the" 1701 + " SigningCertificateLineage, but not signing with APK" 1702 + " Signature Scheme v3"); 1703 } 1704 } catch (IllegalArgumentException e) { 1705 throw new IllegalStateException( 1706 "Provided signer configs do not match the " 1707 + "provided SigningCertificateLineage", 1708 e); 1709 } 1710 } else if (mV3SigningEnabled && mSignerConfigs.size() > 1) { 1711 throw new IllegalStateException( 1712 "Multiple signing certificates provided for use with APK Signature Scheme" 1713 + " v3 without an accompanying SigningCertificateLineage"); 1714 } 1715 1716 return new DefaultApkSignerEngine( 1717 mSignerConfigs, 1718 mStampSignerConfig, 1719 mSourceStampSigningCertificateLineage, 1720 mMinSdkVersion, 1721 mV1SigningEnabled, 1722 mV2SigningEnabled, 1723 mV3SigningEnabled, 1724 mVerityEnabled, 1725 mDebuggableApkPermitted, 1726 mOtherSignersSignaturesPreserved, 1727 mCreatedBy, 1728 mSigningCertificateLineage); 1729 } 1730 1731 /** Sets the signer configuration for the SourceStamp to be embedded in the APK. */ setStampSignerConfig(SignerConfig stampSignerConfig)1732 public Builder setStampSignerConfig(SignerConfig stampSignerConfig) { 1733 mStampSignerConfig = stampSignerConfig; 1734 return this; 1735 } 1736 1737 /** 1738 * Sets the source stamp {@link SigningCertificateLineage}. This structure provides proof of 1739 * signing certificate rotation for certificates previously used to sign source stamps. 1740 */ setSourceStampSigningCertificateLineage( SigningCertificateLineage sourceStampSigningCertificateLineage)1741 public Builder setSourceStampSigningCertificateLineage( 1742 SigningCertificateLineage sourceStampSigningCertificateLineage) { 1743 mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage; 1744 return this; 1745 } 1746 1747 /** 1748 * Sets whether the APK should be signed using JAR signing (aka v1 signature scheme). 1749 * 1750 * <p>By default, the APK will be signed using this scheme. 1751 */ setV1SigningEnabled(boolean enabled)1752 public Builder setV1SigningEnabled(boolean enabled) { 1753 mV1SigningEnabled = enabled; 1754 return this; 1755 } 1756 1757 /** 1758 * Sets whether the APK should be signed using APK Signature Scheme v2 (aka v2 signature 1759 * scheme). 1760 * 1761 * <p>By default, the APK will be signed using this scheme. 1762 */ setV2SigningEnabled(boolean enabled)1763 public Builder setV2SigningEnabled(boolean enabled) { 1764 mV2SigningEnabled = enabled; 1765 return this; 1766 } 1767 1768 /** 1769 * Sets whether the APK should be signed using APK Signature Scheme v3 (aka v3 signature 1770 * scheme). 1771 * 1772 * <p>By default, the APK will be signed using this scheme. 1773 */ setV3SigningEnabled(boolean enabled)1774 public Builder setV3SigningEnabled(boolean enabled) { 1775 mV3SigningEnabled = enabled; 1776 if (enabled) { 1777 mV3SigningExplicitlyEnabled = true; 1778 } else { 1779 mV3SigningExplicitlyDisabled = true; 1780 } 1781 return this; 1782 } 1783 1784 /** 1785 * Sets whether the APK should be signed using the verity signature algorithm in the v2 and 1786 * v3 signature blocks. 1787 * 1788 * <p>By default, the APK will be signed using the verity signature algorithm for the v2 and 1789 * v3 signature schemes. 1790 */ setVerityEnabled(boolean enabled)1791 public Builder setVerityEnabled(boolean enabled) { 1792 mVerityEnabled = enabled; 1793 return this; 1794 } 1795 1796 /** 1797 * Sets whether the APK should be signed even if it is marked as debuggable ({@code 1798 * android:debuggable="true"} in its {@code AndroidManifest.xml}). For backward 1799 * compatibility reasons, the default value of this setting is {@code true}. 1800 * 1801 * <p>It is dangerous to sign debuggable APKs with production/release keys because Android 1802 * platform loosens security checks for such APKs. For example, arbitrary unauthorized code 1803 * may be executed in the context of such an app by anybody with ADB shell access. 1804 */ setDebuggableApkPermitted(boolean permitted)1805 public Builder setDebuggableApkPermitted(boolean permitted) { 1806 mDebuggableApkPermitted = permitted; 1807 return this; 1808 } 1809 1810 /** 1811 * Sets whether signatures produced by signers other than the ones configured in this engine 1812 * should be copied from the input APK to the output APK. 1813 * 1814 * <p>By default, signatures of other signers are omitted from the output APK. 1815 */ setOtherSignersSignaturesPreserved(boolean preserved)1816 public Builder setOtherSignersSignaturesPreserved(boolean preserved) { 1817 mOtherSignersSignaturesPreserved = preserved; 1818 return this; 1819 } 1820 1821 /** Sets the value of the {@code Created-By} field in JAR signature files. */ setCreatedBy(String createdBy)1822 public Builder setCreatedBy(String createdBy) { 1823 if (createdBy == null) { 1824 throw new NullPointerException(); 1825 } 1826 mCreatedBy = createdBy; 1827 return this; 1828 } 1829 1830 /** 1831 * Sets the {@link SigningCertificateLineage} to use with the v3 signature scheme. This 1832 * structure provides proof of signing certificate rotation linking {@link SignerConfig} 1833 * objects to previous ones. 1834 */ setSigningCertificateLineage( SigningCertificateLineage signingCertificateLineage)1835 public Builder setSigningCertificateLineage( 1836 SigningCertificateLineage signingCertificateLineage) { 1837 if (signingCertificateLineage != null) { 1838 mV3SigningEnabled = true; 1839 mSigningCertificateLineage = signingCertificateLineage; 1840 } 1841 return this; 1842 } 1843 } 1844 } 1845