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.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT; 21 import static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V3_SUPPORT; 22 23 import com.android.apksig.apk.ApkFormatException; 24 import com.android.apksig.apk.ApkSigningBlockNotFoundException; 25 import com.android.apksig.apk.ApkUtils; 26 import com.android.apksig.apk.MinSdkVersionException; 27 import com.android.apksig.internal.apk.v3.V3SchemeConstants; 28 import com.android.apksig.internal.util.AndroidSdkVersion; 29 import com.android.apksig.internal.util.ByteBufferDataSource; 30 import com.android.apksig.internal.zip.CentralDirectoryRecord; 31 import com.android.apksig.internal.zip.EocdRecord; 32 import com.android.apksig.internal.zip.LocalFileRecord; 33 import com.android.apksig.internal.zip.ZipUtils; 34 import com.android.apksig.util.DataSink; 35 import com.android.apksig.util.DataSinks; 36 import com.android.apksig.util.DataSource; 37 import com.android.apksig.util.DataSources; 38 import com.android.apksig.util.ReadableDataSink; 39 import com.android.apksig.zip.ZipFormatException; 40 41 import java.io.Closeable; 42 import java.io.File; 43 import java.io.IOException; 44 import java.io.RandomAccessFile; 45 import java.nio.ByteBuffer; 46 import java.nio.ByteOrder; 47 import java.security.InvalidKeyException; 48 import java.security.NoSuchAlgorithmException; 49 import java.security.PrivateKey; 50 import java.security.SignatureException; 51 import java.security.cert.X509Certificate; 52 import java.util.ArrayList; 53 import java.util.Arrays; 54 import java.util.Collections; 55 import java.util.HashMap; 56 import java.util.HashSet; 57 import java.util.List; 58 import java.util.Map; 59 import java.util.Set; 60 61 /** 62 * APK signer. 63 * 64 * <p>The signer preserves as much of the input APK as possible. For example, it preserves the order 65 * of APK entries and preserves their contents, including compressed form and alignment of data. 66 * 67 * <p>Use {@link Builder} to obtain instances of this signer. 68 * 69 * @see <a href="https://source.android.com/security/apksigning/index.html">Application Signing</a> 70 */ 71 public class ApkSigner { 72 73 /** 74 * Extensible data block/field header ID used for storing information about alignment of 75 * uncompressed entries as well as for aligning the entries's data. See ZIP appnote.txt section 76 * 4.5 Extensible data fields. 77 */ 78 private static final short ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID = (short) 0xd935; 79 80 /** 81 * Minimum size (in bytes) of the extensible data block/field used for alignment of uncompressed 82 * entries. 83 */ 84 private static final short ALIGNMENT_ZIP_EXTRA_DATA_FIELD_MIN_SIZE_BYTES = 6; 85 86 private static final short ANDROID_COMMON_PAGE_ALIGNMENT_BYTES = 4096; 87 88 private static final short ANDROID_FILE_ALIGNMENT_BYTES = 4096; 89 90 /** Name of the Android manifest ZIP entry in APKs. */ 91 private static final String ANDROID_MANIFEST_ZIP_ENTRY_NAME = "AndroidManifest.xml"; 92 93 private final List<SignerConfig> mSignerConfigs; 94 private final SignerConfig mSourceStampSignerConfig; 95 private final SigningCertificateLineage mSourceStampSigningCertificateLineage; 96 private final boolean mForceSourceStampOverwrite; 97 private final boolean mSourceStampTimestampEnabled; 98 private final Integer mMinSdkVersion; 99 private final int mRotationMinSdkVersion; 100 private final boolean mRotationTargetsDevRelease; 101 private final boolean mV1SigningEnabled; 102 private final boolean mV2SigningEnabled; 103 private final boolean mV3SigningEnabled; 104 private final boolean mV4SigningEnabled; 105 private final boolean mAlignFileSize; 106 private final boolean mVerityEnabled; 107 private final boolean mV4ErrorReportingEnabled; 108 private final boolean mDebuggableApkPermitted; 109 private final boolean mOtherSignersSignaturesPreserved; 110 private final String mCreatedBy; 111 112 private final ApkSignerEngine mSignerEngine; 113 114 private final File mInputApkFile; 115 private final DataSource mInputApkDataSource; 116 117 private final File mOutputApkFile; 118 private final DataSink mOutputApkDataSink; 119 private final DataSource mOutputApkDataSource; 120 121 private final File mOutputV4File; 122 123 private final SigningCertificateLineage mSigningCertificateLineage; 124 ApkSigner( List<SignerConfig> signerConfigs, SignerConfig sourceStampSignerConfig, SigningCertificateLineage sourceStampSigningCertificateLineage, boolean forceSourceStampOverwrite, boolean sourceStampTimestampEnabled, Integer minSdkVersion, int rotationMinSdkVersion, boolean rotationTargetsDevRelease, boolean v1SigningEnabled, boolean v2SigningEnabled, boolean v3SigningEnabled, boolean v4SigningEnabled, boolean alignFileSize, boolean verityEnabled, boolean v4ErrorReportingEnabled, boolean debuggableApkPermitted, boolean otherSignersSignaturesPreserved, String createdBy, ApkSignerEngine signerEngine, File inputApkFile, DataSource inputApkDataSource, File outputApkFile, DataSink outputApkDataSink, DataSource outputApkDataSource, File outputV4File, SigningCertificateLineage signingCertificateLineage)125 private ApkSigner( 126 List<SignerConfig> signerConfigs, 127 SignerConfig sourceStampSignerConfig, 128 SigningCertificateLineage sourceStampSigningCertificateLineage, 129 boolean forceSourceStampOverwrite, 130 boolean sourceStampTimestampEnabled, 131 Integer minSdkVersion, 132 int rotationMinSdkVersion, 133 boolean rotationTargetsDevRelease, 134 boolean v1SigningEnabled, 135 boolean v2SigningEnabled, 136 boolean v3SigningEnabled, 137 boolean v4SigningEnabled, 138 boolean alignFileSize, 139 boolean verityEnabled, 140 boolean v4ErrorReportingEnabled, 141 boolean debuggableApkPermitted, 142 boolean otherSignersSignaturesPreserved, 143 String createdBy, 144 ApkSignerEngine signerEngine, 145 File inputApkFile, 146 DataSource inputApkDataSource, 147 File outputApkFile, 148 DataSink outputApkDataSink, 149 DataSource outputApkDataSource, 150 File outputV4File, 151 SigningCertificateLineage signingCertificateLineage) { 152 153 mSignerConfigs = signerConfigs; 154 mSourceStampSignerConfig = sourceStampSignerConfig; 155 mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage; 156 mForceSourceStampOverwrite = forceSourceStampOverwrite; 157 mSourceStampTimestampEnabled = sourceStampTimestampEnabled; 158 mMinSdkVersion = minSdkVersion; 159 mRotationMinSdkVersion = rotationMinSdkVersion; 160 mRotationTargetsDevRelease = rotationTargetsDevRelease; 161 mV1SigningEnabled = v1SigningEnabled; 162 mV2SigningEnabled = v2SigningEnabled; 163 mV3SigningEnabled = v3SigningEnabled; 164 mV4SigningEnabled = v4SigningEnabled; 165 mAlignFileSize = alignFileSize; 166 mVerityEnabled = verityEnabled; 167 mV4ErrorReportingEnabled = v4ErrorReportingEnabled; 168 mDebuggableApkPermitted = debuggableApkPermitted; 169 mOtherSignersSignaturesPreserved = otherSignersSignaturesPreserved; 170 mCreatedBy = createdBy; 171 172 mSignerEngine = signerEngine; 173 174 mInputApkFile = inputApkFile; 175 mInputApkDataSource = inputApkDataSource; 176 177 mOutputApkFile = outputApkFile; 178 mOutputApkDataSink = outputApkDataSink; 179 mOutputApkDataSource = outputApkDataSource; 180 181 mOutputV4File = outputV4File; 182 183 mSigningCertificateLineage = signingCertificateLineage; 184 } 185 186 /** 187 * Signs the input APK and outputs the resulting signed APK. The input APK is not modified. 188 * 189 * @throws IOException if an I/O error is encountered while reading or writing the APKs 190 * @throws ApkFormatException if the input APK is malformed 191 * @throws NoSuchAlgorithmException if the APK signatures cannot be produced or verified because 192 * a required cryptographic algorithm implementation is missing 193 * @throws InvalidKeyException if a signature could not be generated because a signing key is 194 * not suitable for generating the signature 195 * @throws SignatureException if an error occurred while generating or verifying a signature 196 * @throws IllegalStateException if this signer's configuration is missing required information 197 * or if the signing engine is in an invalid state. 198 */ sign()199 public void sign() 200 throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException, 201 SignatureException, IllegalStateException { 202 Closeable in = null; 203 DataSource inputApk; 204 try { 205 if (mInputApkDataSource != null) { 206 inputApk = mInputApkDataSource; 207 } else if (mInputApkFile != null) { 208 RandomAccessFile inputFile = new RandomAccessFile(mInputApkFile, "r"); 209 in = inputFile; 210 inputApk = DataSources.asDataSource(inputFile); 211 } else { 212 throw new IllegalStateException("Input APK not specified"); 213 } 214 215 Closeable out = null; 216 try { 217 DataSink outputApkOut; 218 DataSource outputApkIn; 219 if (mOutputApkDataSink != null) { 220 outputApkOut = mOutputApkDataSink; 221 outputApkIn = mOutputApkDataSource; 222 } else if (mOutputApkFile != null) { 223 RandomAccessFile outputFile = new RandomAccessFile(mOutputApkFile, "rw"); 224 out = outputFile; 225 outputFile.setLength(0); 226 outputApkOut = DataSinks.asDataSink(outputFile); 227 outputApkIn = DataSources.asDataSource(outputFile); 228 } else { 229 throw new IllegalStateException("Output APK not specified"); 230 } 231 232 sign(inputApk, outputApkOut, outputApkIn); 233 } finally { 234 if (out != null) { 235 out.close(); 236 } 237 } 238 } finally { 239 if (in != null) { 240 in.close(); 241 } 242 } 243 } 244 sign(DataSource inputApk, DataSink outputApkOut, DataSource outputApkIn)245 private void sign(DataSource inputApk, DataSink outputApkOut, DataSource outputApkIn) 246 throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException, 247 SignatureException { 248 // Step 1. Find input APK's main ZIP sections 249 ApkUtils.ZipSections inputZipSections; 250 try { 251 inputZipSections = ApkUtils.findZipSections(inputApk); 252 } catch (ZipFormatException e) { 253 throw new ApkFormatException("Malformed APK: not a ZIP archive", e); 254 } 255 long inputApkSigningBlockOffset = -1; 256 DataSource inputApkSigningBlock = null; 257 try { 258 ApkUtils.ApkSigningBlock apkSigningBlockInfo = 259 ApkUtils.findApkSigningBlock(inputApk, inputZipSections); 260 inputApkSigningBlockOffset = apkSigningBlockInfo.getStartOffset(); 261 inputApkSigningBlock = apkSigningBlockInfo.getContents(); 262 } catch (ApkSigningBlockNotFoundException e) { 263 // Input APK does not contain an APK Signing Block. That's OK. APKs are not required to 264 // contain this block. It's only needed if the APK is signed using APK Signature Scheme 265 // v2 and/or v3. 266 } 267 DataSource inputApkLfhSection = 268 inputApk.slice( 269 0, 270 (inputApkSigningBlockOffset != -1) 271 ? inputApkSigningBlockOffset 272 : inputZipSections.getZipCentralDirectoryOffset()); 273 274 // Step 2. Parse the input APK's ZIP Central Directory 275 ByteBuffer inputCd = getZipCentralDirectory(inputApk, inputZipSections); 276 List<CentralDirectoryRecord> inputCdRecords = 277 parseZipCentralDirectory(inputCd, inputZipSections); 278 279 List<Hints.PatternWithRange> pinPatterns = 280 extractPinPatterns(inputCdRecords, inputApkLfhSection); 281 List<Hints.ByteRange> pinByteRanges = pinPatterns == null ? null : new ArrayList<>(); 282 283 // Step 3. Obtain a signer engine instance 284 ApkSignerEngine signerEngine; 285 if (mSignerEngine != null) { 286 // Use the provided signer engine 287 signerEngine = mSignerEngine; 288 } else { 289 // Construct a signer engine from the provided parameters 290 int minSdkVersion; 291 if (mMinSdkVersion != null) { 292 // No need to extract minSdkVersion from the APK's AndroidManifest.xml 293 minSdkVersion = mMinSdkVersion; 294 } else { 295 // Need to extract minSdkVersion from the APK's AndroidManifest.xml 296 minSdkVersion = getMinSdkVersionFromApk(inputCdRecords, inputApkLfhSection); 297 } 298 List<DefaultApkSignerEngine.SignerConfig> engineSignerConfigs = 299 new ArrayList<>(mSignerConfigs.size()); 300 for (SignerConfig signerConfig : mSignerConfigs) { 301 DefaultApkSignerEngine.SignerConfig.Builder signerConfigBuilder = 302 new DefaultApkSignerEngine.SignerConfig.Builder( 303 signerConfig.getName(), 304 signerConfig.getPrivateKey(), 305 signerConfig.getCertificates(), 306 signerConfig.getDeterministicDsaSigning()); 307 int signerMinSdkVersion = signerConfig.getMinSdkVersion(); 308 SigningCertificateLineage signerLineage = 309 signerConfig.getSigningCertificateLineage(); 310 if (signerMinSdkVersion > 0) { 311 signerConfigBuilder.setLineageForMinSdkVersion(signerLineage, 312 signerMinSdkVersion); 313 } 314 engineSignerConfigs.add(signerConfigBuilder.build()); 315 } 316 DefaultApkSignerEngine.Builder signerEngineBuilder = 317 new DefaultApkSignerEngine.Builder(engineSignerConfigs, minSdkVersion) 318 .setV1SigningEnabled(mV1SigningEnabled) 319 .setV2SigningEnabled(mV2SigningEnabled) 320 .setV3SigningEnabled(mV3SigningEnabled) 321 .setVerityEnabled(mVerityEnabled) 322 .setDebuggableApkPermitted(mDebuggableApkPermitted) 323 .setOtherSignersSignaturesPreserved(mOtherSignersSignaturesPreserved) 324 .setSigningCertificateLineage(mSigningCertificateLineage) 325 .setMinSdkVersionForRotation(mRotationMinSdkVersion) 326 .setRotationTargetsDevRelease(mRotationTargetsDevRelease); 327 if (mCreatedBy != null) { 328 signerEngineBuilder.setCreatedBy(mCreatedBy); 329 } 330 if (mSourceStampSignerConfig != null) { 331 signerEngineBuilder.setStampSignerConfig( 332 new DefaultApkSignerEngine.SignerConfig.Builder( 333 mSourceStampSignerConfig.getName(), 334 mSourceStampSignerConfig.getPrivateKey(), 335 mSourceStampSignerConfig.getCertificates(), 336 mSourceStampSignerConfig.getDeterministicDsaSigning()) 337 .build()); 338 signerEngineBuilder.setSourceStampTimestampEnabled(mSourceStampTimestampEnabled); 339 } 340 if (mSourceStampSigningCertificateLineage != null) { 341 signerEngineBuilder.setSourceStampSigningCertificateLineage( 342 mSourceStampSigningCertificateLineage); 343 } 344 signerEngine = signerEngineBuilder.build(); 345 } 346 347 // Step 4. Provide the signer engine with the input APK's APK Signing Block (if any) 348 if (inputApkSigningBlock != null) { 349 signerEngine.inputApkSigningBlock(inputApkSigningBlock); 350 } 351 352 // Step 5. Iterate over input APK's entries and output the Local File Header + data of those 353 // entries which need to be output. Entries are iterated in the order in which their Local 354 // File Header records are stored in the file. This is to achieve better data locality in 355 // case Central Directory entries are in the wrong order. 356 List<CentralDirectoryRecord> inputCdRecordsSortedByLfhOffset = 357 new ArrayList<>(inputCdRecords); 358 Collections.sort( 359 inputCdRecordsSortedByLfhOffset, 360 CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR); 361 int lastModifiedDateForNewEntries = -1; 362 int lastModifiedTimeForNewEntries = -1; 363 long inputOffset = 0; 364 long outputOffset = 0; 365 byte[] sourceStampCertificateDigest = null; 366 Map<String, CentralDirectoryRecord> outputCdRecordsByName = 367 new HashMap<>(inputCdRecords.size()); 368 for (final CentralDirectoryRecord inputCdRecord : inputCdRecordsSortedByLfhOffset) { 369 String entryName = inputCdRecord.getName(); 370 if (Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME.equals(entryName)) { 371 continue; // We'll re-add below if needed. 372 } 373 if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(entryName)) { 374 try { 375 sourceStampCertificateDigest = 376 LocalFileRecord.getUncompressedData( 377 inputApkLfhSection, inputCdRecord, inputApkLfhSection.size()); 378 } catch (ZipFormatException ex) { 379 throw new ApkFormatException("Bad source stamp entry"); 380 } 381 continue; // Existing source stamp is handled below as needed. 382 } 383 ApkSignerEngine.InputJarEntryInstructions entryInstructions = 384 signerEngine.inputJarEntry(entryName); 385 boolean shouldOutput; 386 switch (entryInstructions.getOutputPolicy()) { 387 case OUTPUT: 388 shouldOutput = true; 389 break; 390 case OUTPUT_BY_ENGINE: 391 case SKIP: 392 shouldOutput = false; 393 break; 394 default: 395 throw new RuntimeException( 396 "Unknown output policy: " + entryInstructions.getOutputPolicy()); 397 } 398 399 long inputLocalFileHeaderStartOffset = inputCdRecord.getLocalFileHeaderOffset(); 400 if (inputLocalFileHeaderStartOffset > inputOffset) { 401 // Unprocessed data in input starting at inputOffset and ending and the start of 402 // this record's LFH. We output this data verbatim because this signer is supposed 403 // to preserve as much of input as possible. 404 long chunkSize = inputLocalFileHeaderStartOffset - inputOffset; 405 inputApkLfhSection.feed(inputOffset, chunkSize, outputApkOut); 406 outputOffset += chunkSize; 407 inputOffset = inputLocalFileHeaderStartOffset; 408 } 409 LocalFileRecord inputLocalFileRecord; 410 try { 411 inputLocalFileRecord = 412 LocalFileRecord.getRecord( 413 inputApkLfhSection, inputCdRecord, inputApkLfhSection.size()); 414 } catch (ZipFormatException e) { 415 throw new ApkFormatException("Malformed ZIP entry: " + inputCdRecord.getName(), e); 416 } 417 inputOffset += inputLocalFileRecord.getSize(); 418 419 ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest = 420 entryInstructions.getInspectJarEntryRequest(); 421 if (inspectEntryRequest != null) { 422 fulfillInspectInputJarEntryRequest( 423 inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest); 424 } 425 426 if (shouldOutput) { 427 // Find the max value of last modified, to be used for new entries added by the 428 // signer. 429 int lastModifiedDate = inputCdRecord.getLastModificationDate(); 430 int lastModifiedTime = inputCdRecord.getLastModificationTime(); 431 if ((lastModifiedDateForNewEntries == -1) 432 || (lastModifiedDate > lastModifiedDateForNewEntries) 433 || ((lastModifiedDate == lastModifiedDateForNewEntries) 434 && (lastModifiedTime > lastModifiedTimeForNewEntries))) { 435 lastModifiedDateForNewEntries = lastModifiedDate; 436 lastModifiedTimeForNewEntries = lastModifiedTime; 437 } 438 439 inspectEntryRequest = signerEngine.outputJarEntry(entryName); 440 if (inspectEntryRequest != null) { 441 fulfillInspectInputJarEntryRequest( 442 inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest); 443 } 444 445 // Output entry's Local File Header + data 446 long outputLocalFileHeaderOffset = outputOffset; 447 OutputSizeAndDataOffset outputLfrResult = 448 outputInputJarEntryLfhRecordPreservingDataAlignment( 449 inputApkLfhSection, 450 inputLocalFileRecord, 451 outputApkOut, 452 outputLocalFileHeaderOffset); 453 outputOffset += outputLfrResult.outputBytes; 454 long outputDataOffset = 455 outputLocalFileHeaderOffset + outputLfrResult.dataOffsetBytes; 456 457 if (pinPatterns != null) { 458 boolean pinFileHeader = false; 459 for (Hints.PatternWithRange pinPattern : pinPatterns) { 460 if (pinPattern.matcher(inputCdRecord.getName()).matches()) { 461 Hints.ByteRange dataRange = 462 new Hints.ByteRange(outputDataOffset, outputOffset); 463 Hints.ByteRange pinRange = 464 pinPattern.ClampToAbsoluteByteRange(dataRange); 465 if (pinRange != null) { 466 pinFileHeader = true; 467 pinByteRanges.add(pinRange); 468 } 469 } 470 } 471 if (pinFileHeader) { 472 pinByteRanges.add( 473 new Hints.ByteRange(outputLocalFileHeaderOffset, outputDataOffset)); 474 } 475 } 476 477 // Enqueue entry's Central Directory record for output 478 CentralDirectoryRecord outputCdRecord; 479 if (outputLocalFileHeaderOffset == inputLocalFileRecord.getStartOffsetInArchive()) { 480 outputCdRecord = inputCdRecord; 481 } else { 482 outputCdRecord = 483 inputCdRecord.createWithModifiedLocalFileHeaderOffset( 484 outputLocalFileHeaderOffset); 485 } 486 outputCdRecordsByName.put(entryName, outputCdRecord); 487 } 488 } 489 long inputLfhSectionSize = inputApkLfhSection.size(); 490 if (inputOffset < inputLfhSectionSize) { 491 // Unprocessed data in input starting at inputOffset and ending and the end of the input 492 // APK's LFH section. We output this data verbatim because this signer is supposed 493 // to preserve as much of input as possible. 494 long chunkSize = inputLfhSectionSize - inputOffset; 495 inputApkLfhSection.feed(inputOffset, chunkSize, outputApkOut); 496 outputOffset += chunkSize; 497 inputOffset = inputLfhSectionSize; 498 } 499 500 // Step 6. Sort output APK's Central Directory records in the order in which they should 501 // appear in the output 502 List<CentralDirectoryRecord> outputCdRecords = new ArrayList<>(inputCdRecords.size() + 10); 503 for (CentralDirectoryRecord inputCdRecord : inputCdRecords) { 504 String entryName = inputCdRecord.getName(); 505 CentralDirectoryRecord outputCdRecord = outputCdRecordsByName.get(entryName); 506 if (outputCdRecord != null) { 507 outputCdRecords.add(outputCdRecord); 508 } 509 } 510 511 if (lastModifiedDateForNewEntries == -1) { 512 lastModifiedDateForNewEntries = 0x3a21; // Jan 1 2009 (DOS) 513 lastModifiedTimeForNewEntries = 0; 514 } 515 516 // Step 7. Generate and output SourceStamp certificate hash, if necessary. This may output 517 // more Local File Header + data entries and add to the list of output Central Directory 518 // records. 519 if (signerEngine.isEligibleForSourceStamp()) { 520 byte[] uncompressedData = signerEngine.generateSourceStampCertificateDigest(); 521 if (mForceSourceStampOverwrite 522 || sourceStampCertificateDigest == null 523 || Arrays.equals(uncompressedData, sourceStampCertificateDigest)) { 524 outputOffset += 525 outputDataToOutputApk( 526 SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME, 527 uncompressedData, 528 outputOffset, 529 outputCdRecords, 530 lastModifiedTimeForNewEntries, 531 lastModifiedDateForNewEntries, 532 outputApkOut); 533 } else { 534 throw new ApkFormatException( 535 String.format( 536 "Cannot generate SourceStamp. APK contains an existing entry with" 537 + " the name: %s, and it is different than the provided source" 538 + " stamp certificate", 539 SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME)); 540 } 541 } 542 543 // Step 7.5. Generate pinlist.meta file if necessary. 544 // This has to be before the step 8 so that the file is signed. 545 if (pinByteRanges != null) { 546 // Covers JAR signature and zip central dir entry. 547 // The signature files don't have to be pinned, but pinning them isn't that wasteful 548 // since the total size is small. 549 pinByteRanges.add(new Hints.ByteRange(outputOffset, Long.MAX_VALUE)); 550 String entryName = Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME; 551 byte[] uncompressedData = Hints.encodeByteRangeList(pinByteRanges); 552 553 requestOutputEntryInspection(signerEngine, entryName, uncompressedData); 554 outputOffset += 555 outputDataToOutputApk( 556 entryName, 557 uncompressedData, 558 outputOffset, 559 outputCdRecords, 560 lastModifiedTimeForNewEntries, 561 lastModifiedDateForNewEntries, 562 outputApkOut); 563 } 564 565 // Step 8. Generate and output JAR signatures, if necessary. This may output more Local File 566 // Header + data entries and add to the list of output Central Directory records. 567 ApkSignerEngine.OutputJarSignatureRequest outputJarSignatureRequest = 568 signerEngine.outputJarEntries(); 569 if (outputJarSignatureRequest != null) { 570 for (ApkSignerEngine.OutputJarSignatureRequest.JarEntry entry : 571 outputJarSignatureRequest.getAdditionalJarEntries()) { 572 String entryName = entry.getName(); 573 byte[] uncompressedData = entry.getData(); 574 575 requestOutputEntryInspection(signerEngine, entryName, uncompressedData); 576 outputOffset += 577 outputDataToOutputApk( 578 entryName, 579 uncompressedData, 580 outputOffset, 581 outputCdRecords, 582 lastModifiedTimeForNewEntries, 583 lastModifiedDateForNewEntries, 584 outputApkOut); 585 } 586 outputJarSignatureRequest.done(); 587 } 588 589 // Step 9. Construct output ZIP Central Directory in an in-memory buffer 590 long outputCentralDirSizeBytes = 0; 591 for (CentralDirectoryRecord record : outputCdRecords) { 592 outputCentralDirSizeBytes += record.getSize(); 593 } 594 if (outputCentralDirSizeBytes > Integer.MAX_VALUE) { 595 throw new IOException( 596 "Output ZIP Central Directory too large: " 597 + outputCentralDirSizeBytes 598 + " bytes"); 599 } 600 ByteBuffer outputCentralDir = ByteBuffer.allocate((int) outputCentralDirSizeBytes); 601 for (CentralDirectoryRecord record : outputCdRecords) { 602 record.copyTo(outputCentralDir); 603 } 604 outputCentralDir.flip(); 605 DataSource outputCentralDirDataSource = new ByteBufferDataSource(outputCentralDir); 606 long outputCentralDirStartOffset = outputOffset; 607 int outputCentralDirRecordCount = outputCdRecords.size(); 608 609 // Step 10. Construct output ZIP End of Central Directory record in an in-memory buffer 610 // because it can be adjusted in Step 11 due to signing block. 611 // - CD offset (it's shifted by signing block) 612 // - Comments (when the output file needs to be sized 4k-aligned) 613 ByteBuffer outputEocd = 614 EocdRecord.createWithModifiedCentralDirectoryInfo( 615 inputZipSections.getZipEndOfCentralDirectory(), 616 outputCentralDirRecordCount, 617 outputCentralDirDataSource.size(), 618 outputCentralDirStartOffset); 619 620 // Step 11. Generate and output APK Signature Scheme v2 and/or v3 signatures and/or 621 // SourceStamp signatures, if necessary. 622 // This may insert an APK Signing Block just before the output's ZIP Central Directory 623 ApkSignerEngine.OutputApkSigningBlockRequest2 outputApkSigningBlockRequest = 624 signerEngine.outputZipSections2( 625 outputApkIn, 626 outputCentralDirDataSource, 627 DataSources.asDataSource(outputEocd)); 628 629 if (outputApkSigningBlockRequest != null) { 630 int padding = outputApkSigningBlockRequest.getPaddingSizeBeforeApkSigningBlock(); 631 byte[] outputApkSigningBlock = outputApkSigningBlockRequest.getApkSigningBlock(); 632 outputApkSigningBlockRequest.done(); 633 634 long fileSize = 635 outputCentralDirStartOffset 636 + outputCentralDirDataSource.size() 637 + padding 638 + outputApkSigningBlock.length 639 + outputEocd.remaining(); 640 if (mAlignFileSize && (fileSize % ANDROID_FILE_ALIGNMENT_BYTES != 0)) { 641 int eocdPadding = 642 (int) 643 (ANDROID_FILE_ALIGNMENT_BYTES 644 - fileSize % ANDROID_FILE_ALIGNMENT_BYTES); 645 // Replace EOCD with padding one so that output file size can be the multiples of 646 // alignment. 647 outputEocd = EocdRecord.createWithPaddedComment(outputEocd, eocdPadding); 648 649 // Since EoCD has changed, we need to regenerate signing block as well. 650 outputApkSigningBlockRequest = 651 signerEngine.outputZipSections2( 652 outputApkIn, 653 new ByteBufferDataSource(outputCentralDir), 654 DataSources.asDataSource(outputEocd)); 655 outputApkSigningBlock = outputApkSigningBlockRequest.getApkSigningBlock(); 656 outputApkSigningBlockRequest.done(); 657 } 658 659 outputApkOut.consume(ByteBuffer.allocate(padding)); 660 outputApkOut.consume(outputApkSigningBlock, 0, outputApkSigningBlock.length); 661 ZipUtils.setZipEocdCentralDirectoryOffset( 662 outputEocd, 663 outputCentralDirStartOffset + padding + outputApkSigningBlock.length); 664 } 665 666 // Step 12. Output ZIP Central Directory and ZIP End of Central Directory 667 outputCentralDirDataSource.feed(0, outputCentralDirDataSource.size(), outputApkOut); 668 outputApkOut.consume(outputEocd); 669 signerEngine.outputDone(); 670 671 // Step 13. Generate and output APK Signature Scheme v4 signatures, if necessary. 672 if (mV4SigningEnabled) { 673 signerEngine.signV4(outputApkIn, mOutputV4File, !mV4ErrorReportingEnabled); 674 } 675 } 676 requestOutputEntryInspection( ApkSignerEngine signerEngine, String entryName, byte[] uncompressedData)677 private static void requestOutputEntryInspection( 678 ApkSignerEngine signerEngine, 679 String entryName, 680 byte[] uncompressedData) 681 throws IOException { 682 ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest = 683 signerEngine.outputJarEntry(entryName); 684 if (inspectEntryRequest != null) { 685 inspectEntryRequest.getDataSink().consume( 686 uncompressedData, 0, uncompressedData.length); 687 inspectEntryRequest.done(); 688 } 689 } 690 outputDataToOutputApk( String entryName, byte[] uncompressedData, long localFileHeaderOffset, List<CentralDirectoryRecord> outputCdRecords, int lastModifiedTimeForNewEntries, int lastModifiedDateForNewEntries, DataSink outputApkOut)691 private static long outputDataToOutputApk( 692 String entryName, 693 byte[] uncompressedData, 694 long localFileHeaderOffset, 695 List<CentralDirectoryRecord> outputCdRecords, 696 int lastModifiedTimeForNewEntries, 697 int lastModifiedDateForNewEntries, 698 DataSink outputApkOut) 699 throws IOException { 700 ZipUtils.DeflateResult deflateResult = ZipUtils.deflate(ByteBuffer.wrap(uncompressedData)); 701 byte[] compressedData = deflateResult.output; 702 long uncompressedDataCrc32 = deflateResult.inputCrc32; 703 long numOfDataBytes = 704 LocalFileRecord.outputRecordWithDeflateCompressedData( 705 entryName, 706 lastModifiedTimeForNewEntries, 707 lastModifiedDateForNewEntries, 708 compressedData, 709 uncompressedDataCrc32, 710 uncompressedData.length, 711 outputApkOut); 712 outputCdRecords.add( 713 CentralDirectoryRecord.createWithDeflateCompressedData( 714 entryName, 715 lastModifiedTimeForNewEntries, 716 lastModifiedDateForNewEntries, 717 uncompressedDataCrc32, 718 compressedData.length, 719 uncompressedData.length, 720 localFileHeaderOffset)); 721 return numOfDataBytes; 722 } 723 fulfillInspectInputJarEntryRequest( DataSource lfhSection, LocalFileRecord localFileRecord, ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest)724 private static void fulfillInspectInputJarEntryRequest( 725 DataSource lfhSection, 726 LocalFileRecord localFileRecord, 727 ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest) 728 throws IOException, ApkFormatException { 729 try { 730 localFileRecord.outputUncompressedData(lfhSection, inspectEntryRequest.getDataSink()); 731 } catch (ZipFormatException e) { 732 throw new ApkFormatException("Malformed ZIP entry: " + localFileRecord.getName(), e); 733 } 734 inspectEntryRequest.done(); 735 } 736 737 private static class OutputSizeAndDataOffset { 738 public long outputBytes; 739 public long dataOffsetBytes; 740 OutputSizeAndDataOffset(long outputBytes, long dataOffsetBytes)741 public OutputSizeAndDataOffset(long outputBytes, long dataOffsetBytes) { 742 this.outputBytes = outputBytes; 743 this.dataOffsetBytes = dataOffsetBytes; 744 } 745 } 746 outputInputJarEntryLfhRecordPreservingDataAlignment( DataSource inputLfhSection, LocalFileRecord inputRecord, DataSink outputLfhSection, long outputOffset)747 private static OutputSizeAndDataOffset outputInputJarEntryLfhRecordPreservingDataAlignment( 748 DataSource inputLfhSection, 749 LocalFileRecord inputRecord, 750 DataSink outputLfhSection, 751 long outputOffset) 752 throws IOException { 753 long inputOffset = inputRecord.getStartOffsetInArchive(); 754 if (inputOffset == outputOffset) { 755 // This record's data will be aligned same as in the input APK. 756 return new OutputSizeAndDataOffset( 757 inputRecord.outputRecord(inputLfhSection, outputLfhSection), 758 inputRecord.getDataStartOffsetInRecord()); 759 } 760 int dataAlignmentMultiple = getInputJarEntryDataAlignmentMultiple(inputRecord); 761 if ((dataAlignmentMultiple <= 1) 762 || ((inputOffset % dataAlignmentMultiple) 763 == (outputOffset % dataAlignmentMultiple))) { 764 // This record's data will be aligned same as in the input APK. 765 return new OutputSizeAndDataOffset( 766 inputRecord.outputRecord(inputLfhSection, outputLfhSection), 767 inputRecord.getDataStartOffsetInRecord()); 768 } 769 770 long inputDataStartOffset = inputOffset + inputRecord.getDataStartOffsetInRecord(); 771 if ((inputDataStartOffset % dataAlignmentMultiple) != 0) { 772 // This record's data is not aligned in the input APK. No need to align it in the 773 // output. 774 return new OutputSizeAndDataOffset( 775 inputRecord.outputRecord(inputLfhSection, outputLfhSection), 776 inputRecord.getDataStartOffsetInRecord()); 777 } 778 779 // This record's data needs to be re-aligned in the output. This is achieved using the 780 // record's extra field. 781 ByteBuffer aligningExtra = 782 createExtraFieldToAlignData( 783 inputRecord.getExtra(), 784 outputOffset + inputRecord.getExtraFieldStartOffsetInsideRecord(), 785 dataAlignmentMultiple); 786 long dataOffset = 787 (long) inputRecord.getDataStartOffsetInRecord() 788 + aligningExtra.remaining() 789 - inputRecord.getExtra().remaining(); 790 return new OutputSizeAndDataOffset( 791 inputRecord.outputRecordWithModifiedExtra( 792 inputLfhSection, aligningExtra, outputLfhSection), 793 dataOffset); 794 } 795 getInputJarEntryDataAlignmentMultiple(LocalFileRecord entry)796 private static int getInputJarEntryDataAlignmentMultiple(LocalFileRecord entry) { 797 if (entry.isDataCompressed()) { 798 // Compressed entries don't need to be aligned 799 return 1; 800 } 801 802 // Attempt to obtain the alignment multiple from the entry's extra field. 803 ByteBuffer extra = entry.getExtra(); 804 if (extra.hasRemaining()) { 805 extra.order(ByteOrder.LITTLE_ENDIAN); 806 // FORMAT: sequence of fields. Each field consists of: 807 // * uint16 ID 808 // * uint16 size 809 // * 'size' bytes: payload 810 while (extra.remaining() >= 4) { 811 short headerId = extra.getShort(); 812 int dataSize = ZipUtils.getUnsignedInt16(extra); 813 if (dataSize > extra.remaining()) { 814 // Malformed field -- insufficient input remaining 815 break; 816 } 817 if (headerId != ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID) { 818 // Skip this field 819 extra.position(extra.position() + dataSize); 820 continue; 821 } 822 // This is APK alignment field. 823 // FORMAT: 824 // * uint16 alignment multiple (in bytes) 825 // * remaining bytes -- padding to achieve alignment of data which starts after 826 // the extra field 827 if (dataSize < 2) { 828 // Malformed 829 break; 830 } 831 return ZipUtils.getUnsignedInt16(extra); 832 } 833 } 834 835 // Fall back to filename-based defaults 836 return (entry.getName().endsWith(".so")) ? ANDROID_COMMON_PAGE_ALIGNMENT_BYTES : 4; 837 } 838 createExtraFieldToAlignData( ByteBuffer original, long extraStartOffset, int dataAlignmentMultiple)839 private static ByteBuffer createExtraFieldToAlignData( 840 ByteBuffer original, long extraStartOffset, int dataAlignmentMultiple) { 841 if (dataAlignmentMultiple <= 1) { 842 return original; 843 } 844 845 // In the worst case scenario, we'll increase the output size by 6 + dataAlignment - 1. 846 ByteBuffer result = ByteBuffer.allocate(original.remaining() + 5 + dataAlignmentMultiple); 847 result.order(ByteOrder.LITTLE_ENDIAN); 848 849 // Step 1. Output all extra fields other than the one which is to do with alignment 850 // FORMAT: sequence of fields. Each field consists of: 851 // * uint16 ID 852 // * uint16 size 853 // * 'size' bytes: payload 854 while (original.remaining() >= 4) { 855 short headerId = original.getShort(); 856 int dataSize = ZipUtils.getUnsignedInt16(original); 857 if (dataSize > original.remaining()) { 858 // Malformed field -- insufficient input remaining 859 break; 860 } 861 if (((headerId == 0) && (dataSize == 0)) 862 || (headerId == ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID)) { 863 // Ignore the field if it has to do with the old APK data alignment method (filling 864 // the extra field with 0x00 bytes) or the new APK data alignment method. 865 original.position(original.position() + dataSize); 866 continue; 867 } 868 // Copy this field (including header) to the output 869 original.position(original.position() - 4); 870 int originalLimit = original.limit(); 871 original.limit(original.position() + 4 + dataSize); 872 result.put(original); 873 original.limit(originalLimit); 874 } 875 876 // Step 2. Add alignment field 877 // FORMAT: 878 // * uint16 extra header ID 879 // * uint16 extra data size 880 // Payload ('data size' bytes) 881 // * uint16 alignment multiple (in bytes) 882 // * remaining bytes -- padding to achieve alignment of data which starts after the 883 // extra field 884 long dataMinStartOffset = 885 extraStartOffset 886 + result.position() 887 + ALIGNMENT_ZIP_EXTRA_DATA_FIELD_MIN_SIZE_BYTES; 888 int paddingSizeBytes = 889 (dataAlignmentMultiple - ((int) (dataMinStartOffset % dataAlignmentMultiple))) 890 % dataAlignmentMultiple; 891 result.putShort(ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID); 892 ZipUtils.putUnsignedInt16(result, 2 + paddingSizeBytes); 893 ZipUtils.putUnsignedInt16(result, dataAlignmentMultiple); 894 result.position(result.position() + paddingSizeBytes); 895 result.flip(); 896 897 return result; 898 } 899 getZipCentralDirectory( DataSource apk, ApkUtils.ZipSections apkSections)900 private static ByteBuffer getZipCentralDirectory( 901 DataSource apk, ApkUtils.ZipSections apkSections) 902 throws IOException, ApkFormatException { 903 long cdSizeBytes = apkSections.getZipCentralDirectorySizeBytes(); 904 if (cdSizeBytes > Integer.MAX_VALUE) { 905 throw new ApkFormatException("ZIP Central Directory too large: " + cdSizeBytes); 906 } 907 long cdOffset = apkSections.getZipCentralDirectoryOffset(); 908 ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes); 909 cd.order(ByteOrder.LITTLE_ENDIAN); 910 return cd; 911 } 912 parseZipCentralDirectory( ByteBuffer cd, ApkUtils.ZipSections apkSections)913 private static List<CentralDirectoryRecord> parseZipCentralDirectory( 914 ByteBuffer cd, ApkUtils.ZipSections apkSections) throws ApkFormatException { 915 long cdOffset = apkSections.getZipCentralDirectoryOffset(); 916 int expectedCdRecordCount = apkSections.getZipCentralDirectoryRecordCount(); 917 List<CentralDirectoryRecord> cdRecords = new ArrayList<>(expectedCdRecordCount); 918 Set<String> entryNames = new HashSet<>(expectedCdRecordCount); 919 for (int i = 0; i < expectedCdRecordCount; i++) { 920 CentralDirectoryRecord cdRecord; 921 int offsetInsideCd = cd.position(); 922 try { 923 cdRecord = CentralDirectoryRecord.getRecord(cd); 924 } catch (ZipFormatException e) { 925 throw new ApkFormatException( 926 "Malformed ZIP Central Directory record #" 927 + (i + 1) 928 + " at file offset " 929 + (cdOffset + offsetInsideCd), 930 e); 931 } 932 String entryName = cdRecord.getName(); 933 if (!entryNames.add(entryName)) { 934 throw new ApkFormatException( 935 "Multiple ZIP entries with the same name: " + entryName); 936 } 937 cdRecords.add(cdRecord); 938 } 939 if (cd.hasRemaining()) { 940 throw new ApkFormatException( 941 "Unused space at the end of ZIP Central Directory: " 942 + cd.remaining() 943 + " bytes starting at file offset " 944 + (cdOffset + cd.position())); 945 } 946 947 return cdRecords; 948 } 949 findCdRecord( List<CentralDirectoryRecord> cdRecords, String name)950 private static CentralDirectoryRecord findCdRecord( 951 List<CentralDirectoryRecord> cdRecords, String name) { 952 for (CentralDirectoryRecord cdRecord : cdRecords) { 953 if (name.equals(cdRecord.getName())) { 954 return cdRecord; 955 } 956 } 957 return null; 958 } 959 960 /** 961 * Returns the contents of the APK's {@code AndroidManifest.xml} or {@code null} if this entry 962 * is not present in the APK. 963 */ getAndroidManifestFromApk( List<CentralDirectoryRecord> cdRecords, DataSource lhfSection)964 static ByteBuffer getAndroidManifestFromApk( 965 List<CentralDirectoryRecord> cdRecords, DataSource lhfSection) 966 throws IOException, ApkFormatException, ZipFormatException { 967 CentralDirectoryRecord androidManifestCdRecord = 968 findCdRecord(cdRecords, ANDROID_MANIFEST_ZIP_ENTRY_NAME); 969 if (androidManifestCdRecord == null) { 970 throw new ApkFormatException("Missing " + ANDROID_MANIFEST_ZIP_ENTRY_NAME); 971 } 972 973 return ByteBuffer.wrap( 974 LocalFileRecord.getUncompressedData( 975 lhfSection, androidManifestCdRecord, lhfSection.size())); 976 } 977 978 /** 979 * Return list of pin patterns embedded in the pin pattern asset file. If no such file, return 980 * {@code null}. 981 */ extractPinPatterns( List<CentralDirectoryRecord> cdRecords, DataSource lhfSection)982 private static List<Hints.PatternWithRange> extractPinPatterns( 983 List<CentralDirectoryRecord> cdRecords, DataSource lhfSection) 984 throws IOException, ApkFormatException { 985 CentralDirectoryRecord pinListCdRecord = 986 findCdRecord(cdRecords, Hints.PIN_HINT_ASSET_ZIP_ENTRY_NAME); 987 List<Hints.PatternWithRange> pinPatterns = null; 988 if (pinListCdRecord != null) { 989 pinPatterns = new ArrayList<>(); 990 byte[] patternBlob; 991 try { 992 patternBlob = 993 LocalFileRecord.getUncompressedData( 994 lhfSection, pinListCdRecord, lhfSection.size()); 995 } catch (ZipFormatException ex) { 996 throw new ApkFormatException("Bad " + pinListCdRecord); 997 } 998 pinPatterns = Hints.parsePinPatterns(patternBlob); 999 } 1000 return pinPatterns; 1001 } 1002 1003 /** 1004 * Returns the minimum Android version (API Level) supported by the provided APK. This is based 1005 * on the {@code android:minSdkVersion} attributes of the APK's {@code AndroidManifest.xml}. 1006 */ getMinSdkVersionFromApk( List<CentralDirectoryRecord> cdRecords, DataSource lhfSection)1007 private static int getMinSdkVersionFromApk( 1008 List<CentralDirectoryRecord> cdRecords, DataSource lhfSection) 1009 throws IOException, MinSdkVersionException { 1010 ByteBuffer androidManifest; 1011 try { 1012 androidManifest = getAndroidManifestFromApk(cdRecords, lhfSection); 1013 } catch (ZipFormatException | ApkFormatException e) { 1014 throw new MinSdkVersionException( 1015 "Failed to determine APK's minimum supported Android platform version", e); 1016 } 1017 return ApkUtils.getMinSdkVersionFromBinaryAndroidManifest(androidManifest); 1018 } 1019 1020 /** 1021 * Configuration of a signer. 1022 * 1023 * <p>Use {@link Builder} to obtain configuration instances. 1024 */ 1025 public static class SignerConfig { 1026 private final String mName; 1027 private final PrivateKey mPrivateKey; 1028 private final List<X509Certificate> mCertificates; 1029 private final boolean mDeterministicDsaSigning; 1030 private final int mMinSdkVersion; 1031 private final SigningCertificateLineage mSigningCertificateLineage; 1032 SignerConfig(Builder builder)1033 private SignerConfig(Builder builder) { 1034 mName = builder.mName; 1035 mPrivateKey = builder.mPrivateKey; 1036 mCertificates = Collections.unmodifiableList(new ArrayList<>(builder.mCertificates)); 1037 mDeterministicDsaSigning = builder.mDeterministicDsaSigning; 1038 mMinSdkVersion = builder.mMinSdkVersion; 1039 mSigningCertificateLineage = builder.mSigningCertificateLineage; 1040 } 1041 1042 /** Returns the name of this signer. */ getName()1043 public String getName() { 1044 return mName; 1045 } 1046 1047 /** Returns the signing key of this signer. */ getPrivateKey()1048 public PrivateKey getPrivateKey() { 1049 return mPrivateKey; 1050 } 1051 1052 /** 1053 * Returns the certificate(s) of this signer. The first certificate's public key corresponds 1054 * to this signer's private key. 1055 */ getCertificates()1056 public List<X509Certificate> getCertificates() { 1057 return mCertificates; 1058 } 1059 1060 /** 1061 * If this signer is a DSA signer, whether or not the signing is done deterministically. 1062 */ getDeterministicDsaSigning()1063 public boolean getDeterministicDsaSigning() { 1064 return mDeterministicDsaSigning; 1065 } 1066 1067 /** Returns the minimum SDK version for which this signer should be used. */ getMinSdkVersion()1068 public int getMinSdkVersion() { 1069 return mMinSdkVersion; 1070 } 1071 1072 /** Returns the {@link SigningCertificateLineage} for this signer. */ getSigningCertificateLineage()1073 public SigningCertificateLineage getSigningCertificateLineage() { 1074 return mSigningCertificateLineage; 1075 } 1076 1077 /** Builder of {@link SignerConfig} instances. */ 1078 public static class Builder { 1079 private final String mName; 1080 private final PrivateKey mPrivateKey; 1081 private final List<X509Certificate> mCertificates; 1082 private final boolean mDeterministicDsaSigning; 1083 1084 private int mMinSdkVersion; 1085 private SigningCertificateLineage mSigningCertificateLineage; 1086 1087 /** 1088 * Constructs a new {@code Builder}. 1089 * 1090 * @param name signer's name. The name is reflected in the name of files comprising the 1091 * JAR signature of the APK. 1092 * @param privateKey signing key 1093 * @param certificates list of one or more X.509 certificates. The subject public key of 1094 * the first certificate must correspond to the {@code privateKey}. 1095 */ Builder( String name, PrivateKey privateKey, List<X509Certificate> certificates)1096 public Builder( 1097 String name, 1098 PrivateKey privateKey, 1099 List<X509Certificate> certificates) { 1100 this(name, privateKey, certificates, false); 1101 } 1102 1103 /** 1104 * Constructs a new {@code Builder}. 1105 * 1106 * @param name signer's name. The name is reflected in the name of files comprising the 1107 * JAR signature of the APK. 1108 * @param privateKey signing key 1109 * @param certificates list of one or more X.509 certificates. The subject public key of 1110 * the first certificate must correspond to the {@code privateKey}. 1111 * @param deterministicDsaSigning When signing using DSA, whether or not the 1112 * deterministic variant (RFC6979) should be used. 1113 */ Builder( String name, PrivateKey privateKey, List<X509Certificate> certificates, boolean deterministicDsaSigning)1114 public Builder( 1115 String name, 1116 PrivateKey privateKey, 1117 List<X509Certificate> certificates, 1118 boolean deterministicDsaSigning) { 1119 if (name.isEmpty()) { 1120 throw new IllegalArgumentException("Empty name"); 1121 } 1122 mName = name; 1123 mPrivateKey = privateKey; 1124 mCertificates = new ArrayList<>(certificates); 1125 mDeterministicDsaSigning = deterministicDsaSigning; 1126 } 1127 1128 /** @see #setLineageForMinSdkVersion(SigningCertificateLineage, int) */ setMinSdkVersion(int minSdkVersion)1129 public Builder setMinSdkVersion(int minSdkVersion) { 1130 return setLineageForMinSdkVersion(null, minSdkVersion); 1131 } 1132 1133 /** 1134 * Sets the specified {@code minSdkVersion} as the minimum Android platform version 1135 * (API level) for which the provided {@code lineage} (where applicable) should be used 1136 * to produce the APK's signature. This method is useful if callers want to specify a 1137 * particular rotated signer or lineage with restricted capabilities for later 1138 * platform releases. 1139 * 1140 * <p><em>Note:</em>>The V1 and V2 signature schemes do not support key rotation and 1141 * signing lineages with capabilities; only an app's original signer(s) can be used for 1142 * the V1 and V2 signature blocks. Because of this, only a value of {@code 1143 * minSdkVersion} >= 28 (Android P) where support for the V3 signature scheme was 1144 * introduced can be specified. 1145 * 1146 * <p><em>Note:</em>Due to limitations with platform targeting in the V3.0 signature 1147 * scheme, specifying a {@code minSdkVersion} value <= 32 (Android Sv2) will result in 1148 * the current {@code SignerConfig} being used in the V3.0 signing block and applied to 1149 * Android P through at least Sv2 (and later depending on the {@code minSdkVersion} for 1150 * subsequent {@code SignerConfig} instances). Because of this, only a single {@code 1151 * SignerConfig} can be instantiated with a minimum SDK version <= 32. 1152 * 1153 * @param lineage the {@code SigningCertificateLineage} to target the specified {@code 1154 * minSdkVersion} 1155 * @param minSdkVersion the minimum SDK version for which this {@code SignerConfig} 1156 * should be used 1157 * @return this {@code Builder} instance 1158 * 1159 * @throws IllegalArgumentException if the provided {@code minSdkVersion} < 28 or the 1160 * certificate provided in the constructor is not in the specified {@code lineage}. 1161 */ setLineageForMinSdkVersion(SigningCertificateLineage lineage, int minSdkVersion)1162 public Builder setLineageForMinSdkVersion(SigningCertificateLineage lineage, 1163 int minSdkVersion) { 1164 if (minSdkVersion < AndroidSdkVersion.P) { 1165 throw new IllegalArgumentException( 1166 "SDK targeted signing config is only supported with the V3 signature " 1167 + "scheme on Android P (SDK version " 1168 + AndroidSdkVersion.P + ") and later"); 1169 } 1170 if (minSdkVersion < MIN_SDK_WITH_V31_SUPPORT) { 1171 minSdkVersion = AndroidSdkVersion.P; 1172 } 1173 mMinSdkVersion = minSdkVersion; 1174 // If a lineage is provided, ensure the signing certificate for this signer is in 1175 // the lineage; in the case of multiple signing certificates, the first is always 1176 // used in the lineage. 1177 if (lineage != null && !lineage.isCertificateInLineage(mCertificates.get(0))) { 1178 throw new IllegalArgumentException( 1179 "The provided lineage does not contain the signing certificate, " 1180 + mCertificates.get(0).getSubjectDN() 1181 + ", for this SignerConfig"); 1182 } 1183 mSigningCertificateLineage = lineage; 1184 return this; 1185 } 1186 1187 /** 1188 * Returns a new {@code SignerConfig} instance configured based on the configuration of 1189 * this builder. 1190 */ build()1191 public SignerConfig build() { 1192 return new SignerConfig(this); 1193 } 1194 } 1195 } 1196 1197 /** 1198 * Builder of {@link ApkSigner} instances. 1199 * 1200 * <p>The builder requires the following information to construct a working {@code ApkSigner}: 1201 * 1202 * <ul> 1203 * <li>Signer configs or {@link ApkSignerEngine} -- provided in the constructor, 1204 * <li>APK to be signed -- see {@link #setInputApk(File) setInputApk} variants, 1205 * <li>where to store the output signed APK -- see {@link #setOutputApk(File) setOutputApk} 1206 * variants. 1207 * </ul> 1208 */ 1209 public static class Builder { 1210 private final List<SignerConfig> mSignerConfigs; 1211 private SignerConfig mSourceStampSignerConfig; 1212 private SigningCertificateLineage mSourceStampSigningCertificateLineage; 1213 private boolean mForceSourceStampOverwrite = false; 1214 private boolean mSourceStampTimestampEnabled = true; 1215 private boolean mV1SigningEnabled = true; 1216 private boolean mV2SigningEnabled = true; 1217 private boolean mV3SigningEnabled = true; 1218 private boolean mV4SigningEnabled = true; 1219 private boolean mAlignFileSize = false; 1220 private boolean mVerityEnabled = false; 1221 private boolean mV4ErrorReportingEnabled = false; 1222 private boolean mDebuggableApkPermitted = true; 1223 private boolean mOtherSignersSignaturesPreserved; 1224 private String mCreatedBy; 1225 private Integer mMinSdkVersion; 1226 private int mRotationMinSdkVersion = V3SchemeConstants.DEFAULT_ROTATION_MIN_SDK_VERSION; 1227 private boolean mRotationTargetsDevRelease = false; 1228 1229 private final ApkSignerEngine mSignerEngine; 1230 1231 private File mInputApkFile; 1232 private DataSource mInputApkDataSource; 1233 1234 private File mOutputApkFile; 1235 private DataSink mOutputApkDataSink; 1236 private DataSource mOutputApkDataSource; 1237 1238 private File mOutputV4File; 1239 1240 private SigningCertificateLineage mSigningCertificateLineage; 1241 1242 // APK Signature Scheme v3 only supports a single signing certificate, so to move to v3 1243 // signing by default, but not require prior clients to update to explicitly disable v3 1244 // signing for multiple signers, we modify the mV3SigningEnabled depending on the provided 1245 // inputs (multiple signers and mSigningCertificateLineage in particular). Maintain two 1246 // extra variables to record whether or not mV3SigningEnabled has been set directly by a 1247 // client and so should override the default behavior. 1248 private boolean mV3SigningExplicitlyDisabled = false; 1249 private boolean mV3SigningExplicitlyEnabled = false; 1250 1251 /** 1252 * Constructs a new {@code Builder} for an {@code ApkSigner} which signs using the provided 1253 * signer configurations. The resulting signer may be further customized through this 1254 * builder's setters, such as {@link #setMinSdkVersion(int)}, {@link 1255 * #setV1SigningEnabled(boolean)}, {@link #setV2SigningEnabled(boolean)}, {@link 1256 * #setOtherSignersSignaturesPreserved(boolean)}, {@link #setCreatedBy(String)}. 1257 * 1258 * <p>{@link #Builder(ApkSignerEngine)} is an alternative for advanced use cases where more 1259 * control over low-level details of signing is desired. 1260 */ Builder(List<SignerConfig> signerConfigs)1261 public Builder(List<SignerConfig> signerConfigs) { 1262 if (signerConfigs.isEmpty()) { 1263 throw new IllegalArgumentException("At least one signer config must be provided"); 1264 } 1265 if (signerConfigs.size() > 1) { 1266 // APK Signature Scheme v3 only supports single signer, unless a 1267 // SigningCertificateLineage is provided, in which case this will be reset to true, 1268 // since we don't yet have a v4 scheme about which to worry 1269 mV3SigningEnabled = false; 1270 } 1271 mSignerConfigs = new ArrayList<>(signerConfigs); 1272 mSignerEngine = null; 1273 } 1274 1275 /** 1276 * Constructs a new {@code Builder} for an {@code ApkSigner} which signs using the provided 1277 * signing engine. This is meant for advanced use cases where more control is needed over 1278 * the lower-level details of signing. For typical use cases, {@link #Builder(List)} is more 1279 * appropriate. 1280 */ Builder(ApkSignerEngine signerEngine)1281 public Builder(ApkSignerEngine signerEngine) { 1282 if (signerEngine == null) { 1283 throw new NullPointerException("signerEngine == null"); 1284 } 1285 mSignerEngine = signerEngine; 1286 mSignerConfigs = null; 1287 } 1288 1289 /** Sets the signing configuration of the source stamp to be embedded in the APK. */ setSourceStampSignerConfig(SignerConfig sourceStampSignerConfig)1290 public Builder setSourceStampSignerConfig(SignerConfig sourceStampSignerConfig) { 1291 mSourceStampSignerConfig = sourceStampSignerConfig; 1292 return this; 1293 } 1294 1295 /** 1296 * Sets the source stamp {@link SigningCertificateLineage}. This structure provides proof of 1297 * signing certificate rotation for certificates previously used to sign source stamps. 1298 */ setSourceStampSigningCertificateLineage( SigningCertificateLineage sourceStampSigningCertificateLineage)1299 public Builder setSourceStampSigningCertificateLineage( 1300 SigningCertificateLineage sourceStampSigningCertificateLineage) { 1301 mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage; 1302 return this; 1303 } 1304 1305 /** 1306 * Sets whether the APK should overwrite existing source stamp, if found. 1307 * 1308 * @param force {@code true} to require the APK to be overwrite existing source stamp 1309 */ setForceSourceStampOverwrite(boolean force)1310 public Builder setForceSourceStampOverwrite(boolean force) { 1311 mForceSourceStampOverwrite = force; 1312 return this; 1313 } 1314 1315 /** 1316 * Sets whether the source stamp should contain the timestamp attribute with the time 1317 * at which the source stamp was signed. 1318 */ setSourceStampTimestampEnabled(boolean value)1319 public Builder setSourceStampTimestampEnabled(boolean value) { 1320 mSourceStampTimestampEnabled = value; 1321 return this; 1322 } 1323 1324 /** 1325 * Sets the APK to be signed. 1326 * 1327 * @see #setInputApk(DataSource) 1328 */ setInputApk(File inputApk)1329 public Builder setInputApk(File inputApk) { 1330 if (inputApk == null) { 1331 throw new NullPointerException("inputApk == null"); 1332 } 1333 mInputApkFile = inputApk; 1334 mInputApkDataSource = null; 1335 return this; 1336 } 1337 1338 /** 1339 * Sets the APK to be signed. 1340 * 1341 * @see #setInputApk(File) 1342 */ setInputApk(DataSource inputApk)1343 public Builder setInputApk(DataSource inputApk) { 1344 if (inputApk == null) { 1345 throw new NullPointerException("inputApk == null"); 1346 } 1347 mInputApkDataSource = inputApk; 1348 mInputApkFile = null; 1349 return this; 1350 } 1351 1352 /** 1353 * Sets the location of the output (signed) APK. {@code ApkSigner} will create this file if 1354 * it doesn't exist. 1355 * 1356 * @see #setOutputApk(ReadableDataSink) 1357 * @see #setOutputApk(DataSink, DataSource) 1358 */ setOutputApk(File outputApk)1359 public Builder setOutputApk(File outputApk) { 1360 if (outputApk == null) { 1361 throw new NullPointerException("outputApk == null"); 1362 } 1363 mOutputApkFile = outputApk; 1364 mOutputApkDataSink = null; 1365 mOutputApkDataSource = null; 1366 return this; 1367 } 1368 1369 /** 1370 * Sets the readable data sink which will receive the output (signed) APK. After signing, 1371 * the contents of the output APK will be available via the {@link DataSource} interface of 1372 * the sink. 1373 * 1374 * <p>This variant of {@code setOutputApk} is useful for avoiding writing the output APK to 1375 * a file. For example, an in-memory data sink, such as {@link 1376 * DataSinks#newInMemoryDataSink()}, could be used instead of a file. 1377 * 1378 * @see #setOutputApk(File) 1379 * @see #setOutputApk(DataSink, DataSource) 1380 */ setOutputApk(ReadableDataSink outputApk)1381 public Builder setOutputApk(ReadableDataSink outputApk) { 1382 if (outputApk == null) { 1383 throw new NullPointerException("outputApk == null"); 1384 } 1385 return setOutputApk(outputApk, outputApk); 1386 } 1387 1388 /** 1389 * Sets the sink which will receive the output (signed) APK. Data received by the {@code 1390 * outputApkOut} sink must be visible through the {@code outputApkIn} data source. 1391 * 1392 * <p>This is an advanced variant of {@link #setOutputApk(ReadableDataSink)}, enabling the 1393 * sink and the source to be different objects. 1394 * 1395 * @see #setOutputApk(ReadableDataSink) 1396 * @see #setOutputApk(File) 1397 */ setOutputApk(DataSink outputApkOut, DataSource outputApkIn)1398 public Builder setOutputApk(DataSink outputApkOut, DataSource outputApkIn) { 1399 if (outputApkOut == null) { 1400 throw new NullPointerException("outputApkOut == null"); 1401 } 1402 if (outputApkIn == null) { 1403 throw new NullPointerException("outputApkIn == null"); 1404 } 1405 mOutputApkFile = null; 1406 mOutputApkDataSink = outputApkOut; 1407 mOutputApkDataSource = outputApkIn; 1408 return this; 1409 } 1410 1411 /** 1412 * Sets the location of the V4 output file. {@code ApkSigner} will create this file if it 1413 * doesn't exist. 1414 */ setV4SignatureOutputFile(File v4SignatureOutputFile)1415 public Builder setV4SignatureOutputFile(File v4SignatureOutputFile) { 1416 if (v4SignatureOutputFile == null) { 1417 throw new NullPointerException("v4HashRootOutputFile == null"); 1418 } 1419 mOutputV4File = v4SignatureOutputFile; 1420 return this; 1421 } 1422 1423 /** 1424 * Sets the minimum Android platform version (API Level) on which APK signatures produced by 1425 * the signer being built must verify. This method is useful for overriding the default 1426 * behavior where the minimum API Level is obtained from the {@code android:minSdkVersion} 1427 * attribute of the APK's {@code AndroidManifest.xml}. 1428 * 1429 * <p><em>Note:</em> This method may result in APK signatures which don't verify on some 1430 * Android platform versions supported by the APK. 1431 * 1432 * <p><em>Note:</em> This method may only be invoked when this builder is not initialized 1433 * with an {@link ApkSignerEngine}. 1434 * 1435 * @throws IllegalStateException if this builder was initialized with an {@link 1436 * ApkSignerEngine} 1437 */ setMinSdkVersion(int minSdkVersion)1438 public Builder setMinSdkVersion(int minSdkVersion) { 1439 checkInitializedWithoutEngine(); 1440 mMinSdkVersion = minSdkVersion; 1441 return this; 1442 } 1443 1444 /** 1445 * Sets the minimum Android platform version (API Level) for which an APK's rotated signing 1446 * key should be used to produce the APK's signature. The original signing key for the APK 1447 * will be used for all previous platform versions. If a rotated key with signing lineage is 1448 * not provided then this method is a noop. This method is useful for overriding the 1449 * default behavior where Android T is set as the minimum API level for rotation. 1450 * 1451 * <p><em>Note:</em>Specifying a {@code minSdkVersion} value <= 32 (Android Sv2) will result 1452 * in the original V3 signing block being used without platform targeting. 1453 * 1454 * <p><em>Note:</em> This method may only be invoked when this builder is not initialized 1455 * with an {@link ApkSignerEngine}. 1456 * 1457 * @throws IllegalStateException if this builder was initialized with an {@link 1458 * ApkSignerEngine} 1459 */ setMinSdkVersionForRotation(int minSdkVersion)1460 public Builder setMinSdkVersionForRotation(int minSdkVersion) { 1461 checkInitializedWithoutEngine(); 1462 // If the provided SDK version does not support v3.1, then use the default SDK version 1463 // with rotation support. 1464 if (minSdkVersion < MIN_SDK_WITH_V31_SUPPORT) { 1465 mRotationMinSdkVersion = MIN_SDK_WITH_V3_SUPPORT; 1466 } else { 1467 mRotationMinSdkVersion = minSdkVersion; 1468 } 1469 return this; 1470 } 1471 1472 /** 1473 * Sets whether the rotation-min-sdk-version is intended to target a development release; 1474 * this is primarily required after the T SDK is finalized, and an APK needs to target U 1475 * during its development cycle for rotation. 1476 * 1477 * <p>This is only required after the T SDK is finalized since S and earlier releases do 1478 * not know about the V3.1 block ID, but once T is released and work begins on U, U will 1479 * use the SDK version of T during development. Specifying a rotation-min-sdk-version of T's 1480 * SDK version along with setting {@code enabled} to true will allow an APK to use the 1481 * rotated key on a device running U while causing this to be bypassed for T. 1482 * 1483 * <p><em>Note:</em>If the rotation-min-sdk-version is less than or equal to 32 (Android 1484 * Sv2), then the rotated signing key will be used in the v3.0 signing block and this call 1485 * will be a noop. 1486 * 1487 * <p><em>Note:</em> This method may only be invoked when this builder is not initialized 1488 * with an {@link ApkSignerEngine}. 1489 */ setRotationTargetsDevRelease(boolean enabled)1490 public Builder setRotationTargetsDevRelease(boolean enabled) { 1491 checkInitializedWithoutEngine(); 1492 mRotationTargetsDevRelease = enabled; 1493 return this; 1494 } 1495 1496 /** 1497 * Sets whether the APK should be signed using JAR signing (aka v1 signature scheme). 1498 * 1499 * <p>By default, whether APK is signed using JAR signing is determined by {@code 1500 * ApkSigner}, based on the platform versions supported by the APK or specified using {@link 1501 * #setMinSdkVersion(int)}. Disabling JAR signing will result in APK signatures which don't 1502 * verify on Android Marshmallow (Android 6.0, API Level 23) and lower. 1503 * 1504 * <p><em>Note:</em> This method may only be invoked when this builder is not initialized 1505 * with an {@link ApkSignerEngine}. 1506 * 1507 * @param enabled {@code true} to require the APK to be signed using JAR signing, {@code 1508 * false} to require the APK to not be signed using JAR signing. 1509 * @throws IllegalStateException if this builder was initialized with an {@link 1510 * ApkSignerEngine} 1511 * @see <a 1512 * href="https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File">JAR 1513 * signing</a> 1514 */ setV1SigningEnabled(boolean enabled)1515 public Builder setV1SigningEnabled(boolean enabled) { 1516 checkInitializedWithoutEngine(); 1517 mV1SigningEnabled = enabled; 1518 return this; 1519 } 1520 1521 /** 1522 * Sets whether the APK should be signed using APK Signature Scheme v2 (aka v2 signature 1523 * scheme). 1524 * 1525 * <p>By default, whether APK is signed using APK Signature Scheme v2 is determined by 1526 * {@code ApkSigner} based on the platform versions supported by the APK or specified using 1527 * {@link #setMinSdkVersion(int)}. 1528 * 1529 * <p><em>Note:</em> This method may only be invoked when this builder is not initialized 1530 * with an {@link ApkSignerEngine}. 1531 * 1532 * @param enabled {@code true} to require the APK to be signed using APK Signature Scheme 1533 * v2, {@code false} to require the APK to not be signed using APK Signature Scheme v2. 1534 * @throws IllegalStateException if this builder was initialized with an {@link 1535 * ApkSignerEngine} 1536 * @see <a href="https://source.android.com/security/apksigning/v2.html">APK Signature 1537 * Scheme v2</a> 1538 */ setV2SigningEnabled(boolean enabled)1539 public Builder setV2SigningEnabled(boolean enabled) { 1540 checkInitializedWithoutEngine(); 1541 mV2SigningEnabled = enabled; 1542 return this; 1543 } 1544 1545 /** 1546 * Sets whether the APK should be signed using APK Signature Scheme v3 (aka v3 signature 1547 * scheme). 1548 * 1549 * <p>By default, whether APK is signed using APK Signature Scheme v3 is determined by 1550 * {@code ApkSigner} based on the platform versions supported by the APK or specified using 1551 * {@link #setMinSdkVersion(int)}. 1552 * 1553 * <p><em>Note:</em> This method may only be invoked when this builder is not initialized 1554 * with an {@link ApkSignerEngine}. 1555 * 1556 * <p><em>Note:</em> APK Signature Scheme v3 only supports a single signing certificate, but 1557 * may take multiple signers mapping to different targeted platform versions. 1558 * 1559 * @param enabled {@code true} to require the APK to be signed using APK Signature Scheme 1560 * v3, {@code false} to require the APK to not be signed using APK Signature Scheme v3. 1561 * @throws IllegalStateException if this builder was initialized with an {@link 1562 * ApkSignerEngine} 1563 */ setV3SigningEnabled(boolean enabled)1564 public Builder setV3SigningEnabled(boolean enabled) { 1565 checkInitializedWithoutEngine(); 1566 mV3SigningEnabled = enabled; 1567 if (enabled) { 1568 mV3SigningExplicitlyEnabled = true; 1569 } else { 1570 mV3SigningExplicitlyDisabled = true; 1571 } 1572 return this; 1573 } 1574 1575 /** 1576 * Sets whether the APK should be signed using APK Signature Scheme v4. 1577 * 1578 * <p>V4 signing requires that the APK be v2 or v3 signed. 1579 * 1580 * @param enabled {@code true} to require the APK to be signed using APK Signature Scheme v2 1581 * or v3 and generate an v4 signature file 1582 */ setV4SigningEnabled(boolean enabled)1583 public Builder setV4SigningEnabled(boolean enabled) { 1584 checkInitializedWithoutEngine(); 1585 mV4SigningEnabled = enabled; 1586 mV4ErrorReportingEnabled = enabled; 1587 return this; 1588 } 1589 1590 /** 1591 * Sets whether errors during v4 signing should be reported and halt the signing process. 1592 * 1593 * <p>Error reporting for v4 signing is disabled by default, but will be enabled if the 1594 * caller invokes {@link #setV4SigningEnabled} with a value of true. This method is useful 1595 * for tools that enable v4 signing by default but don't want to fail the signing process if 1596 * the user did not explicitly request the v4 signing. 1597 * 1598 * @param enabled {@code false} to prevent errors encountered during the V4 signing from 1599 * halting the signing process 1600 */ setV4ErrorReportingEnabled(boolean enabled)1601 public Builder setV4ErrorReportingEnabled(boolean enabled) { 1602 checkInitializedWithoutEngine(); 1603 mV4ErrorReportingEnabled = enabled; 1604 return this; 1605 } 1606 1607 /** 1608 * Sets whether the output APK files should be sized as multiples of 4K. 1609 * 1610 * <p><em>Note:</em> This method may only be invoked when this builder is not initialized 1611 * with an {@link ApkSignerEngine}. 1612 * 1613 * @throws IllegalStateException if this builder was initialized with an {@link 1614 * ApkSignerEngine} 1615 */ setAlignFileSize(boolean alignFileSize)1616 public Builder setAlignFileSize(boolean alignFileSize) { 1617 checkInitializedWithoutEngine(); 1618 mAlignFileSize = alignFileSize; 1619 return this; 1620 } 1621 1622 /** 1623 * Sets whether to enable the verity signature algorithm for the v2 and v3 signature 1624 * schemes. 1625 * 1626 * @param enabled {@code true} to enable the verity signature algorithm for inclusion in the 1627 * v2 and v3 signature blocks. 1628 */ setVerityEnabled(boolean enabled)1629 public Builder setVerityEnabled(boolean enabled) { 1630 checkInitializedWithoutEngine(); 1631 mVerityEnabled = enabled; 1632 return this; 1633 } 1634 1635 /** 1636 * Sets whether the APK should be signed even if it is marked as debuggable ({@code 1637 * android:debuggable="true"} in its {@code AndroidManifest.xml}). For backward 1638 * compatibility reasons, the default value of this setting is {@code true}. 1639 * 1640 * <p>It is dangerous to sign debuggable APKs with production/release keys because Android 1641 * platform loosens security checks for such APKs. For example, arbitrary unauthorized code 1642 * may be executed in the context of such an app by anybody with ADB shell access. 1643 * 1644 * <p><em>Note:</em> This method may only be invoked when this builder is not initialized 1645 * with an {@link ApkSignerEngine}. 1646 */ setDebuggableApkPermitted(boolean permitted)1647 public Builder setDebuggableApkPermitted(boolean permitted) { 1648 checkInitializedWithoutEngine(); 1649 mDebuggableApkPermitted = permitted; 1650 return this; 1651 } 1652 1653 /** 1654 * Sets whether signatures produced by signers other than the ones configured in this engine 1655 * should be copied from the input APK to the output APK. 1656 * 1657 * <p>By default, signatures of other signers are omitted from the output APK. 1658 * 1659 * <p><em>Note:</em> This method may only be invoked when this builder is not initialized 1660 * with an {@link ApkSignerEngine}. 1661 * 1662 * @throws IllegalStateException if this builder was initialized with an {@link 1663 * ApkSignerEngine} 1664 */ setOtherSignersSignaturesPreserved(boolean preserved)1665 public Builder setOtherSignersSignaturesPreserved(boolean preserved) { 1666 checkInitializedWithoutEngine(); 1667 mOtherSignersSignaturesPreserved = preserved; 1668 return this; 1669 } 1670 1671 /** 1672 * Sets the value of the {@code Created-By} field in JAR signature files. 1673 * 1674 * <p><em>Note:</em> This method may only be invoked when this builder is not initialized 1675 * with an {@link ApkSignerEngine}. 1676 * 1677 * @throws IllegalStateException if this builder was initialized with an {@link 1678 * ApkSignerEngine} 1679 */ setCreatedBy(String createdBy)1680 public Builder setCreatedBy(String createdBy) { 1681 checkInitializedWithoutEngine(); 1682 if (createdBy == null) { 1683 throw new NullPointerException(); 1684 } 1685 mCreatedBy = createdBy; 1686 return this; 1687 } 1688 checkInitializedWithoutEngine()1689 private void checkInitializedWithoutEngine() { 1690 if (mSignerEngine != null) { 1691 throw new IllegalStateException( 1692 "Operation is not available when builder initialized with an engine"); 1693 } 1694 } 1695 1696 /** 1697 * Sets the {@link SigningCertificateLineage} to use with the v3 signature scheme. This 1698 * structure provides proof of signing certificate rotation linking {@link SignerConfig} 1699 * objects to previous ones. 1700 */ setSigningCertificateLineage( SigningCertificateLineage signingCertificateLineage)1701 public Builder setSigningCertificateLineage( 1702 SigningCertificateLineage signingCertificateLineage) { 1703 if (signingCertificateLineage != null) { 1704 mV3SigningEnabled = true; 1705 mSigningCertificateLineage = signingCertificateLineage; 1706 } 1707 return this; 1708 } 1709 1710 /** 1711 * Returns a new {@code ApkSigner} instance initialized according to the configuration of 1712 * this builder. 1713 */ build()1714 public ApkSigner build() { 1715 if (mV3SigningExplicitlyDisabled && mV3SigningExplicitlyEnabled) { 1716 throw new IllegalStateException( 1717 "Builder configured to both enable and disable APK " 1718 + "Signature Scheme v3 signing"); 1719 } 1720 1721 if (mV3SigningExplicitlyDisabled) { 1722 mV3SigningEnabled = false; 1723 } 1724 1725 if (mV3SigningExplicitlyEnabled) { 1726 mV3SigningEnabled = true; 1727 } 1728 1729 // If V4 signing is not explicitly set, and V2/V3 signing is disabled, then V4 signing 1730 // must be disabled as well as it is dependent on V2/V3. 1731 if (mV4SigningEnabled && !mV2SigningEnabled && !mV3SigningEnabled) { 1732 if (!mV4ErrorReportingEnabled) { 1733 mV4SigningEnabled = false; 1734 } else { 1735 throw new IllegalStateException( 1736 "APK Signature Scheme v4 signing requires at least " 1737 + "v2 or v3 signing to be enabled"); 1738 } 1739 } 1740 1741 // TODO - if v3 signing is enabled, check provided signers and history to see if valid 1742 1743 return new ApkSigner( 1744 mSignerConfigs, 1745 mSourceStampSignerConfig, 1746 mSourceStampSigningCertificateLineage, 1747 mForceSourceStampOverwrite, 1748 mSourceStampTimestampEnabled, 1749 mMinSdkVersion, 1750 mRotationMinSdkVersion, 1751 mRotationTargetsDevRelease, 1752 mV1SigningEnabled, 1753 mV2SigningEnabled, 1754 mV3SigningEnabled, 1755 mV4SigningEnabled, 1756 mAlignFileSize, 1757 mVerityEnabled, 1758 mV4ErrorReportingEnabled, 1759 mDebuggableApkPermitted, 1760 mOtherSignersSignaturesPreserved, 1761 mCreatedBy, 1762 mSignerEngine, 1763 mInputApkFile, 1764 mInputApkDataSource, 1765 mOutputApkFile, 1766 mOutputApkDataSink, 1767 mOutputApkDataSource, 1768 mOutputV4File, 1769 mSigningCertificateLineage); 1770 } 1771 } 1772 } 1773