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