1 /*
2  * Copyright (C) 2017 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 org.junit.Assert.assertTrue;
20 import static org.junit.Assert.fail;
21 
22 import com.android.apksig.ApkVerifier.Issue;
23 import com.android.apksig.apk.ApkFormatException;
24 import com.android.apksig.apk.ApkUtils;
25 import com.android.apksig.internal.apk.ApkSigningBlockUtils;
26 import com.android.apksig.internal.apk.SignatureInfo;
27 import com.android.apksig.internal.apk.v2.V2SchemeSigner;
28 import com.android.apksig.internal.apk.v3.V3SchemeSigner;
29 import com.android.apksig.internal.asn1.Asn1BerParser;
30 import com.android.apksig.internal.util.ByteBufferDataSource;
31 import com.android.apksig.internal.util.Resources;
32 import com.android.apksig.internal.x509.RSAPublicKey;
33 import com.android.apksig.internal.x509.SubjectPublicKeyInfo;
34 import com.android.apksig.util.DataSinks;
35 import com.android.apksig.util.DataSource;
36 import com.android.apksig.util.DataSources;
37 import com.android.apksig.util.ReadableDataSink;
38 import java.io.File;
39 import java.io.IOException;
40 import java.nio.ByteBuffer;
41 import java.nio.channels.ByteChannel;
42 import java.nio.file.Files;
43 import java.nio.file.StandardOpenOption;
44 import java.security.NoSuchAlgorithmException;
45 import java.security.PrivateKey;
46 import java.security.SignatureException;
47 import java.security.cert.X509Certificate;
48 import java.util.Arrays;
49 import java.util.Collections;
50 import java.util.List;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 import org.junit.runners.JUnit4;
54 
55 import java.math.BigInteger;
56 
57 @RunWith(JUnit4.class)
58 public class ApkSignerTest {
59 
60     /**
61      * Whether to preserve, as files, outputs of failed tests. This is useful for investigating test
62      * failures.
63      */
64     private static final boolean KEEP_FAILING_OUTPUT_AS_FILES = false;
65 
66     // All signers with the same prefix and an _X suffix were signed with the private key of the
67     // (X-1) signer.
68     private static final String FIRST_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048";
69     private static final String SECOND_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_2";
70     private static final String THIRD_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_3";
71 
72     // This is the same cert as above with the modulus reencoded to remove the leading 0 sign bit.
73     private static final String FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS =
74             "rsa-2048_negmod.x509.der";
75 
76     private static final String LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME =
77             "rsa-2048-lineage-2-signers";
78 
main(String[] params)79     public static void main(String[] params) throws Exception {
80         File outDir = (params.length > 0) ? new File(params[0]) : new File(".");
81         generateGoldenFiles(outDir);
82     }
83 
generateGoldenFiles(File outDir)84     private static void generateGoldenFiles(File outDir) throws Exception {
85         System.out.println(
86                 "Generating golden files " + ApkSignerTest.class.getSimpleName()
87                     + " into " + outDir);
88         if (!outDir.mkdirs()) {
89             throw new IOException("Failed to create directory: " + outDir);
90         }
91         List<ApkSigner.SignerConfig> rsa2048SignerConfig =
92                 Collections.singletonList(
93                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
94         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = Arrays.asList(
95                 rsa2048SignerConfig.get(0),
96                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
97         SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(
98                 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
99 
100         signGolden(
101                 "golden-unaligned-in.apk",
102                 new File(outDir, "golden-unaligned-out.apk"),
103                 new ApkSigner.Builder(rsa2048SignerConfig));
104         signGolden(
105                 "golden-legacy-aligned-in.apk",
106                 new File(outDir, "golden-legacy-aligned-out.apk"),
107                 new ApkSigner.Builder(rsa2048SignerConfig));
108         signGolden(
109                 "golden-aligned-in.apk",
110                 new File(outDir, "golden-aligned-out.apk"),
111                 new ApkSigner.Builder(rsa2048SignerConfig));
112 
113         signGolden(
114                 "golden-unaligned-in.apk",
115                 new File(outDir, "golden-unaligned-v1-out.apk"),
116                 new ApkSigner.Builder(rsa2048SignerConfig)
117                         .setV1SigningEnabled(true)
118                         .setV2SigningEnabled(false)
119                         .setV3SigningEnabled(false));
120         signGolden(
121                 "golden-legacy-aligned-in.apk",
122                 new File(outDir, "golden-legacy-aligned-v1-out.apk"),
123                 new ApkSigner.Builder(rsa2048SignerConfig)
124                         .setV1SigningEnabled(true)
125                         .setV2SigningEnabled(false)
126                         .setV3SigningEnabled(false));
127         signGolden(
128                 "golden-aligned-in.apk",
129                 new File(outDir, "golden-aligned-v1-out.apk"),
130                 new ApkSigner.Builder(rsa2048SignerConfig)
131                         .setV1SigningEnabled(true)
132                         .setV2SigningEnabled(false)
133                         .setV3SigningEnabled(false));
134 
135         signGolden(
136                 "golden-unaligned-in.apk",
137                 new File(outDir, "golden-unaligned-v2-out.apk"),
138                 new ApkSigner.Builder(rsa2048SignerConfig)
139                         .setV1SigningEnabled(false)
140                         .setV2SigningEnabled(true)
141                         .setV3SigningEnabled(false));
142         signGolden(
143                 "golden-legacy-aligned-in.apk",
144                 new File(outDir, "golden-legacy-aligned-v2-out.apk"),
145                 new ApkSigner.Builder(rsa2048SignerConfig)
146                         .setV1SigningEnabled(false)
147                         .setV2SigningEnabled(true)
148                         .setV3SigningEnabled(false));
149         signGolden(
150                 "golden-aligned-in.apk",
151                 new File(outDir, "golden-aligned-v2-out.apk"),
152                 new ApkSigner.Builder(rsa2048SignerConfig)
153                         .setV1SigningEnabled(false)
154                         .setV2SigningEnabled(true)
155                         .setV3SigningEnabled(false));
156 
157         signGolden(
158                 "golden-unaligned-in.apk",
159                 new File(outDir, "golden-unaligned-v3-out.apk"),
160                 new ApkSigner.Builder(rsa2048SignerConfig)
161                         .setV1SigningEnabled(false)
162                         .setV2SigningEnabled(false)
163                         .setV3SigningEnabled(true));
164         signGolden(
165                 "golden-legacy-aligned-in.apk",
166                 new File(outDir, "golden-legacy-aligned-v3-out.apk"),
167                 new ApkSigner.Builder(rsa2048SignerConfig)
168                         .setV1SigningEnabled(false)
169                         .setV2SigningEnabled(false)
170                         .setV3SigningEnabled(true));
171         signGolden(
172                 "golden-aligned-in.apk",
173                 new File(outDir, "golden-aligned-v3-out.apk"),
174                 new ApkSigner.Builder(rsa2048SignerConfig)
175                         .setV1SigningEnabled(false)
176                         .setV2SigningEnabled(false)
177                         .setV3SigningEnabled(true));
178 
179         signGolden(
180                 "golden-unaligned-in.apk",
181                 new File(outDir, "golden-unaligned-v3-lineage-out.apk"),
182                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
183                         .setV1SigningEnabled(false)
184                         .setV2SigningEnabled(false)
185                         .setV3SigningEnabled(true)
186                         .setSigningCertificateLineage(lineage));
187         signGolden(
188                 "golden-legacy-aligned-in.apk",
189                 new File(outDir, "golden-legacy-aligned-v3-lineage-out.apk"),
190                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
191                         .setV1SigningEnabled(false)
192                         .setV2SigningEnabled(false)
193                         .setV3SigningEnabled(true)
194                         .setSigningCertificateLineage(lineage));
195         signGolden(
196                 "golden-aligned-in.apk",
197                 new File(outDir, "golden-aligned-v3-lineage-out.apk"),
198                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
199                         .setV1SigningEnabled(false)
200                         .setV2SigningEnabled(false)
201                         .setV3SigningEnabled(true)
202                         .setSigningCertificateLineage(lineage));
203 
204         signGolden(
205                 "golden-unaligned-in.apk",
206                 new File(outDir, "golden-unaligned-v1v2-out.apk"),
207                 new ApkSigner.Builder(rsa2048SignerConfig)
208                         .setV1SigningEnabled(true)
209                         .setV2SigningEnabled(true)
210                         .setV3SigningEnabled(false));
211         signGolden(
212                 "golden-legacy-aligned-in.apk",
213                 new File(outDir, "golden-legacy-aligned-v1v2-out.apk"),
214                 new ApkSigner.Builder(rsa2048SignerConfig)
215                         .setV1SigningEnabled(true)
216                         .setV2SigningEnabled(true)
217                         .setV3SigningEnabled(false));
218         signGolden(
219                 "golden-aligned-in.apk",
220                 new File(outDir, "golden-aligned-v1v2-out.apk"),
221                 new ApkSigner.Builder(rsa2048SignerConfig)
222                         .setV1SigningEnabled(true)
223                         .setV2SigningEnabled(true)
224                         .setV3SigningEnabled(false));
225 
226         signGolden(
227                 "golden-unaligned-in.apk",
228                 new File(outDir, "golden-unaligned-v2v3-out.apk"),
229                 new ApkSigner.Builder(rsa2048SignerConfig)
230                         .setV1SigningEnabled(false)
231                         .setV2SigningEnabled(true)
232                         .setV3SigningEnabled(true));
233         signGolden(
234                 "golden-legacy-aligned-in.apk",
235                 new File(outDir, "golden-legacy-aligned-v2v3-out.apk"),
236                 new ApkSigner.Builder(rsa2048SignerConfig)
237                         .setV1SigningEnabled(false)
238                         .setV2SigningEnabled(true)
239                         .setV3SigningEnabled(true));
240         signGolden(
241                 "golden-aligned-in.apk",
242                 new File(outDir, "golden-aligned-v2v3-out.apk"),
243                 new ApkSigner.Builder(rsa2048SignerConfig)
244                         .setV1SigningEnabled(false)
245                         .setV2SigningEnabled(true)
246                         .setV3SigningEnabled(true));
247         signGolden(
248                 "golden-unaligned-in.apk",
249                 new File(outDir, "golden-unaligned-v2v3-lineage-out.apk"),
250                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
251                         .setV1SigningEnabled(false)
252                         .setV2SigningEnabled(true)
253                         .setV3SigningEnabled(true)
254                         .setSigningCertificateLineage(lineage));
255         signGolden(
256                 "golden-legacy-aligned-in.apk",
257                 new File(outDir, "golden-legacy-aligned-v2v3-lineage-out.apk"),
258                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
259                         .setV1SigningEnabled(false)
260                         .setV2SigningEnabled(true)
261                         .setV3SigningEnabled(true)
262                         .setSigningCertificateLineage(lineage));
263         signGolden(
264                 "golden-aligned-in.apk",
265                 new File(outDir, "golden-aligned-v2v3-lineage-out.apk"),
266                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
267                         .setV1SigningEnabled(false)
268                         .setV2SigningEnabled(true)
269                         .setV3SigningEnabled(true)
270                         .setSigningCertificateLineage(lineage));
271 
272         signGolden(
273                 "golden-unaligned-in.apk",
274                 new File(outDir, "golden-unaligned-v1v2v3-out.apk"),
275                 new ApkSigner.Builder(rsa2048SignerConfig)
276                         .setV1SigningEnabled(true)
277                         .setV2SigningEnabled(true)
278                         .setV3SigningEnabled(true));
279         signGolden(
280                 "golden-legacy-aligned-in.apk",
281                 new File(outDir, "golden-legacy-aligned-v1v2v3-out.apk"),
282                 new ApkSigner.Builder(rsa2048SignerConfig)
283                         .setV1SigningEnabled(true)
284                         .setV2SigningEnabled(true)
285                         .setV3SigningEnabled(true));
286         signGolden(
287                 "golden-aligned-in.apk",
288                 new File(outDir, "golden-aligned-v1v2v3-out.apk"),
289                 new ApkSigner.Builder(rsa2048SignerConfig)
290                         .setV1SigningEnabled(true)
291                         .setV2SigningEnabled(true)
292                         .setV3SigningEnabled(true));
293         signGolden(
294                 "golden-unaligned-in.apk",
295                 new File(outDir, "golden-unaligned-v1v2v3-lineage-out.apk"),
296                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
297                         .setV1SigningEnabled(true)
298                         .setV2SigningEnabled(true)
299                         .setV3SigningEnabled(true)
300                         .setSigningCertificateLineage(lineage));
301         signGolden(
302                 "golden-legacy-aligned-in.apk",
303                 new File(outDir, "golden-legacy-aligned-v1v2v3-lineage-out.apk"),
304                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
305                         .setV1SigningEnabled(true)
306                         .setV2SigningEnabled(true)
307                         .setV3SigningEnabled(true)
308                         .setSigningCertificateLineage(lineage));
309         signGolden(
310                 "golden-aligned-in.apk",
311                 new File(outDir, "golden-aligned-v1v2v3-lineage-out.apk"),
312                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
313                         .setV1SigningEnabled(true)
314                         .setV2SigningEnabled(true)
315                         .setV3SigningEnabled(true)
316                         .setSigningCertificateLineage(lineage));
317 
318         signGolden(
319                 "original.apk", new File(outDir, "golden-rsa-out.apk"),
320                 new ApkSigner.Builder(rsa2048SignerConfig));
321         signGolden(
322                 "original.apk", new File(outDir, "golden-rsa-minSdkVersion-1-out.apk"),
323                 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(1));
324         signGolden(
325                 "original.apk", new File(outDir, "golden-rsa-minSdkVersion-18-out.apk"),
326                 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(18));
327         signGolden(
328                 "original.apk", new File(outDir, "golden-rsa-minSdkVersion-24-out.apk"),
329                 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(24));
330     }
331 
signGolden( String inResourceName, File outFile, ApkSigner.Builder apkSignerBuilder)332     private static void signGolden(
333             String inResourceName, File outFile, ApkSigner.Builder apkSignerBuilder)
334                     throws Exception {
335         DataSource in =
336                 DataSources.asDataSource(
337                         ByteBuffer.wrap(Resources.toByteArray(ApkSigner.class, inResourceName)));
338         apkSignerBuilder
339                 .setInputApk(in)
340                 .setOutputApk(outFile)
341                 .build()
342                 .sign();
343     }
344 
345     @Test
testAlignmentPreserved_Golden()346     public void testAlignmentPreserved_Golden() throws Exception {
347         // Regression tests for preserving (mis)alignment of ZIP Local File Header data
348         // NOTE: Expected output files can be re-generated by running the "main" method.
349 
350         List<ApkSigner.SignerConfig> rsa2048SignerConfig =
351                 Collections.singletonList(
352                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
353         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = Arrays.asList(
354                 rsa2048SignerConfig.get(0),
355                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
356         SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(),
357                 LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
358         // Uncompressed entries in this input file are not aligned -- the file was created using
359         // the jar utility. temp4.txt entry was then manually added into the archive. This entry's
360         // ZIP Local File Header "extra" field declares that the entry's data must be aligned to
361         // 4 kB boundary, but the data isn't actually aligned in the file.
362         assertGolden(
363                 "golden-unaligned-in.apk", "golden-unaligned-out.apk",
364                 new ApkSigner.Builder(rsa2048SignerConfig));
365         assertGolden(
366                 "golden-unaligned-in.apk", "golden-unaligned-v1-out.apk",
367                 new ApkSigner.Builder(rsa2048SignerConfig)
368                         .setV1SigningEnabled(true)
369                         .setV2SigningEnabled(false)
370                         .setV3SigningEnabled(false));
371         assertGolden(
372                 "golden-unaligned-in.apk", "golden-unaligned-v2-out.apk",
373                 new ApkSigner.Builder(rsa2048SignerConfig)
374                         .setV1SigningEnabled(false)
375                         .setV2SigningEnabled(true)
376                         .setV3SigningEnabled(false));
377         assertGolden(
378                 "golden-unaligned-in.apk", "golden-unaligned-v3-out.apk",
379                 new ApkSigner.Builder(rsa2048SignerConfig)
380                         .setV1SigningEnabled(false)
381                         .setV2SigningEnabled(false)
382                         .setV3SigningEnabled(true));
383         assertGolden(
384                 "golden-unaligned-in.apk", "golden-unaligned-v3-lineage-out.apk",
385                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
386                         .setV1SigningEnabled(false)
387                         .setV2SigningEnabled(false)
388                         .setV3SigningEnabled(true)
389                         .setSigningCertificateLineage(lineage));
390         assertGolden(
391                 "golden-unaligned-in.apk", "golden-unaligned-v1v2-out.apk",
392                 new ApkSigner.Builder(rsa2048SignerConfig)
393                         .setV1SigningEnabled(true)
394                         .setV2SigningEnabled(true)
395                         .setV3SigningEnabled(false));
396         assertGolden(
397                 "golden-unaligned-in.apk", "golden-unaligned-v2v3-out.apk",
398                 new ApkSigner.Builder(rsa2048SignerConfig)
399                         .setV1SigningEnabled(false)
400                         .setV2SigningEnabled(true)
401                         .setV3SigningEnabled(true));
402         assertGolden(
403                 "golden-unaligned-in.apk", "golden-unaligned-v2v3-lineage-out.apk",
404                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
405                         .setV1SigningEnabled(false)
406                         .setV2SigningEnabled(true)
407                         .setV3SigningEnabled(true)
408                         .setSigningCertificateLineage(lineage));
409         assertGolden(
410                 "golden-unaligned-in.apk", "golden-unaligned-v1v2v3-out.apk",
411                 new ApkSigner.Builder(rsa2048SignerConfig)
412                         .setV1SigningEnabled(true)
413                         .setV2SigningEnabled(true)
414                         .setV3SigningEnabled(true));
415         assertGolden(
416                 "golden-unaligned-in.apk", "golden-unaligned-v1v2v3-lineage-out.apk",
417                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
418                         .setV1SigningEnabled(true)
419                         .setV2SigningEnabled(true)
420                         .setV3SigningEnabled(true)
421                         .setSigningCertificateLineage(lineage));
422 
423         // Uncompressed entries in this input file are aligned by zero-padding the "extra" field, as
424         // performed by zipalign at the time of writing. This padding technique produces ZIP
425         // archives whose "extra" field are not compliant with APPNOTE.TXT. Hence, this technique
426         // was deprecated.
427         assertGolden(
428                 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-out.apk",
429                 new ApkSigner.Builder(rsa2048SignerConfig));
430         assertGolden(
431                 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1-out.apk",
432                 new ApkSigner.Builder(rsa2048SignerConfig)
433                         .setV1SigningEnabled(true)
434                         .setV2SigningEnabled(false)
435                         .setV3SigningEnabled(false));
436         assertGolden(
437                 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2-out.apk",
438                 new ApkSigner.Builder(rsa2048SignerConfig)
439                         .setV1SigningEnabled(false)
440                         .setV2SigningEnabled(true)
441                         .setV3SigningEnabled(false));
442         assertGolden(
443                 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v3-out.apk",
444                 new ApkSigner.Builder(rsa2048SignerConfig)
445                         .setV1SigningEnabled(false)
446                         .setV2SigningEnabled(false)
447                         .setV3SigningEnabled(true));
448         assertGolden(
449                 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v3-lineage-out.apk",
450                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
451                         .setV1SigningEnabled(false)
452                         .setV2SigningEnabled(false)
453                         .setV3SigningEnabled(true)
454                         .setSigningCertificateLineage(lineage));
455         assertGolden(
456                 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2-out.apk",
457                 new ApkSigner.Builder(rsa2048SignerConfig)
458                         .setV1SigningEnabled(true)
459                         .setV2SigningEnabled(true)
460                         .setV3SigningEnabled(false));
461         assertGolden(
462                 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2v3-out.apk",
463                 new ApkSigner.Builder(rsa2048SignerConfig)
464                         .setV1SigningEnabled(false)
465                         .setV2SigningEnabled(true)
466                         .setV3SigningEnabled(true));
467         assertGolden(
468                 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2v3-lineage-out.apk",
469                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
470                         .setV1SigningEnabled(false)
471                         .setV2SigningEnabled(true)
472                         .setV3SigningEnabled(true)
473                         .setSigningCertificateLineage(lineage));
474         assertGolden(
475                 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2v3-out.apk",
476                 new ApkSigner.Builder(rsa2048SignerConfig)
477                         .setV1SigningEnabled(true)
478                         .setV2SigningEnabled(true)
479                         .setV3SigningEnabled(true));
480         assertGolden(
481                 "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2v3-lineage-out.apk",
482                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
483                         .setV1SigningEnabled(true)
484                         .setV2SigningEnabled(true)
485                         .setV3SigningEnabled(true)
486                         .setSigningCertificateLineage(lineage));
487 
488         // Uncompressed entries in this input file are aligned by padding the "extra" field, as
489         // generated by signapk and apksigner. This padding technique produces "extra" fields which
490         // are compliant with APPNOTE.TXT.
491         assertGolden(
492                 "golden-aligned-in.apk", "golden-aligned-out.apk",
493                 new ApkSigner.Builder(rsa2048SignerConfig));
494         assertGolden(
495                 "golden-aligned-in.apk", "golden-aligned-v1-out.apk",
496                 new ApkSigner.Builder(rsa2048SignerConfig)
497                         .setV1SigningEnabled(true)
498                         .setV2SigningEnabled(false)
499                         .setV3SigningEnabled(false));
500         assertGolden(
501                 "golden-aligned-in.apk", "golden-aligned-v2-out.apk",
502                 new ApkSigner.Builder(rsa2048SignerConfig)
503                         .setV1SigningEnabled(false)
504                         .setV2SigningEnabled(true)
505                         .setV3SigningEnabled(false));
506         assertGolden(
507                 "golden-aligned-in.apk", "golden-aligned-v3-out.apk",
508                 new ApkSigner.Builder(rsa2048SignerConfig)
509                         .setV1SigningEnabled(false)
510                         .setV2SigningEnabled(false)
511                         .setV3SigningEnabled(true));
512         assertGolden(
513                 "golden-aligned-in.apk", "golden-aligned-v3-lineage-out.apk",
514                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
515                         .setV1SigningEnabled(false)
516                         .setV2SigningEnabled(false)
517                         .setV3SigningEnabled(true)
518                         .setSigningCertificateLineage(lineage));
519         assertGolden(
520                 "golden-aligned-in.apk", "golden-aligned-v1v2-out.apk",
521                 new ApkSigner.Builder(rsa2048SignerConfig)
522                         .setV1SigningEnabled(true)
523                         .setV2SigningEnabled(true)
524                         .setV3SigningEnabled(false));
525         assertGolden(
526                 "golden-aligned-in.apk", "golden-aligned-v2v3-out.apk",
527                 new ApkSigner.Builder(rsa2048SignerConfig)
528                         .setV1SigningEnabled(false)
529                         .setV2SigningEnabled(true)
530                         .setV3SigningEnabled(true));
531         assertGolden(
532                 "golden-aligned-in.apk", "golden-aligned-v2v3-lineage-out.apk",
533                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
534                         .setV1SigningEnabled(false)
535                         .setV2SigningEnabled(true)
536                         .setV3SigningEnabled(true)
537                         .setSigningCertificateLineage(lineage));
538         assertGolden(
539                 "golden-aligned-in.apk", "golden-aligned-v1v2v3-out.apk",
540                 new ApkSigner.Builder(rsa2048SignerConfig)
541                         .setV1SigningEnabled(true)
542                         .setV2SigningEnabled(true)
543                         .setV3SigningEnabled(true));
544         assertGolden(
545                 "golden-aligned-in.apk", "golden-aligned-v1v2v3-lineage-out.apk",
546                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
547                         .setV1SigningEnabled(true)
548                         .setV2SigningEnabled(true)
549                         .setV3SigningEnabled(true)
550                         .setSigningCertificateLineage(lineage));
551     }
552 
553     @Test
testMinSdkVersion_Golden()554     public void testMinSdkVersion_Golden() throws Exception {
555         // Regression tests for minSdkVersion-based signature/digest algorithm selection
556         // NOTE: Expected output files can be re-generated by running the "main" method.
557 
558         List<ApkSigner.SignerConfig> rsaSignerConfig = Collections.singletonList(
559                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
560         assertGolden("original.apk", "golden-rsa-out.apk", new ApkSigner.Builder(rsaSignerConfig));
561         assertGolden(
562                 "original.apk", "golden-rsa-minSdkVersion-1-out.apk",
563                 new ApkSigner.Builder(rsaSignerConfig).setMinSdkVersion(1));
564         assertGolden(
565                 "original.apk", "golden-rsa-minSdkVersion-18-out.apk",
566                 new ApkSigner.Builder(rsaSignerConfig).setMinSdkVersion(18));
567         assertGolden(
568                 "original.apk", "golden-rsa-minSdkVersion-24-out.apk",
569                 new ApkSigner.Builder(rsaSignerConfig).setMinSdkVersion(24));
570 
571         // TODO: Add tests for DSA and ECDSA. This is non-trivial because the default
572         // implementations of these signature algorithms are non-deterministic which means output
573         // files always differ from golden files.
574     }
575 
576     @Test
testRsaSignedVerifies()577     public void testRsaSignedVerifies() throws Exception {
578         List<ApkSigner.SignerConfig> signers = Collections.singletonList(
579                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
580         String in = "original.apk";
581 
582         // Sign so that the APK is guaranteed to verify on API Level 1+
583         DataSource out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1));
584         assertVerified(verifyForMinSdkVersion(out, 1));
585 
586         // Sign so that the APK is guaranteed to verify on API Level 18+
587         out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(18));
588         assertVerified(verifyForMinSdkVersion(out, 18));
589         // Does not verify on API Level 17 because RSA with SHA-256 not supported
590         assertVerificationFailure(
591                 verifyForMinSdkVersion(out, 17), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG);
592     }
593 
594     @Test
testDsaSignedVerifies()595     public void testDsaSignedVerifies() throws Exception {
596         List<ApkSigner.SignerConfig> signers =
597                 Collections.singletonList(getDefaultSignerConfigFromResources("dsa-1024"));
598         String in = "original.apk";
599 
600         // Sign so that the APK is guaranteed to verify on API Level 1+
601         DataSource out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1));
602         assertVerified(verifyForMinSdkVersion(out, 1));
603 
604         // Sign so that the APK is guaranteed to verify on API Level 21+
605         out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(21));
606         assertVerified(verifyForMinSdkVersion(out, 21));
607         // Does not verify on API Level 20 because DSA with SHA-256 not supported
608         assertVerificationFailure(
609                 verifyForMinSdkVersion(out, 20), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG);
610     }
611 
612     @Test
testEcSignedVerifies()613     public void testEcSignedVerifies() throws Exception {
614         List<ApkSigner.SignerConfig> signers =
615                 Collections.singletonList(getDefaultSignerConfigFromResources("ec-p256"));
616         String in = "original.apk";
617 
618         // NOTE: EC APK signatures are not supported prior to API Level 18
619         // Sign so that the APK is guaranteed to verify on API Level 18+
620         DataSource out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(18));
621         assertVerified(verifyForMinSdkVersion(out, 18));
622         // Does not verify on API Level 17 because EC not supported
623         assertVerificationFailure(
624                 verifyForMinSdkVersion(out, 17), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG);
625     }
626 
627     @Test
testV1SigningRejectsInvalidZipEntryNames()628     public void testV1SigningRejectsInvalidZipEntryNames() throws Exception {
629         // ZIP/JAR entry name cannot contain CR, LF, or NUL characters when the APK is being
630         // JAR-signed.
631         List<ApkSigner.SignerConfig> signers = Collections.singletonList(
632                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
633         try {
634             sign("v1-only-with-cr-in-entry-name.apk",
635                     new ApkSigner.Builder(signers).setV1SigningEnabled(true));
636             fail();
637         } catch (ApkFormatException expected) {}
638 
639         try {
640             sign("v1-only-with-lf-in-entry-name.apk",
641                     new ApkSigner.Builder(signers).setV1SigningEnabled(true));
642             fail();
643         } catch (ApkFormatException expected) {}
644 
645         try {
646             sign("v1-only-with-nul-in-entry-name.apk",
647                     new ApkSigner.Builder(signers).setV1SigningEnabled(true));
648             fail();
649         } catch (ApkFormatException expected) {}
650     }
651 
652     @Test
testWeirdZipCompressionMethod()653     public void testWeirdZipCompressionMethod() throws Exception {
654         // Any ZIP compression method other than STORED is treated as DEFLATED by Android.
655         // This APK declares compression method 21 (neither STORED nor DEFLATED) for CERT.RSA entry,
656         // but the entry is actually Deflate-compressed.
657         List<ApkSigner.SignerConfig> signers = Collections.singletonList(
658                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
659         sign("weird-compression-method.apk", new ApkSigner.Builder(signers));
660     }
661 
662     @Test
testZipCompressionMethodMismatchBetweenLfhAndCd()663     public void testZipCompressionMethodMismatchBetweenLfhAndCd() throws Exception {
664         // Android Package Manager ignores compressionMethod field in Local File Header and always
665         // uses the compressionMethod from Central Directory instead.
666         // In this APK, compression method of CERT.RSA is declared as STORED in Local File Header
667         // and as DEFLATED in Central Directory. The entry is actually Deflate-compressed.
668         List<ApkSigner.SignerConfig> signers = Collections.singletonList(
669                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
670         sign("mismatched-compression-method.apk", new ApkSigner.Builder(signers));
671     }
672 
673     @Test
testDebuggableApk()674     public void testDebuggableApk() throws Exception {
675         // APK which uses a boolean value "true" in its android:debuggable
676         String apk = "debuggable-boolean.apk";
677         List<ApkSigner.SignerConfig> signers = Collections.singletonList(
678                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
679         // Signing debuggable APKs is permitted by default
680         sign(apk, new ApkSigner.Builder(signers));
681         // Signing debuggable APK succeeds when explicitly requested
682         sign(apk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(true));
683         // Signing debuggable APK fails when requested
684         try {
685             sign(apk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(false));
686             fail();
687         } catch (SignatureException expected) {}
688 
689         // APK which uses a reference value, pointing to boolean "false", in its android:debuggable
690         apk = "debuggable-resource.apk";
691         // When we permit signing regardless of whether the APK is debuggable, the value of
692         // android:debuggable should be ignored.
693         sign(apk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(true));
694 
695         // When we disallow signing debuggable APKs, APKs with android:debuggable being a resource
696         // reference must be rejected, because there's no easy way to establish whether the resolved
697         // boolean value is the same for all resource configurations.
698         try {
699             sign(apk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(false));
700             fail();
701         } catch (SignatureException expected) {}
702     }
703 
704     @Test(expected = IllegalStateException.class)
testV3SigningWithSignersNotInLineageFails()705     public void testV3SigningWithSignersNotInLineageFails() throws Exception {
706         // APKs signed with the v3 scheme after a key rotation must specify the lineage containing
707         // the proof of rotation. This test verifies that the signing will fail if the provided
708         // signers are not in the specified lineage.
709         List<ApkSigner.SignerConfig> signers = Arrays.asList(
710                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
711                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
712         SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(),
713                 "rsa-1024-lineage-2-signers");
714         sign("original.apk", new ApkSigner.Builder(signers).setSigningCertificateLineage(lineage));
715     }
716 
717     @Test
testSigningWithLineageRequiresOldestSignerForV1AndV2()718     public void testSigningWithLineageRequiresOldestSignerForV1AndV2() throws Exception {
719         // After a key rotation the oldest signer must still be specified for v1 and v2 signing.
720         // The lineage contains the proof of rotation and will be used to determine the oldest
721         // signer.
722         ApkSigner.SignerConfig firstSigner = getDefaultSignerConfigFromResources(
723                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
724         ApkSigner.SignerConfig secondSigner = getDefaultSignerConfigFromResources(
725                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
726         ApkSigner.SignerConfig thirdSigner = getDefaultSignerConfigFromResources(
727                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
728         SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(),
729                 "rsa-2048-lineage-3-signers");
730 
731         // Verifies that the v1 signing scheme requires the oldest signer after a key rotation.
732         List<ApkSigner.SignerConfig> signers = Collections.singletonList(thirdSigner);
733         try {
734             sign("original.apk", new ApkSigner.Builder(signers)
735                     .setV1SigningEnabled(true)
736                     .setV2SigningEnabled(false)
737                     .setV3SigningEnabled(true)
738                     .setSigningCertificateLineage(lineage));
739             fail("The signing should have failed due to the oldest signer in the lineage not being"
740                     + " provided for v1 signing");
741         } catch (IllegalArgumentException expected) {}
742 
743         // Verifies that the v2 signing scheme requires the oldest signer after a key rotation.
744         try {
745             sign("original.apk", new ApkSigner.Builder(signers)
746                     .setV1SigningEnabled(false)
747                     .setV2SigningEnabled(true)
748                     .setV3SigningEnabled(true)
749                     .setSigningCertificateLineage(lineage));
750             fail("The signing should have failed due to the oldest signer in the lineage not being"
751                     + " provided for v2 signing");
752         } catch (IllegalArgumentException expected) {}
753 
754         // Verifies that when only the v3 signing scheme is requested the oldest signer does not
755         // need to be provided.
756         sign("original.apk", new ApkSigner.Builder(signers)
757                 .setV1SigningEnabled(false)
758                 .setV2SigningEnabled(false)
759                 .setV3SigningEnabled(true)
760                 .setSigningCertificateLineage(lineage));
761 
762         // Verifies that an intermediate signer in the lineage is not sufficient to satisfy the
763         // requirement that the oldest signer be provided for v1 and v2 signing.
764         signers = Arrays.asList(secondSigner, thirdSigner);
765         try {
766             sign("original.apk", new ApkSigner.Builder(signers)
767                     .setV1SigningEnabled(true)
768                     .setV2SigningEnabled(true)
769                     .setV3SigningEnabled(true)
770                     .setSigningCertificateLineage(lineage));
771             fail("The signing should have failed due to the oldest signer in the lineage not being"
772                     + " provided for v1/v2 signing");
773         } catch (IllegalArgumentException expected) {}
774 
775         // Verifies that the signing is successful when the oldest and newest signers are provided
776         // and that intermediate signers are not required.
777         signers = Arrays.asList(firstSigner, thirdSigner);
778         sign("original.apk", new ApkSigner.Builder(signers)
779                 .setV1SigningEnabled(true)
780                 .setV2SigningEnabled(true)
781                 .setV3SigningEnabled(true)
782                 .setSigningCertificateLineage(lineage));
783     }
784 
785     @Test(expected = IllegalStateException.class)
testV3SigningWithMultipleSignersAndNoLineageFails()786     public void testV3SigningWithMultipleSignersAndNoLineageFails() throws Exception {
787         // The v3 signing scheme does not support multiple signers; if multiple signers are provided
788         // it is assumed these signers are part of the lineage. This test verifies v3 signing
789         // fails if multiple signers are provided without a lineage.
790         ApkSigner.SignerConfig firstSigner = getDefaultSignerConfigFromResources(
791                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
792         ApkSigner.SignerConfig secondSigner = getDefaultSignerConfigFromResources(
793                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
794         List<ApkSigner.SignerConfig> signers = Arrays.asList(firstSigner, secondSigner);
795         sign("original.apk", new ApkSigner.Builder(signers)
796                 .setV1SigningEnabled(true)
797                 .setV2SigningEnabled(true)
798                 .setV3SigningEnabled(true));
799     }
800 
801     @Test
testLineageCanBeReadAfterV3Signing()802     public void testLineageCanBeReadAfterV3Signing() throws Exception {
803         SigningCertificateLineage.SignerConfig firstSigner = Resources.toLineageSignerConfig(
804                 getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
805         SigningCertificateLineage.SignerConfig secondSigner = Resources.toLineageSignerConfig(
806                 getClass(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
807         SigningCertificateLineage lineage = new SigningCertificateLineage.Builder(firstSigner,
808                 secondSigner).build();
809         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(
810                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
811                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
812         DataSource out = sign("original.apk", new ApkSigner.Builder(signerConfigs)
813                 .setV3SigningEnabled(true)
814                 .setSigningCertificateLineage(lineage));
815         SigningCertificateLineage lineageFromApk = SigningCertificateLineage.readFromApkDataSource(
816                 out);
817         assertTrue("The first signer was not in the lineage from the signed APK",
818                 lineageFromApk.isSignerInLineage((firstSigner)));
819         assertTrue("The second signer was not in the lineage from the signed APK",
820                 lineageFromApk.isSignerInLineage((secondSigner)));
821     }
822 
823     @Test
testPublicKeyHasPositiveModulusAfterSigning()824     public void testPublicKeyHasPositiveModulusAfterSigning() throws Exception {
825         // The V2 and V3 signature schemes include the public key from the certificate in the
826         // signing block. If a certificate with an RSAPublicKey is improperly encoded with a
827         // negative modulus this was previously written to the signing block as is and failed on
828         // device verification since on device the public key in the certificate was reencoded with
829         // the correct encoding for the modulus. This test uses an improperly encoded certificate to
830         // sign an APK and verifies that the public key in the signing block is corrected with a
831         // positive modulus to allow on device installs / updates.
832         List<ApkSigner.SignerConfig> signersList = Collections.singletonList(
833                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
834                         FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS));
835         DataSource signedApk = sign("original.apk", new ApkSigner.Builder(signersList)
836                 .setV1SigningEnabled(true)
837                 .setV2SigningEnabled(true)
838                 .setV3SigningEnabled(true));
839         RSAPublicKey v2PublicKey = getRSAPublicKeyFromSigningBlock(signedApk,
840                 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2);
841         assertTrue("The modulus in the public key in the V2 signing block must not be negative",
842                 v2PublicKey.modulus.compareTo(BigInteger.ZERO) > 0);
843         RSAPublicKey v3PublicKey = getRSAPublicKeyFromSigningBlock(signedApk,
844                 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3);
845         assertTrue("The modulus in the public key in the V3 signing block must not be negative",
846                 v3PublicKey.modulus.compareTo(BigInteger.ZERO) > 0);
847     }
848 
getRSAPublicKeyFromSigningBlock(DataSource apk, int signatureVersionId)849     private RSAPublicKey getRSAPublicKeyFromSigningBlock(DataSource apk, int signatureVersionId)
850             throws Exception {
851         int signatureVersionBlockId;
852         switch (signatureVersionId) {
853             case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2:
854                 signatureVersionBlockId = V2SchemeSigner.APK_SIGNATURE_SCHEME_V2_BLOCK_ID;
855                 break;
856             case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3:
857                 signatureVersionBlockId = V3SchemeSigner.APK_SIGNATURE_SCHEME_V3_BLOCK_ID;
858                 break;
859             default:
860                 throw new Exception(
861                         "Invalid signature version ID specified: " + signatureVersionId);
862         }
863         ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk);
864         ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result(
865                 signatureVersionId);
866         SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature(apk, zipSections,
867                 signatureVersionBlockId, result);
868         // FORMAT:
869         // * length prefixed sequence of length prefixed signers
870         //   * length-prefixed signed data
871         //   * V3+ only - minSDK (uint32)
872         //   * V3+ only - maxSDK (uint32)
873         //   * length-prefixed sequence of length-prefixed signatures:
874         //   * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded)
875         ByteBuffer signers = ApkSigningBlockUtils.getLengthPrefixedSlice(
876                 signatureInfo.signatureBlock);
877         ByteBuffer signer = ApkSigningBlockUtils.getLengthPrefixedSlice(signers);
878         // Since all the data is read from the signer block the signedData and signatures are
879         // discarded.
880         ApkSigningBlockUtils.getLengthPrefixedSlice(signer);
881         // For V3+ signature version IDs discard the min / max SDKs as well
882         if (signatureVersionId >= ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3) {
883             signer.getInt();
884             signer.getInt();
885         }
886         ApkSigningBlockUtils.getLengthPrefixedSlice(signer);
887         ByteBuffer publicKey = ApkSigningBlockUtils.getLengthPrefixedSlice(signer);
888         SubjectPublicKeyInfo subjectPublicKeyInfo = Asn1BerParser.parse(publicKey,
889                 SubjectPublicKeyInfo.class);
890         ByteBuffer subjectPublicKeyBuffer = subjectPublicKeyInfo.subjectPublicKey;
891         // The SubjectPublicKey is stored as a bit string in the SubjectPublicKeyInfo with the first
892         // byte indicating the number of padding bits in the public key. Read this first byte to
893         // allow parsing the rest of the RSAPublicKey as a sequence.
894         subjectPublicKeyBuffer.get();
895         RSAPublicKey rsaPublicKey = Asn1BerParser.parse(subjectPublicKeyBuffer,
896                 RSAPublicKey.class);
897         return rsaPublicKey;
898     }
899 
900     /**
901      * Asserts that signing the specified golden input file using the provided signing
902      * configuration produces output identical to the specified golden output file.
903      */
assertGolden( String inResourceName, String expectedOutResourceName, ApkSigner.Builder apkSignerBuilder)904     private void assertGolden(
905             String inResourceName, String expectedOutResourceName,
906             ApkSigner.Builder apkSignerBuilder) throws Exception {
907         // Sign the provided golden input
908         DataSource out = sign(inResourceName, apkSignerBuilder);
909 
910         // Assert that the output is identical to the provided golden output
911         if (out.size() > Integer.MAX_VALUE) {
912             throw new RuntimeException("Output too large: " + out.size() + " bytes");
913         }
914         ByteBuffer actualOutBuf = out.getByteBuffer(0, (int) out.size());
915 
916         ByteBuffer expectedOutBuf =
917                 ByteBuffer.wrap(Resources.toByteArray(getClass(), expectedOutResourceName));
918 
919         int actualStartPos = actualOutBuf.position();
920         boolean identical = false;
921         if (actualOutBuf.remaining() == expectedOutBuf.remaining()) {
922             while (actualOutBuf.hasRemaining()) {
923                 if (actualOutBuf.get() != expectedOutBuf.get()) {
924                     break;
925                 }
926             }
927             identical = !actualOutBuf.hasRemaining();
928         }
929 
930         if (identical) {
931             return;
932         }
933         actualOutBuf.position(actualStartPos);
934 
935         if (KEEP_FAILING_OUTPUT_AS_FILES) {
936             File tmp = File.createTempFile(getClass().getSimpleName(), ".apk");
937             try (ByteChannel outChannel =
938                     Files.newByteChannel(
939                             tmp.toPath(),
940                             StandardOpenOption.WRITE,
941                             StandardOpenOption.CREATE,
942                             StandardOpenOption.TRUNCATE_EXISTING)) {
943                 while (actualOutBuf.hasRemaining()) {
944                     outChannel.write(actualOutBuf);
945                 }
946             }
947             fail(tmp + " differs from " + expectedOutResourceName);
948         } else {
949             fail("Output differs from " + expectedOutResourceName);
950         }
951     }
952 
sign( String inResourceName, ApkSigner.Builder apkSignerBuilder)953     private DataSource sign(
954             String inResourceName, ApkSigner.Builder apkSignerBuilder) throws Exception {
955         DataSource in =
956                 DataSources.asDataSource(
957                         ByteBuffer.wrap(Resources.toByteArray(getClass(), inResourceName)));
958         ReadableDataSink out = DataSinks.newInMemoryDataSink();
959         apkSignerBuilder
960                 .setInputApk(in)
961                 .setOutputApk(out)
962                 .build()
963                 .sign();
964         return out;
965     }
966 
verifyForMinSdkVersion(DataSource apk, int minSdkVersion)967     private static ApkVerifier.Result verifyForMinSdkVersion(DataSource apk, int minSdkVersion)
968             throws IOException, ApkFormatException, NoSuchAlgorithmException {
969         return verify(apk, minSdkVersion);
970     }
971 
verify(DataSource apk, Integer minSdkVersionOverride)972     private static ApkVerifier.Result verify(DataSource apk, Integer minSdkVersionOverride)
973             throws IOException, ApkFormatException, NoSuchAlgorithmException {
974         ApkVerifier.Builder builder = new ApkVerifier.Builder(apk);
975         if (minSdkVersionOverride != null) {
976             builder.setMinCheckedPlatformVersion(minSdkVersionOverride);
977         }
978         return builder.build().verify();
979     }
980 
assertVerified(ApkVerifier.Result result)981     private static void assertVerified(ApkVerifier.Result result) {
982         ApkVerifierTest.assertVerified(result);
983     }
984 
assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue)985     private static void assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue) {
986         ApkVerifierTest.assertVerificationFailure(result, expectedIssue);
987     }
988 
getDefaultSignerConfigFromResources( String keyNameInResources)989     private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources(
990             String keyNameInResources) throws Exception {
991         PrivateKey privateKey =
992                 Resources.toPrivateKey(ApkSignerTest.class, keyNameInResources + ".pk8");
993         List<X509Certificate> certs =
994                 Resources.toCertificateChain(ApkSignerTest.class, keyNameInResources + ".x509.pem");
995         return new ApkSigner.SignerConfig.Builder(keyNameInResources, privateKey, certs).build();
996     }
997 
getDefaultSignerConfigFromResources( String keyNameInResources, String certNameInResources)998     private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources(
999             String keyNameInResources, String certNameInResources) throws Exception {
1000         PrivateKey privateKey = Resources.toPrivateKey(ApkSignerTest.class,
1001                 keyNameInResources + ".pk8");
1002         List<X509Certificate> certs = Resources.toCertificateChain(ApkSignerTest.class,
1003                 certNameInResources);
1004         return new ApkSigner.SignerConfig.Builder(keyNameInResources, privateKey, certs).build();
1005     }
1006 }
1007