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