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.internal.apk.v1;
18 
19 import com.android.apksig.ApkVerifier.Issue;
20 import com.android.apksig.ApkVerifier.IssueWithParams;
21 import com.android.apksig.apk.ApkFormatException;
22 import com.android.apksig.apk.ApkUtils;
23 import com.android.apksig.internal.asn1.Asn1BerParser;
24 import com.android.apksig.internal.asn1.Asn1Class;
25 import com.android.apksig.internal.asn1.Asn1DecodingException;
26 import com.android.apksig.internal.asn1.Asn1Field;
27 import com.android.apksig.internal.asn1.Asn1OpaqueObject;
28 import com.android.apksig.internal.asn1.Asn1Type;
29 import com.android.apksig.internal.jar.ManifestParser;
30 import com.android.apksig.internal.pkcs7.Attribute;
31 import com.android.apksig.internal.pkcs7.ContentInfo;
32 import com.android.apksig.internal.pkcs7.IssuerAndSerialNumber;
33 import com.android.apksig.internal.pkcs7.Pkcs7Constants;
34 import com.android.apksig.internal.pkcs7.Pkcs7DecodingException;
35 import com.android.apksig.internal.pkcs7.SignedData;
36 import com.android.apksig.internal.pkcs7.SignerIdentifier;
37 import com.android.apksig.internal.pkcs7.SignerInfo;
38 import com.android.apksig.internal.util.AndroidSdkVersion;
39 import com.android.apksig.internal.util.ByteBufferUtils;
40 import com.android.apksig.internal.util.X509CertificateUtils;
41 import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate;
42 import com.android.apksig.internal.util.InclusiveIntRange;
43 import com.android.apksig.internal.util.Pair;
44 import com.android.apksig.internal.zip.CentralDirectoryRecord;
45 import com.android.apksig.internal.zip.LocalFileRecord;
46 import com.android.apksig.util.DataSinks;
47 import com.android.apksig.util.DataSource;
48 import com.android.apksig.zip.ZipFormatException;
49 
50 import java.io.IOException;
51 import java.math.BigInteger;
52 import java.nio.ByteBuffer;
53 import java.nio.ByteOrder;
54 import java.security.InvalidKeyException;
55 import java.security.MessageDigest;
56 import java.security.NoSuchAlgorithmException;
57 import java.security.Principal;
58 import java.security.Signature;
59 import java.security.SignatureException;
60 import java.security.cert.CertificateException;
61 import java.security.cert.X509Certificate;
62 import java.util.ArrayList;
63 import java.util.Arrays;
64 import java.util.Base64;
65 import java.util.Base64.Decoder;
66 import java.util.Collection;
67 import java.util.Collections;
68 import java.util.HashMap;
69 import java.util.HashSet;
70 import java.util.List;
71 import java.util.Locale;
72 import java.util.Map;
73 import java.util.Set;
74 import java.util.StringTokenizer;
75 import java.util.jar.Attributes;
76 
77 import javax.security.auth.x500.X500Principal;
78 
79 /**
80  * APK verifier which uses JAR signing (aka v1 signing scheme).
81  *
82  * @see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File">Signed JAR File</a>
83  */
84 public abstract class V1SchemeVerifier {
85 
86     private static final String MANIFEST_ENTRY_NAME = V1SchemeSigner.MANIFEST_ENTRY_NAME;
87 
V1SchemeVerifier()88     private V1SchemeVerifier() {}
89 
90     /**
91      * Verifies the provided APK's JAR signatures and returns the result of verification. APK is
92      * considered verified only if {@link Result#verified} is {@code true}. If verification fails,
93      * the result will contain errors -- see {@link Result#getErrors()}.
94      *
95      * <p>Verification succeeds iff the APK's JAR signatures are expected to verify on all Android
96      * platform versions in the {@code [minSdkVersion, maxSdkVersion]} range. If the APK's signature
97      * is expected to not verify on any of the specified platform versions, this method returns a
98      * result with one or more errors and whose {@code Result.verified == false}, or this method
99      * throws an exception.
100      *
101      * @throws ApkFormatException if the APK is malformed
102      * @throws IOException if an I/O error occurs when reading the APK
103      * @throws NoSuchAlgorithmException if the APK's JAR signatures cannot be verified because a
104      *         required cryptographic algorithm implementation is missing
105      */
verify( DataSource apk, ApkUtils.ZipSections apkSections, Map<Integer, String> supportedApkSigSchemeNames, Set<Integer> foundApkSigSchemeIds, int minSdkVersion, int maxSdkVersion)106     public static Result verify(
107             DataSource apk,
108             ApkUtils.ZipSections apkSections,
109             Map<Integer, String> supportedApkSigSchemeNames,
110             Set<Integer> foundApkSigSchemeIds,
111             int minSdkVersion,
112             int maxSdkVersion) throws IOException, ApkFormatException, NoSuchAlgorithmException {
113         if (minSdkVersion > maxSdkVersion) {
114             throw new IllegalArgumentException(
115                     "minSdkVersion (" + minSdkVersion + ") > maxSdkVersion (" + maxSdkVersion
116                             + ")");
117         }
118 
119         Result result = new Result();
120 
121         // Parse the ZIP Central Directory and check that there are no entries with duplicate names.
122         List<CentralDirectoryRecord> cdRecords = parseZipCentralDirectory(apk, apkSections);
123         Set<String> cdEntryNames = checkForDuplicateEntries(cdRecords, result);
124         if (result.containsErrors()) {
125             return result;
126         }
127 
128         // Verify JAR signature(s).
129         Signers.verify(
130                 apk,
131                 apkSections.getZipCentralDirectoryOffset(),
132                 cdRecords,
133                 cdEntryNames,
134                 supportedApkSigSchemeNames,
135                 foundApkSigSchemeIds,
136                 minSdkVersion,
137                 maxSdkVersion,
138                 result);
139 
140         return result;
141     }
142 
143     /**
144      * Returns the set of entry names and reports any duplicate entry names in the {@code result}
145      * as errors.
146      */
checkForDuplicateEntries( List<CentralDirectoryRecord> cdRecords, Result result)147     private static Set<String> checkForDuplicateEntries(
148             List<CentralDirectoryRecord> cdRecords, Result result) {
149         Set<String> cdEntryNames = new HashSet<>(cdRecords.size());
150         Set<String> duplicateCdEntryNames = null;
151         for (CentralDirectoryRecord cdRecord : cdRecords) {
152             String entryName = cdRecord.getName();
153             if (!cdEntryNames.add(entryName)) {
154                 // This is an error. Report this once per duplicate name.
155                 if (duplicateCdEntryNames == null) {
156                     duplicateCdEntryNames = new HashSet<>();
157                 }
158                 if (duplicateCdEntryNames.add(entryName)) {
159                     result.addError(Issue.JAR_SIG_DUPLICATE_ZIP_ENTRY, entryName);
160                 }
161             }
162         }
163         return cdEntryNames;
164     }
165 
166     /**
167     * Parses raw representation of MANIFEST.MF file into a pair of main entry manifest section
168     * representation and a mapping between entry name and its manifest section representation.
169     *
170     * @param manifestBytes raw representation of Manifest.MF
171     * @param cdEntryNames expected set of entry names
172     * @param result object to keep track of errors that happened during the parsing
173     * @return a pair of main entry manifest section representation and a mapping between entry name
174     *     and its manifest section representation
175     */
parseManifest( byte[] manifestBytes, Set<String> cdEntryNames, Result result)176     public static Pair<ManifestParser.Section, Map<String, ManifestParser.Section>> parseManifest(
177             byte[] manifestBytes, Set<String> cdEntryNames, Result result) {
178         ManifestParser manifest = new ManifestParser(manifestBytes);
179         ManifestParser.Section manifestMainSection = manifest.readSection();
180         List<ManifestParser.Section> manifestIndividualSections = manifest.readAllSections();
181         Map<String, ManifestParser.Section> entryNameToManifestSection =
182                 new HashMap<>(manifestIndividualSections.size());
183         int manifestSectionNumber = 0;
184         for (ManifestParser.Section manifestSection : manifestIndividualSections) {
185             manifestSectionNumber++;
186             String entryName = manifestSection.getName();
187             if (entryName == null) {
188                 result.addError(Issue.JAR_SIG_UNNNAMED_MANIFEST_SECTION, manifestSectionNumber);
189                 continue;
190             }
191             if (entryNameToManifestSection.put(entryName, manifestSection) != null) {
192                 result.addError(Issue.JAR_SIG_DUPLICATE_MANIFEST_SECTION, entryName);
193                 continue;
194             }
195             if (!cdEntryNames.contains(entryName)) {
196                 result.addError(
197                         Issue.JAR_SIG_MISSING_ZIP_ENTRY_REFERENCED_IN_MANIFEST, entryName);
198                 continue;
199             }
200         }
201         return Pair.of(manifestMainSection, entryNameToManifestSection);
202     }
203 
204     /**
205      * All JAR signers of an APK.
206      */
207     private static class Signers {
208 
209         /**
210          * Verifies JAR signatures of the provided APK and populates the provided result container
211          * with errors, warnings, and information about signers. The APK is considered verified if
212          * the {@link Result#verified} is {@code true}.
213          */
verify( DataSource apk, long cdStartOffset, List<CentralDirectoryRecord> cdRecords, Set<String> cdEntryNames, Map<Integer, String> supportedApkSigSchemeNames, Set<Integer> foundApkSigSchemeIds, int minSdkVersion, int maxSdkVersion, Result result)214         private static void verify(
215                 DataSource apk,
216                 long cdStartOffset,
217                 List<CentralDirectoryRecord> cdRecords,
218                 Set<String> cdEntryNames,
219                 Map<Integer, String> supportedApkSigSchemeNames,
220                 Set<Integer> foundApkSigSchemeIds,
221                 int minSdkVersion,
222                 int maxSdkVersion,
223                 Result result) throws ApkFormatException, IOException, NoSuchAlgorithmException {
224 
225             // Find JAR manifest and signature block files.
226             CentralDirectoryRecord manifestEntry = null;
227             Map<String, CentralDirectoryRecord> sigFileEntries = new HashMap<>(1);
228             List<CentralDirectoryRecord> sigBlockEntries = new ArrayList<>(1);
229             for (CentralDirectoryRecord cdRecord : cdRecords) {
230                 String entryName = cdRecord.getName();
231                 if (!entryName.startsWith("META-INF/")) {
232                     continue;
233                 }
234                 if ((manifestEntry == null) && (MANIFEST_ENTRY_NAME.equals(entryName))) {
235                     manifestEntry = cdRecord;
236                     continue;
237                 }
238                 if (entryName.endsWith(".SF")) {
239                     sigFileEntries.put(entryName, cdRecord);
240                     continue;
241                 }
242                 if ((entryName.endsWith(".RSA"))
243                         || (entryName.endsWith(".DSA"))
244                         || (entryName.endsWith(".EC"))) {
245                     sigBlockEntries.add(cdRecord);
246                     continue;
247                 }
248             }
249             if (manifestEntry == null) {
250                 result.addError(Issue.JAR_SIG_NO_MANIFEST);
251                 return;
252             }
253 
254             // Parse the JAR manifest and check that all JAR entries it references exist in the APK.
255             byte[] manifestBytes;
256             try {
257                 manifestBytes =
258                         LocalFileRecord.getUncompressedData(apk, manifestEntry, cdStartOffset);
259             } catch (ZipFormatException e) {
260                 throw new ApkFormatException("Malformed ZIP entry: " + manifestEntry.getName(), e);
261             }
262 
263             Pair<ManifestParser.Section, Map<String, ManifestParser.Section>> manifestSections =
264                     parseManifest(manifestBytes, cdEntryNames, result);
265 
266             if (result.containsErrors()) {
267                 return;
268             }
269 
270             ManifestParser.Section manifestMainSection = manifestSections.getFirst();
271             Map<String, ManifestParser.Section> entryNameToManifestSection =
272                     manifestSections.getSecond();
273 
274             // STATE OF AFFAIRS:
275             // * All JAR entries listed in JAR manifest are present in the APK.
276 
277             // Identify signers
278             List<Signer> signers = new ArrayList<>(sigBlockEntries.size());
279             for (CentralDirectoryRecord sigBlockEntry : sigBlockEntries) {
280                 String sigBlockEntryName = sigBlockEntry.getName();
281                 int extensionDelimiterIndex = sigBlockEntryName.lastIndexOf('.');
282                 if (extensionDelimiterIndex == -1) {
283                     throw new RuntimeException(
284                             "Signature block file name does not contain extension: "
285                                     + sigBlockEntryName);
286                 }
287                 String sigFileEntryName =
288                         sigBlockEntryName.substring(0, extensionDelimiterIndex) + ".SF";
289                 CentralDirectoryRecord sigFileEntry = sigFileEntries.get(sigFileEntryName);
290                 if (sigFileEntry == null) {
291                     result.addWarning(
292                             Issue.JAR_SIG_MISSING_FILE, sigBlockEntryName, sigFileEntryName);
293                     continue;
294                 }
295                 String signerName = sigBlockEntryName.substring("META-INF/".length());
296                 Result.SignerInfo signerInfo =
297                         new Result.SignerInfo(
298                                 signerName, sigBlockEntryName, sigFileEntry.getName());
299                 Signer signer = new Signer(signerName, sigBlockEntry, sigFileEntry, signerInfo);
300                 signers.add(signer);
301             }
302             if (signers.isEmpty()) {
303                 result.addError(Issue.JAR_SIG_NO_SIGNATURES);
304                 return;
305             }
306 
307             // Verify each signer's signature block file .(RSA|DSA|EC) against the corresponding
308             // signature file .SF. Any error encountered for any signer terminates verification, to
309             // mimic Android's behavior.
310             for (Signer signer : signers) {
311                 signer.verifySigBlockAgainstSigFile(
312                         apk, cdStartOffset, minSdkVersion, maxSdkVersion);
313                 if (signer.getResult().containsErrors()) {
314                     result.signers.add(signer.getResult());
315                 }
316             }
317             if (result.containsErrors()) {
318                 return;
319             }
320             // STATE OF AFFAIRS:
321             // * All JAR entries listed in JAR manifest are present in the APK.
322             // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
323 
324             // Verify each signer's signature file (.SF) against the JAR manifest.
325             List<Signer> remainingSigners = new ArrayList<>(signers.size());
326             for (Signer signer : signers) {
327                 signer.verifySigFileAgainstManifest(
328                         manifestBytes,
329                         manifestMainSection,
330                         entryNameToManifestSection,
331                         supportedApkSigSchemeNames,
332                         foundApkSigSchemeIds,
333                         minSdkVersion,
334                         maxSdkVersion);
335                 if (signer.isIgnored()) {
336                     result.ignoredSigners.add(signer.getResult());
337                 } else {
338                     if (signer.getResult().containsErrors()) {
339                         result.signers.add(signer.getResult());
340                     } else {
341                         remainingSigners.add(signer);
342                     }
343                 }
344             }
345             if (result.containsErrors()) {
346                 return;
347             }
348             signers = remainingSigners;
349             if (signers.isEmpty()) {
350                 result.addError(Issue.JAR_SIG_NO_SIGNATURES);
351                 return;
352             }
353             // STATE OF AFFAIRS:
354             // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
355             // * Contents of all JAR manifest sections listed in .SF files verify against .SF files.
356             // * All JAR entries listed in JAR manifest are present in the APK.
357 
358             // Verify data of JAR entries against JAR manifest and .SF files. On Android, an APK's
359             // JAR entry is considered signed by signers associated with an .SF file iff the entry
360             // is mentioned in the .SF file and the entry's digest(s) mentioned in the JAR manifest
361             // match theentry's uncompressed data. Android requires that all such JAR entries are
362             // signed by the same set of signers. This set may be smaller than the set of signers
363             // we've identified so far.
364             Set<Signer> apkSigners =
365                     verifyJarEntriesAgainstManifestAndSigners(
366                             apk,
367                             cdStartOffset,
368                             cdRecords,
369                             entryNameToManifestSection,
370                             signers,
371                             minSdkVersion,
372                             maxSdkVersion,
373                             result);
374             if (result.containsErrors()) {
375                 return;
376             }
377             // STATE OF AFFAIRS:
378             // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
379             // * Contents of all JAR manifest sections listed in .SF files verify against .SF files.
380             // * All JAR entries listed in JAR manifest are present in the APK.
381             // * All JAR entries present in the APK and supposed to be covered by JAR signature
382             //   (i.e., reside outside of META-INF/) are covered by signatures from the same set
383             //   of signers.
384 
385             // Report any JAR entries which aren't covered by signature.
386             Set<String> signatureEntryNames = new HashSet<>(1 + result.signers.size() * 2);
387             signatureEntryNames.add(manifestEntry.getName());
388             for (Signer signer : apkSigners) {
389                 signatureEntryNames.add(signer.getSignatureBlockEntryName());
390                 signatureEntryNames.add(signer.getSignatureFileEntryName());
391             }
392             for (CentralDirectoryRecord cdRecord : cdRecords) {
393                 String entryName = cdRecord.getName();
394                 if ((entryName.startsWith("META-INF/"))
395                         && (!entryName.endsWith("/"))
396                         && (!signatureEntryNames.contains(entryName))) {
397                     result.addWarning(Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY, entryName);
398                 }
399             }
400 
401             // Reflect the sets of used signers and ignored signers in the result.
402             for (Signer signer : signers) {
403                 if (apkSigners.contains(signer)) {
404                     result.signers.add(signer.getResult());
405                 } else {
406                     result.ignoredSigners.add(signer.getResult());
407                 }
408             }
409 
410             result.verified = true;
411         }
412     }
413 
414     static class Signer {
415         private final String mName;
416         private final Result.SignerInfo mResult;
417         private final CentralDirectoryRecord mSignatureFileEntry;
418         private final CentralDirectoryRecord mSignatureBlockEntry;
419         private boolean mIgnored;
420 
421         private byte[] mSigFileBytes;
422         private Set<String> mSigFileEntryNames;
423 
Signer( String name, CentralDirectoryRecord sigBlockEntry, CentralDirectoryRecord sigFileEntry, Result.SignerInfo result)424         private Signer(
425                 String name,
426                 CentralDirectoryRecord sigBlockEntry,
427                 CentralDirectoryRecord sigFileEntry,
428                 Result.SignerInfo result) {
429             mName = name;
430             mResult = result;
431             mSignatureBlockEntry = sigBlockEntry;
432             mSignatureFileEntry = sigFileEntry;
433         }
434 
getName()435         public String getName() {
436             return mName;
437         }
438 
getSignatureFileEntryName()439         public String getSignatureFileEntryName() {
440             return mSignatureFileEntry.getName();
441         }
442 
getSignatureBlockEntryName()443         public String getSignatureBlockEntryName() {
444             return mSignatureBlockEntry.getName();
445         }
446 
setIgnored()447         void setIgnored() {
448             mIgnored = true;
449         }
450 
isIgnored()451         public boolean isIgnored() {
452             return mIgnored;
453         }
454 
getSigFileEntryNames()455         public Set<String> getSigFileEntryNames() {
456             return mSigFileEntryNames;
457         }
458 
getResult()459         public Result.SignerInfo getResult() {
460             return mResult;
461         }
462 
verifySigBlockAgainstSigFile( DataSource apk, long cdStartOffset, int minSdkVersion, int maxSdkVersion)463         public void verifySigBlockAgainstSigFile(
464                 DataSource apk, long cdStartOffset, int minSdkVersion, int maxSdkVersion)
465                         throws IOException, ApkFormatException, NoSuchAlgorithmException {
466             // Obtain the signature block from the APK
467             byte[] sigBlockBytes;
468             try {
469                 sigBlockBytes =
470                         LocalFileRecord.getUncompressedData(
471                                 apk, mSignatureBlockEntry, cdStartOffset);
472             } catch (ZipFormatException e) {
473                 throw new ApkFormatException(
474                         "Malformed ZIP entry: " + mSignatureBlockEntry.getName(), e);
475             }
476             // Obtain the signature file from the APK
477             try {
478                 mSigFileBytes =
479                         LocalFileRecord.getUncompressedData(
480                                 apk, mSignatureFileEntry, cdStartOffset);
481             } catch (ZipFormatException e) {
482                 throw new ApkFormatException(
483                         "Malformed ZIP entry: " + mSignatureFileEntry.getName(), e);
484             }
485 
486             // Extract PKCS #7 SignedData from the signature block
487             SignedData signedData;
488             try {
489                 ContentInfo contentInfo =
490                         Asn1BerParser.parse(ByteBuffer.wrap(sigBlockBytes), ContentInfo.class);
491                 if (!Pkcs7Constants.OID_SIGNED_DATA.equals(contentInfo.contentType)) {
492                     throw new Asn1DecodingException(
493                           "Unsupported ContentInfo.contentType: " + contentInfo.contentType);
494                 }
495                 signedData =
496                         Asn1BerParser.parse(contentInfo.content.getEncoded(), SignedData.class);
497             } catch (Asn1DecodingException e) {
498                 e.printStackTrace();
499                 mResult.addError(
500                         Issue.JAR_SIG_PARSE_EXCEPTION, mSignatureBlockEntry.getName(), e);
501                 return;
502             }
503 
504             if (signedData.signerInfos.isEmpty()) {
505                 mResult.addError(Issue.JAR_SIG_NO_SIGNERS, mSignatureBlockEntry.getName());
506                 return;
507             }
508 
509             // Find the first SignedData.SignerInfos element which verifies against the signature
510             // file
511             SignerInfo firstVerifiedSignerInfo = null;
512             X509Certificate firstVerifiedSignerInfoSigningCertificate = null;
513             // Prior to Android N, Android attempts to verify only the first SignerInfo. From N
514             // onwards, Android attempts to verify all SignerInfos and then picks the first verified
515             // SignerInfo.
516             List<SignerInfo> unverifiedSignerInfosToTry;
517             if (minSdkVersion < AndroidSdkVersion.N) {
518                 unverifiedSignerInfosToTry =
519                         Collections.singletonList(signedData.signerInfos.get(0));
520             } else {
521                 unverifiedSignerInfosToTry = signedData.signerInfos;
522             }
523             List<X509Certificate> signedDataCertificates = null;
524             for (SignerInfo unverifiedSignerInfo : unverifiedSignerInfosToTry) {
525                 // Parse SignedData.certificates -- they are needed to verify SignerInfo
526                 if (signedDataCertificates == null) {
527                     try {
528                         signedDataCertificates = parseCertificates(signedData.certificates);
529                     } catch (CertificateException e) {
530                         mResult.addError(
531                                 Issue.JAR_SIG_PARSE_EXCEPTION, mSignatureBlockEntry.getName(), e);
532                         return;
533                     }
534                 }
535 
536                 // Verify SignerInfo
537                 X509Certificate signingCertificate;
538                 try {
539                     signingCertificate =
540                             verifySignerInfoAgainstSigFile(
541                                     signedData,
542                                     signedDataCertificates,
543                                     unverifiedSignerInfo,
544                                     mSigFileBytes,
545                                     minSdkVersion,
546                                     maxSdkVersion);
547                     if (mResult.containsErrors()) {
548                         return;
549                     }
550                     if (signingCertificate != null) {
551                         // SignerInfo verified
552                         if (firstVerifiedSignerInfo == null) {
553                             firstVerifiedSignerInfo = unverifiedSignerInfo;
554                             firstVerifiedSignerInfoSigningCertificate = signingCertificate;
555                         }
556                     }
557                 } catch (Pkcs7DecodingException e) {
558                     mResult.addError(
559                             Issue.JAR_SIG_PARSE_EXCEPTION, mSignatureBlockEntry.getName(), e);
560                     return;
561                 } catch (InvalidKeyException | SignatureException e) {
562                     mResult.addError(
563                             Issue.JAR_SIG_VERIFY_EXCEPTION,
564                             mSignatureBlockEntry.getName(),
565                             mSignatureFileEntry.getName(),
566                             e);
567                     return;
568                 }
569             }
570             if (firstVerifiedSignerInfo == null) {
571                 // No SignerInfo verified
572                 mResult.addError(
573                         Issue.JAR_SIG_DID_NOT_VERIFY,
574                         mSignatureBlockEntry.getName(),
575                         mSignatureFileEntry.getName());
576                 return;
577             }
578             // Verified
579             List<X509Certificate> signingCertChain =
580                     getCertificateChain(
581                             signedDataCertificates, firstVerifiedSignerInfoSigningCertificate);
582             mResult.certChain.clear();
583             mResult.certChain.addAll(signingCertChain);
584         }
585 
586         /**
587          * Returns the signing certificate if the provided {@link SignerInfo} verifies against the
588          * contents of the provided signature file, or {@code null} if it does not verify.
589          */
verifySignerInfoAgainstSigFile( SignedData signedData, Collection<X509Certificate> signedDataCertificates, SignerInfo signerInfo, byte[] signatureFile, int minSdkVersion, int maxSdkVersion)590         private X509Certificate verifySignerInfoAgainstSigFile(
591                 SignedData signedData,
592                 Collection<X509Certificate> signedDataCertificates,
593                 SignerInfo signerInfo,
594                 byte[] signatureFile,
595                 int minSdkVersion,
596                 int maxSdkVersion)
597                         throws Pkcs7DecodingException, NoSuchAlgorithmException,
598                                 InvalidKeyException, SignatureException {
599             String digestAlgorithmOid = signerInfo.digestAlgorithm.algorithm;
600             String signatureAlgorithmOid = signerInfo.signatureAlgorithm.algorithm;
601             InclusiveIntRange desiredApiLevels =
602                     InclusiveIntRange.fromTo(minSdkVersion, maxSdkVersion);
603             List<InclusiveIntRange> apiLevelsWhereDigestAndSigAlgorithmSupported =
604                     getSigAlgSupportedApiLevels(digestAlgorithmOid, signatureAlgorithmOid);
605             List<InclusiveIntRange> apiLevelsWhereDigestAlgorithmNotSupported =
606                     desiredApiLevels.getValuesNotIn(apiLevelsWhereDigestAndSigAlgorithmSupported);
607             if (!apiLevelsWhereDigestAlgorithmNotSupported.isEmpty()) {
608                 String digestAlgorithmUserFriendly =
609                         OidToUserFriendlyNameMapper.getUserFriendlyNameForOid(
610                                 digestAlgorithmOid);
611                 if (digestAlgorithmUserFriendly == null) {
612                     digestAlgorithmUserFriendly = digestAlgorithmOid;
613                 }
614                 String signatureAlgorithmUserFriendly =
615                         OidToUserFriendlyNameMapper.getUserFriendlyNameForOid(
616                                 signatureAlgorithmOid);
617                 if (signatureAlgorithmUserFriendly == null) {
618                     signatureAlgorithmUserFriendly = signatureAlgorithmOid;
619                 }
620                 StringBuilder apiLevelsUserFriendly = new StringBuilder();
621                 for (InclusiveIntRange range : apiLevelsWhereDigestAlgorithmNotSupported) {
622                     if (apiLevelsUserFriendly.length() > 0) {
623                         apiLevelsUserFriendly.append(", ");
624                     }
625                     if (range.getMin() == range.getMax()) {
626                         apiLevelsUserFriendly.append(String.valueOf(range.getMin()));
627                     } else if (range.getMax() == Integer.MAX_VALUE) {
628                         apiLevelsUserFriendly.append(range.getMin() + "+");
629                     } else {
630                         apiLevelsUserFriendly.append(range.getMin() + "-" + range.getMax());
631                     }
632                 }
633                 mResult.addError(
634                         Issue.JAR_SIG_UNSUPPORTED_SIG_ALG,
635                         mSignatureBlockEntry.getName(),
636                         digestAlgorithmOid,
637                         signatureAlgorithmOid,
638                         apiLevelsUserFriendly.toString(),
639                         digestAlgorithmUserFriendly,
640                         signatureAlgorithmUserFriendly);
641                 return null;
642             }
643 
644             // From the bag of certs, obtain the certificate referenced by the SignerInfo,
645             // and verify the cryptographic signature in the SignerInfo against the certificate.
646 
647             // Locate the signing certificate referenced by the SignerInfo
648             X509Certificate signingCertificate =
649                     findCertificate(signedDataCertificates, signerInfo.sid);
650             if (signingCertificate == null) {
651                 throw new SignatureException(
652                         "Signing certificate referenced in SignerInfo not found in"
653                                 + " SignedData");
654             }
655 
656             // Check whether the signing certificate is acceptable. Android performs these
657             // checks explicitly, instead of delegating this to
658             // Signature.initVerify(Certificate).
659             if (signingCertificate.hasUnsupportedCriticalExtension()) {
660                 throw new SignatureException(
661                         "Signing certificate has unsupported critical extensions");
662             }
663             boolean[] keyUsageExtension = signingCertificate.getKeyUsage();
664             if (keyUsageExtension != null) {
665                 boolean digitalSignature =
666                         (keyUsageExtension.length >= 1) && (keyUsageExtension[0]);
667                 boolean nonRepudiation =
668                         (keyUsageExtension.length >= 2) && (keyUsageExtension[1]);
669                 if ((!digitalSignature) && (!nonRepudiation)) {
670                     throw new SignatureException(
671                             "Signing certificate not authorized for use in digital signatures"
672                                     + ": keyUsage extension missing digitalSignature and"
673                                     + " nonRepudiation");
674                 }
675             }
676 
677             // Verify the cryptographic signature in SignerInfo against the certificate's
678             // public key
679             String jcaSignatureAlgorithm =
680                     getJcaSignatureAlgorithm(digestAlgorithmOid, signatureAlgorithmOid);
681             Signature s = Signature.getInstance(jcaSignatureAlgorithm);
682             s.initVerify(signingCertificate.getPublicKey());
683             if (signerInfo.signedAttrs != null) {
684                 // Signed attributes present -- verify signature against the ASN.1 DER encoded form
685                 // of signed attributes. This verifies integrity of the signature file because
686                 // signed attributes must contain the digest of the signature file.
687                 if (minSdkVersion < AndroidSdkVersion.KITKAT) {
688                     // Prior to Android KitKat, APKs with signed attributes are unsafe:
689                     // * The APK's contents are not protected by the JAR signature because the
690                     //   digest in signed attributes is not verified. This means an attacker can
691                     //   arbitrarily modify the APK without invalidating its signature.
692                     // * Luckily, the signature over signed attributes was verified incorrectly
693                     //   (over the verbatim IMPLICIT [0] form rather than over re-encoded
694                     //   UNIVERSAL SET form) which means that JAR signatures which would verify on
695                     //   pre-KitKat Android and yet do not protect the APK from modification could
696                     //   be generated only by broken tools or on purpose by the entity signing the
697                     //   APK.
698                     //
699                     // We thus reject such unsafe APKs, even if they verify on platforms before
700                     // KitKat.
701                     throw new SignatureException(
702                             "APKs with Signed Attributes broken on platforms with API Level < "
703                                     + AndroidSdkVersion.KITKAT);
704                 }
705                 try {
706                     List<Attribute> signedAttributes =
707                             Asn1BerParser.parseImplicitSetOf(
708                                     signerInfo.signedAttrs.getEncoded(), Attribute.class);
709                     SignedAttributes signedAttrs = new SignedAttributes(signedAttributes);
710                     if (maxSdkVersion >= AndroidSdkVersion.N) {
711                         // Content Type attribute is checked only on Android N and newer
712                         String contentType =
713                                 signedAttrs.getSingleObjectIdentifierValue(
714                                         Pkcs7Constants.OID_CONTENT_TYPE);
715                         if (contentType == null) {
716                             throw new SignatureException("No Content Type in signed attributes");
717                         }
718                         if (!contentType.equals(signedData.encapContentInfo.contentType)) {
719                             // Did not verify: Content type signed attribute does not match
720                             // SignedData.encapContentInfo.eContentType. This fails verification of
721                             // this SignerInfo but should not prevent verification of other
722                             // SignerInfos. Hence, no exception is thrown.
723                             return null;
724                         }
725                     }
726                     byte[] expectedSignatureFileDigest =
727                             signedAttrs.getSingleOctetStringValue(
728                                     Pkcs7Constants.OID_MESSAGE_DIGEST);
729                     if (expectedSignatureFileDigest == null) {
730                         throw new SignatureException("No content digest in signed attributes");
731                     }
732                     byte[] actualSignatureFileDigest =
733                             MessageDigest.getInstance(
734                                     getJcaDigestAlgorithm(digestAlgorithmOid))
735                                     .digest(signatureFile);
736                     if (!Arrays.equals(
737                             expectedSignatureFileDigest, actualSignatureFileDigest)) {
738                         // Skip verification: signature file digest in signed attributes does not
739                         // match the signature file. This fails verification of
740                         // this SignerInfo but should not prevent verification of other
741                         // SignerInfos. Hence, no exception is thrown.
742                         return null;
743                     }
744                 } catch (Asn1DecodingException e) {
745                     throw new SignatureException("Failed to parse signed attributes", e);
746                 }
747                 // PKCS #7 requires that signature is over signed attributes re-encoded as
748                 // ASN.1 DER. However, Android does not re-encode except for changing the
749                 // first byte of encoded form from IMPLICIT [0] to UNIVERSAL SET. We do the
750                 // same for maximum compatibility.
751                 ByteBuffer signedAttrsOriginalEncoding = signerInfo.signedAttrs.getEncoded();
752                 s.update((byte) 0x31); // UNIVERSAL SET
753                 signedAttrsOriginalEncoding.position(1);
754                 s.update(signedAttrsOriginalEncoding);
755             } else {
756                 // No signed attributes present -- verify signature against the contents of the
757                 // signature file
758                 s.update(signatureFile);
759             }
760             byte[] sigBytes = ByteBufferUtils.toByteArray(signerInfo.signature.slice());
761             if (!s.verify(sigBytes)) {
762                 // Cryptographic signature did not verify. This fails verification of this
763                 // SignerInfo but should not prevent verification of other SignerInfos. Hence, no
764                 // exception is thrown.
765                 return null;
766             }
767             // Cryptographic signature verified
768             return signingCertificate;
769         }
770 
parseCertificates( List<Asn1OpaqueObject> encodedCertificates)771         private static List<X509Certificate> parseCertificates(
772                 List<Asn1OpaqueObject> encodedCertificates) throws CertificateException {
773             if (encodedCertificates.isEmpty()) {
774                 return Collections.emptyList();
775             }
776 
777             List<X509Certificate> result = new ArrayList<>(encodedCertificates.size());
778             for (int i = 0; i < encodedCertificates.size(); i++) {
779                 Asn1OpaqueObject encodedCertificate = encodedCertificates.get(i);
780                 X509Certificate certificate;
781                 byte[] encodedForm = ByteBufferUtils.toByteArray(encodedCertificate.getEncoded());
782                 try {
783                     certificate = X509CertificateUtils.generateCertificate(encodedForm);
784                 } catch (CertificateException e) {
785                     throw new CertificateException("Failed to parse certificate #" + (i + 1), e);
786                 }
787                 // Wrap the cert so that the result's getEncoded returns exactly the original
788                 // encoded form. Without this, getEncoded may return a different form from what was
789                 // stored in the signature. This is because some X509Certificate(Factory)
790                 // implementations re-encode certificates and/or some implementations of
791                 // X509Certificate.getEncoded() re-encode certificates.
792                 certificate = new GuaranteedEncodedFormX509Certificate(certificate, encodedForm);
793                 result.add(certificate);
794             }
795             return result;
796         }
797 
findCertificate( Collection<X509Certificate> certs, SignerIdentifier id)798         public static X509Certificate findCertificate(
799                 Collection<X509Certificate> certs, SignerIdentifier id) {
800             for (X509Certificate cert : certs) {
801                 if (isMatchingCerticicate(cert, id)) {
802                     return cert;
803                 }
804             }
805             return null;
806         }
807 
getCertificateChain( List<X509Certificate> certs, X509Certificate leaf)808         public static List<X509Certificate> getCertificateChain(
809                 List<X509Certificate> certs, X509Certificate leaf) {
810             List<X509Certificate> unusedCerts = new ArrayList<>(certs);
811             List<X509Certificate> result = new ArrayList<>(1);
812             result.add(leaf);
813             unusedCerts.remove(leaf);
814             X509Certificate root = leaf;
815             while (!root.getSubjectDN().equals(root.getIssuerDN())) {
816                 Principal targetDn = root.getIssuerDN();
817                 boolean issuerFound = false;
818                 for (int i = 0; i < unusedCerts.size(); i++) {
819                     X509Certificate unusedCert = unusedCerts.get(i);
820                     if (targetDn.equals(unusedCert.getSubjectDN())) {
821                         issuerFound = true;
822                         unusedCerts.remove(i);
823                         result.add(unusedCert);
824                         root = unusedCert;
825                         break;
826                     }
827                 }
828                 if (!issuerFound) {
829                     break;
830                 }
831             }
832             return result;
833         }
834 
isMatchingCerticicate(X509Certificate cert, SignerIdentifier id)835         private static boolean isMatchingCerticicate(X509Certificate cert, SignerIdentifier id) {
836             if (id.issuerAndSerialNumber == null) {
837                 // Android doesn't support any other means of identifying the signing certificate
838                 return false;
839             }
840             IssuerAndSerialNumber issuerAndSerialNumber = id.issuerAndSerialNumber;
841             byte[] encodedIssuer =
842                     ByteBufferUtils.toByteArray(issuerAndSerialNumber.issuer.getEncoded());
843             X500Principal idIssuer = new X500Principal(encodedIssuer);
844             BigInteger idSerialNumber = issuerAndSerialNumber.certificateSerialNumber;
845             return idSerialNumber.equals(cert.getSerialNumber())
846                     && idIssuer.equals(cert.getIssuerX500Principal());
847         }
848 
849         private static final String OID_DIGEST_MD5 = "1.2.840.113549.2.5";
850         static final String OID_DIGEST_SHA1 = "1.3.14.3.2.26";
851         private static final String OID_DIGEST_SHA224 = "2.16.840.1.101.3.4.2.4";
852         static final String OID_DIGEST_SHA256 = "2.16.840.1.101.3.4.2.1";
853         private static final String OID_DIGEST_SHA384 = "2.16.840.1.101.3.4.2.2";
854         private static final String OID_DIGEST_SHA512 = "2.16.840.1.101.3.4.2.3";
855 
856         static final String OID_SIG_RSA = "1.2.840.113549.1.1.1";
857         private static final String OID_SIG_MD5_WITH_RSA = "1.2.840.113549.1.1.4";
858         private static final String OID_SIG_SHA1_WITH_RSA = "1.2.840.113549.1.1.5";
859         private static final String OID_SIG_SHA224_WITH_RSA = "1.2.840.113549.1.1.14";
860         private static final String OID_SIG_SHA256_WITH_RSA = "1.2.840.113549.1.1.11";
861         private static final String OID_SIG_SHA384_WITH_RSA = "1.2.840.113549.1.1.12";
862         private static final String OID_SIG_SHA512_WITH_RSA = "1.2.840.113549.1.1.13";
863 
864         static final String OID_SIG_DSA = "1.2.840.10040.4.1";
865         private static final String OID_SIG_SHA1_WITH_DSA = "1.2.840.10040.4.3";
866         private static final String OID_SIG_SHA224_WITH_DSA = "2.16.840.1.101.3.4.3.1";
867         static final String OID_SIG_SHA256_WITH_DSA = "2.16.840.1.101.3.4.3.2";
868         static final String OID_SIG_SHA384_WITH_DSA = "2.16.840.1.101.3.4.3.3";
869         static final String OID_SIG_SHA512_WITH_DSA = "2.16.840.1.101.3.4.3.4";
870 
871         static final String OID_SIG_EC_PUBLIC_KEY = "1.2.840.10045.2.1";
872         private static final String OID_SIG_SHA1_WITH_ECDSA = "1.2.840.10045.4.1";
873         private static final String OID_SIG_SHA224_WITH_ECDSA = "1.2.840.10045.4.3.1";
874         private static final String OID_SIG_SHA256_WITH_ECDSA = "1.2.840.10045.4.3.2";
875         private static final String OID_SIG_SHA384_WITH_ECDSA = "1.2.840.10045.4.3.3";
876         private static final String OID_SIG_SHA512_WITH_ECDSA = "1.2.840.10045.4.3.4";
877 
878         private static final Map<String, List<InclusiveIntRange>> SUPPORTED_SIG_ALG_OIDS =
879                 new HashMap<>();
880         {
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_RSA, InclusiveIntRange.from(0))881             addSupportedSigAlg(
882                     OID_DIGEST_MD5, OID_SIG_RSA,
883                     InclusiveIntRange.from(0));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21))884             addSupportedSigAlg(
885                     OID_DIGEST_MD5, OID_SIG_MD5_WITH_RSA,
886                     InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.fromTo(21, 23))887             addSupportedSigAlg(
888                     OID_DIGEST_MD5, OID_SIG_SHA1_WITH_RSA,
889                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(21, 23))890             addSupportedSigAlg(
891                     OID_DIGEST_MD5, OID_SIG_SHA224_WITH_RSA,
892                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(21, 23))893             addSupportedSigAlg(
894                     OID_DIGEST_MD5, OID_SIG_SHA256_WITH_RSA,
895                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.fromTo(21, 23))896             addSupportedSigAlg(
897                     OID_DIGEST_MD5, OID_SIG_SHA384_WITH_RSA,
898                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.fromTo(21, 23))899             addSupportedSigAlg(
900                     OID_DIGEST_MD5, OID_SIG_SHA512_WITH_RSA,
901                     InclusiveIntRange.fromTo(21, 23));
902 
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_RSA, InclusiveIntRange.from(0))903             addSupportedSigAlg(
904                     OID_DIGEST_SHA1, OID_SIG_RSA,
905                     InclusiveIntRange.from(0));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(21, 23))906             addSupportedSigAlg(
907                     OID_DIGEST_SHA1, OID_SIG_MD5_WITH_RSA,
908                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.from(0))909             addSupportedSigAlg(
910                     OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_RSA,
911                     InclusiveIntRange.from(0));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(21, 23))912             addSupportedSigAlg(
913                     OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_RSA,
914                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(21, 23))915             addSupportedSigAlg(
916                     OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_RSA,
917                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.fromTo(21, 23))918             addSupportedSigAlg(
919                     OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_RSA,
920                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.fromTo(21, 23))921             addSupportedSigAlg(
922                     OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_RSA,
923                     InclusiveIntRange.fromTo(21, 23));
924 
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_RSA, InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21))925             addSupportedSigAlg(
926                     OID_DIGEST_SHA224, OID_SIG_RSA,
927                     InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(21, 23))928             addSupportedSigAlg(
929                     OID_DIGEST_SHA224, OID_SIG_MD5_WITH_RSA,
930                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.fromTo(21, 23))931             addSupportedSigAlg(
932                     OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_RSA,
933                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21))934             addSupportedSigAlg(
935                     OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_RSA,
936                     InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(21, 21))937             addSupportedSigAlg(
938                     OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_RSA,
939                     InclusiveIntRange.fromTo(21, 21));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.fromTo(21, 23))940             addSupportedSigAlg(
941                     OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_RSA,
942                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.fromTo(21, 23))943             addSupportedSigAlg(
944                     OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_RSA,
945                     InclusiveIntRange.fromTo(21, 23));
946 
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_RSA, InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18))947             addSupportedSigAlg(
948                     OID_DIGEST_SHA256, OID_SIG_RSA,
949                     InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(21, 23))950             addSupportedSigAlg(
951                     OID_DIGEST_SHA256, OID_SIG_MD5_WITH_RSA,
952                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.fromTo(21, 21))953             addSupportedSigAlg(
954                     OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_RSA,
955                     InclusiveIntRange.fromTo(21, 21));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(21, 23))956             addSupportedSigAlg(
957                     OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_RSA,
958                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18))959             addSupportedSigAlg(
960                     OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_RSA,
961                     InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.fromTo(21, 23))962             addSupportedSigAlg(
963                     OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_RSA,
964                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.fromTo(21, 23))965             addSupportedSigAlg(
966                     OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_RSA,
967                     InclusiveIntRange.fromTo(21, 23));
968 
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_RSA, InclusiveIntRange.from(18))969             addSupportedSigAlg(
970                     OID_DIGEST_SHA384, OID_SIG_RSA,
971                     InclusiveIntRange.from(18));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(21, 23))972             addSupportedSigAlg(
973                     OID_DIGEST_SHA384, OID_SIG_MD5_WITH_RSA,
974                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.fromTo(21, 23))975             addSupportedSigAlg(
976                     OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_RSA,
977                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(21, 23))978             addSupportedSigAlg(
979                     OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_RSA,
980                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(21, 23))981             addSupportedSigAlg(
982                     OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_RSA,
983                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.from(21))984             addSupportedSigAlg(
985                     OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_RSA,
986                     InclusiveIntRange.from(21));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.fromTo(21, 23))987             addSupportedSigAlg(
988                     OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_RSA,
989                     InclusiveIntRange.fromTo(21, 23));
990 
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_RSA, InclusiveIntRange.from(18))991             addSupportedSigAlg(
992                     OID_DIGEST_SHA512, OID_SIG_RSA,
993                     InclusiveIntRange.from(18));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(21, 23))994             addSupportedSigAlg(
995                     OID_DIGEST_SHA512, OID_SIG_MD5_WITH_RSA,
996                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.fromTo(21, 23))997             addSupportedSigAlg(
998                     OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_RSA,
999                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(21, 23))1000             addSupportedSigAlg(
1001                     OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_RSA,
1002                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(21, 23))1003             addSupportedSigAlg(
1004                     OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_RSA,
1005                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.fromTo(21, 21))1006             addSupportedSigAlg(
1007                     OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_RSA,
1008                     InclusiveIntRange.fromTo(21, 21));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.from(21))1009             addSupportedSigAlg(
1010                     OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_RSA,
1011                     InclusiveIntRange.from(21));
1012 
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1013             addSupportedSigAlg(
1014                     OID_DIGEST_MD5, OID_SIG_SHA1_WITH_DSA,
1015                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1016             addSupportedSigAlg(
1017                     OID_DIGEST_MD5, OID_SIG_SHA224_WITH_DSA,
1018                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1019             addSupportedSigAlg(
1020                     OID_DIGEST_MD5, OID_SIG_SHA256_WITH_DSA,
1021                     InclusiveIntRange.fromTo(21, 23));
1022 
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_DSA, InclusiveIntRange.from(0))1023             addSupportedSigAlg(
1024                     OID_DIGEST_SHA1, OID_SIG_DSA,
1025                     InclusiveIntRange.from(0));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.from(9))1026             addSupportedSigAlg(
1027                     OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_DSA,
1028                     InclusiveIntRange.from(9));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1029             addSupportedSigAlg(
1030                     OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_DSA,
1031                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1032             addSupportedSigAlg(
1033                     OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_DSA,
1034                     InclusiveIntRange.fromTo(21, 23));
1035 
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_DSA, InclusiveIntRange.from(22))1036             addSupportedSigAlg(
1037                     OID_DIGEST_SHA224, OID_SIG_DSA,
1038                     InclusiveIntRange.from(22));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1039             addSupportedSigAlg(
1040                     OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_DSA,
1041                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.from(21))1042             addSupportedSigAlg(
1043                     OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_DSA,
1044                     InclusiveIntRange.from(21));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1045             addSupportedSigAlg(
1046                     OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_DSA,
1047                     InclusiveIntRange.fromTo(21, 23));
1048 
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_DSA, InclusiveIntRange.from(22))1049             addSupportedSigAlg(
1050                     OID_DIGEST_SHA256, OID_SIG_DSA,
1051                     InclusiveIntRange.from(22));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1052             addSupportedSigAlg(
1053                     OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_DSA,
1054                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1055             addSupportedSigAlg(
1056                     OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_DSA,
1057                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.from(21))1058             addSupportedSigAlg(
1059                     OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_DSA,
1060                     InclusiveIntRange.from(21));
1061 
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1062             addSupportedSigAlg(
1063                     OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_DSA,
1064                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1065             addSupportedSigAlg(
1066                     OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_DSA,
1067                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1068             addSupportedSigAlg(
1069                     OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_DSA,
1070                     InclusiveIntRange.fromTo(21, 23));
1071 
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1072             addSupportedSigAlg(
1073                     OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_DSA,
1074                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1075             addSupportedSigAlg(
1076                     OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_DSA,
1077                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.fromTo(21, 23))1078             addSupportedSigAlg(
1079                     OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_DSA,
1080                     InclusiveIntRange.fromTo(21, 23));
1081 
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_EC_PUBLIC_KEY, InclusiveIntRange.from(18))1082             addSupportedSigAlg(
1083                     OID_DIGEST_SHA1, OID_SIG_EC_PUBLIC_KEY,
1084                     InclusiveIntRange.from(18));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_EC_PUBLIC_KEY, InclusiveIntRange.from(21))1085             addSupportedSigAlg(
1086                     OID_DIGEST_SHA224, OID_SIG_EC_PUBLIC_KEY,
1087                     InclusiveIntRange.from(21));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_EC_PUBLIC_KEY, InclusiveIntRange.from(18))1088             addSupportedSigAlg(
1089                     OID_DIGEST_SHA256, OID_SIG_EC_PUBLIC_KEY,
1090                     InclusiveIntRange.from(18));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_EC_PUBLIC_KEY, InclusiveIntRange.from(18))1091             addSupportedSigAlg(
1092                     OID_DIGEST_SHA384, OID_SIG_EC_PUBLIC_KEY,
1093                     InclusiveIntRange.from(18));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_EC_PUBLIC_KEY, InclusiveIntRange.from(18))1094             addSupportedSigAlg(
1095                     OID_DIGEST_SHA512, OID_SIG_EC_PUBLIC_KEY,
1096                     InclusiveIntRange.from(18));
1097 
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1098             addSupportedSigAlg(
1099                     OID_DIGEST_MD5, OID_SIG_SHA1_WITH_ECDSA,
1100                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1101             addSupportedSigAlg(
1102                     OID_DIGEST_MD5, OID_SIG_SHA224_WITH_ECDSA,
1103                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1104             addSupportedSigAlg(
1105                     OID_DIGEST_MD5, OID_SIG_SHA256_WITH_ECDSA,
1106                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1107             addSupportedSigAlg(
1108                     OID_DIGEST_MD5, OID_SIG_SHA384_WITH_ECDSA,
1109                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1110             addSupportedSigAlg(
1111                     OID_DIGEST_MD5, OID_SIG_SHA512_WITH_ECDSA,
1112                     InclusiveIntRange.fromTo(21, 23));
1113 
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.from(18))1114             addSupportedSigAlg(
1115                     OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_ECDSA,
1116                     InclusiveIntRange.from(18));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1117             addSupportedSigAlg(
1118                     OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_ECDSA,
1119                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1120             addSupportedSigAlg(
1121                     OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_ECDSA,
1122                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1123             addSupportedSigAlg(
1124                     OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_ECDSA,
1125                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1126             addSupportedSigAlg(
1127                     OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_ECDSA,
1128                     InclusiveIntRange.fromTo(21, 23));
1129 
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1130             addSupportedSigAlg(
1131                     OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_ECDSA,
1132                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.from(21))1133             addSupportedSigAlg(
1134                     OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_ECDSA,
1135                     InclusiveIntRange.from(21));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1136             addSupportedSigAlg(
1137                     OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_ECDSA,
1138                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1139             addSupportedSigAlg(
1140                     OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_ECDSA,
1141                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1142             addSupportedSigAlg(
1143                     OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_ECDSA,
1144                     InclusiveIntRange.fromTo(21, 23));
1145 
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1146             addSupportedSigAlg(
1147                     OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_ECDSA,
1148                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1149             addSupportedSigAlg(
1150                     OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_ECDSA,
1151                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.from(21))1152             addSupportedSigAlg(
1153                     OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_ECDSA,
1154                     InclusiveIntRange.from(21));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1155             addSupportedSigAlg(
1156                     OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_ECDSA,
1157                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1158             addSupportedSigAlg(
1159                     OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_ECDSA,
1160                     InclusiveIntRange.fromTo(21, 23));
1161 
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1162             addSupportedSigAlg(
1163                     OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_ECDSA,
1164                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1165             addSupportedSigAlg(
1166                     OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_ECDSA,
1167                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1168             addSupportedSigAlg(
1169                     OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_ECDSA,
1170                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.from(21))1171             addSupportedSigAlg(
1172                     OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_ECDSA,
1173                     InclusiveIntRange.from(21));
addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1174             addSupportedSigAlg(
1175                     OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_ECDSA,
1176                     InclusiveIntRange.fromTo(21, 23));
1177 
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1178             addSupportedSigAlg(
1179                     OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_ECDSA,
1180                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1181             addSupportedSigAlg(
1182                     OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_ECDSA,
1183                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1184             addSupportedSigAlg(
1185                     OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_ECDSA,
1186                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23))1187             addSupportedSigAlg(
1188                     OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_ECDSA,
1189                     InclusiveIntRange.fromTo(21, 23));
addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.from(21))1190             addSupportedSigAlg(
1191                     OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_ECDSA,
1192                     InclusiveIntRange.from(21));
1193         }
1194 
addSupportedSigAlg( String digestAlgorithmOid, String signatureAlgorithmOid, InclusiveIntRange... supportedApiLevels)1195         private static void addSupportedSigAlg(
1196                 String digestAlgorithmOid,
1197                 String signatureAlgorithmOid,
1198                 InclusiveIntRange... supportedApiLevels) {
1199             SUPPORTED_SIG_ALG_OIDS.put(
1200                     digestAlgorithmOid + "with" + signatureAlgorithmOid,
1201                     Arrays.asList(supportedApiLevels));
1202         }
1203 
getSigAlgSupportedApiLevels( String digestAlgorithmOid, String signatureAlgorithmOid)1204         private List<InclusiveIntRange> getSigAlgSupportedApiLevels(
1205                 String digestAlgorithmOid,
1206                 String signatureAlgorithmOid) {
1207             List<InclusiveIntRange> result =
1208                     SUPPORTED_SIG_ALG_OIDS.get(digestAlgorithmOid + "with" + signatureAlgorithmOid);
1209             return (result != null) ? result : Collections.emptyList();
1210         }
1211 
1212         private static class OidToUserFriendlyNameMapper {
OidToUserFriendlyNameMapper()1213             private OidToUserFriendlyNameMapper() {}
1214 
1215             private static final Map<String, String> OID_TO_USER_FRIENDLY_NAME = new HashMap<>();
1216             static {
OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_MD5, "MD5")1217                 OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_MD5, "MD5");
OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA1, "SHA-1")1218                 OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA1, "SHA-1");
OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA224, "SHA-224")1219                 OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA224, "SHA-224");
OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA256, "SHA-256")1220                 OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA256, "SHA-256");
OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA384, "SHA-384")1221                 OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA384, "SHA-384");
OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA512, "SHA-512")1222                 OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA512, "SHA-512");
1223 
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_RSA, "RSA")1224                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_RSA, "RSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_MD5_WITH_RSA, "MD5 with RSA")1225                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_MD5_WITH_RSA, "MD5 with RSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA1_WITH_RSA, "SHA-1 with RSA")1226                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA1_WITH_RSA, "SHA-1 with RSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA224_WITH_RSA, "SHA-224 with RSA")1227                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA224_WITH_RSA, "SHA-224 with RSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA256_WITH_RSA, "SHA-256 with RSA")1228                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA256_WITH_RSA, "SHA-256 with RSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA384_WITH_RSA, "SHA-384 with RSA")1229                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA384_WITH_RSA, "SHA-384 with RSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA512_WITH_RSA, "SHA-512 with RSA")1230                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA512_WITH_RSA, "SHA-512 with RSA");
1231 
1232 
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_DSA, "DSA")1233                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_DSA, "DSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA1_WITH_DSA, "SHA-1 with DSA")1234                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA1_WITH_DSA, "SHA-1 with DSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA224_WITH_DSA, "SHA-224 with DSA")1235                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA224_WITH_DSA, "SHA-224 with DSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA256_WITH_DSA, "SHA-256 with DSA")1236                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA256_WITH_DSA, "SHA-256 with DSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA384_WITH_DSA, "SHA-384 with DSA")1237                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA384_WITH_DSA, "SHA-384 with DSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA512_WITH_DSA, "SHA-512 with DSA")1238                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA512_WITH_DSA, "SHA-512 with DSA");
1239 
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_EC_PUBLIC_KEY, "ECDSA")1240                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_EC_PUBLIC_KEY, "ECDSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA1_WITH_ECDSA, "SHA-1 with ECDSA")1241                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA1_WITH_ECDSA, "SHA-1 with ECDSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA224_WITH_ECDSA, "SHA-224 with ECDSA")1242                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA224_WITH_ECDSA, "SHA-224 with ECDSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA256_WITH_ECDSA, "SHA-256 with ECDSA")1243                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA256_WITH_ECDSA, "SHA-256 with ECDSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA384_WITH_ECDSA, "SHA-384 with ECDSA")1244                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA384_WITH_ECDSA, "SHA-384 with ECDSA");
OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA512_WITH_ECDSA, "SHA-512 with ECDSA")1245                 OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA512_WITH_ECDSA, "SHA-512 with ECDSA");
1246             }
1247 
getUserFriendlyNameForOid(String oid)1248             public static String getUserFriendlyNameForOid(String oid) {
1249                 return OID_TO_USER_FRIENDLY_NAME.get(oid);
1250             }
1251         }
1252 
1253         private static final Map<String, String> OID_TO_JCA_DIGEST_ALG = new HashMap<>();
1254         static {
OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_MD5, "MD5")1255             OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_MD5, "MD5");
OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA1, "SHA-1")1256             OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA1, "SHA-1");
OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA224, "SHA-224")1257             OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA224, "SHA-224");
OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA256, "SHA-256")1258             OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA256, "SHA-256");
OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA384, "SHA-384")1259             OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA384, "SHA-384");
OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA512, "SHA-512")1260             OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA512, "SHA-512");
1261         }
1262 
getJcaDigestAlgorithm(String oid)1263         private static String getJcaDigestAlgorithm(String oid)
1264                 throws SignatureException {
1265             String result = OID_TO_JCA_DIGEST_ALG.get(oid);
1266             if (result == null) {
1267                 throw new SignatureException("Unsupported digest algorithm: " + oid);
1268             }
1269             return result;
1270         }
1271 
1272         private static final Map<String, String> OID_TO_JCA_SIGNATURE_ALG = new HashMap<>();
1273         static {
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_MD5_WITH_RSA, "MD5withRSA")1274             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_MD5_WITH_RSA, "MD5withRSA");
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA1_WITH_RSA, "SHA1withRSA")1275             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA1_WITH_RSA, "SHA1withRSA");
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA224_WITH_RSA, "SHA224withRSA")1276             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA224_WITH_RSA, "SHA224withRSA");
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA256_WITH_RSA, "SHA256withRSA")1277             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA256_WITH_RSA, "SHA256withRSA");
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA384_WITH_RSA, "SHA384withRSA")1278             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA384_WITH_RSA, "SHA384withRSA");
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA512_WITH_RSA, "SHA512withRSA")1279             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA512_WITH_RSA, "SHA512withRSA");
1280 
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA1_WITH_DSA, "SHA1withDSA")1281             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA1_WITH_DSA, "SHA1withDSA");
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA224_WITH_DSA, "SHA224withDSA")1282             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA224_WITH_DSA, "SHA224withDSA");
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA256_WITH_DSA, "SHA256withDSA")1283             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA256_WITH_DSA, "SHA256withDSA");
1284 
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA1_WITH_ECDSA, "SHA1withECDSA")1285             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA1_WITH_ECDSA, "SHA1withECDSA");
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA224_WITH_ECDSA, "SHA224withECDSA")1286             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA224_WITH_ECDSA, "SHA224withECDSA");
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA256_WITH_ECDSA, "SHA256withECDSA")1287             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA256_WITH_ECDSA, "SHA256withECDSA");
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA384_WITH_ECDSA, "SHA384withECDSA")1288             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA384_WITH_ECDSA, "SHA384withECDSA");
OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA512_WITH_ECDSA, "SHA512withECDSA")1289             OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA512_WITH_ECDSA, "SHA512withECDSA");
1290         }
1291 
getJcaSignatureAlgorithm( String digestAlgorithmOid, String signatureAlgorithmOid)1292         private static String getJcaSignatureAlgorithm(
1293                 String digestAlgorithmOid,
1294                 String signatureAlgorithmOid) throws SignatureException {
1295             // First check whether the signature algorithm OID alone is sufficient
1296             String result = OID_TO_JCA_SIGNATURE_ALG.get(signatureAlgorithmOid);
1297             if (result != null) {
1298                 return result;
1299             }
1300 
1301             // Signature algorithm OID alone is insufficient. Need to combine digest algorithm OID
1302             // with signature algorithm OID.
1303             String suffix;
1304             if (OID_SIG_RSA.equals(signatureAlgorithmOid)) {
1305                 suffix = "RSA";
1306             } else if (OID_SIG_DSA.equals(signatureAlgorithmOid)) {
1307                 suffix = "DSA";
1308             } else if (OID_SIG_EC_PUBLIC_KEY.equals(signatureAlgorithmOid)) {
1309                 suffix = "ECDSA";
1310             } else {
1311                 throw new SignatureException(
1312                         "Unsupported JCA Signature algorithm"
1313                                 + " . Digest algorithm: " + digestAlgorithmOid
1314                                 + ", signature algorithm: " + signatureAlgorithmOid);
1315             }
1316             String jcaDigestAlg = getJcaDigestAlgorithm(digestAlgorithmOid);
1317             // Canonical name for SHA-1 with ... is SHA1with, rather than SHA1. Same for all other
1318             // SHA algorithms.
1319             if (jcaDigestAlg.startsWith("SHA-")) {
1320                 jcaDigestAlg = "SHA" + jcaDigestAlg.substring("SHA-".length());
1321             }
1322             return jcaDigestAlg + "with" + suffix;
1323         }
1324 
verifySigFileAgainstManifest( byte[] manifestBytes, ManifestParser.Section manifestMainSection, Map<String, ManifestParser.Section> entryNameToManifestSection, Map<Integer, String> supportedApkSigSchemeNames, Set<Integer> foundApkSigSchemeIds, int minSdkVersion, int maxSdkVersion)1325         public void verifySigFileAgainstManifest(
1326                 byte[] manifestBytes,
1327                 ManifestParser.Section manifestMainSection,
1328                 Map<String, ManifestParser.Section> entryNameToManifestSection,
1329                 Map<Integer, String> supportedApkSigSchemeNames,
1330                 Set<Integer> foundApkSigSchemeIds,
1331                 int minSdkVersion,
1332                 int maxSdkVersion) throws NoSuchAlgorithmException {
1333             // Inspect the main section of the .SF file.
1334             ManifestParser sf = new ManifestParser(mSigFileBytes);
1335             ManifestParser.Section sfMainSection = sf.readSection();
1336             if (sfMainSection.getAttributeValue(Attributes.Name.SIGNATURE_VERSION) == null) {
1337                 mResult.addError(
1338                         Issue.JAR_SIG_MISSING_VERSION_ATTR_IN_SIG_FILE,
1339                         mSignatureFileEntry.getName());
1340                 setIgnored();
1341                 return;
1342             }
1343 
1344             if (maxSdkVersion >= AndroidSdkVersion.N) {
1345                 // Android N and newer rejects APKs whose .SF file says they were supposed to be
1346                 // signed with APK Signature Scheme v2 (or newer) and yet no such signature was
1347                 // found.
1348                 checkForStrippedApkSignatures(
1349                         sfMainSection, supportedApkSigSchemeNames, foundApkSigSchemeIds);
1350                 if (mResult.containsErrors()) {
1351                     return;
1352                 }
1353             }
1354 
1355             boolean createdBySigntool = false;
1356             String createdBy = sfMainSection.getAttributeValue("Created-By");
1357             if (createdBy != null) {
1358                 createdBySigntool = createdBy.indexOf("signtool") != -1;
1359             }
1360             boolean manifestDigestVerified =
1361                     verifyManifestDigest(
1362                             sfMainSection,
1363                             createdBySigntool,
1364                             manifestBytes,
1365                             minSdkVersion,
1366                             maxSdkVersion);
1367             if (!createdBySigntool) {
1368                 verifyManifestMainSectionDigest(
1369                         sfMainSection,
1370                         manifestMainSection,
1371                         manifestBytes,
1372                         minSdkVersion,
1373                         maxSdkVersion);
1374             }
1375             if (mResult.containsErrors()) {
1376                 return;
1377             }
1378 
1379             // Inspect per-entry sections of .SF file. Technically, if the digest of JAR manifest
1380             // verifies, per-entry sections should be ignored. However, most Android platform
1381             // implementations require that such sections exist.
1382             List<ManifestParser.Section> sfSections = sf.readAllSections();
1383             Set<String> sfEntryNames = new HashSet<>(sfSections.size());
1384             int sfSectionNumber = 0;
1385             for (ManifestParser.Section sfSection : sfSections) {
1386                 sfSectionNumber++;
1387                 String entryName = sfSection.getName();
1388                 if (entryName == null) {
1389                     mResult.addError(
1390                             Issue.JAR_SIG_UNNNAMED_SIG_FILE_SECTION,
1391                             mSignatureFileEntry.getName(),
1392                             sfSectionNumber);
1393                     setIgnored();
1394                     return;
1395                 }
1396                 if (!sfEntryNames.add(entryName)) {
1397                     mResult.addError(
1398                             Issue.JAR_SIG_DUPLICATE_SIG_FILE_SECTION,
1399                             mSignatureFileEntry.getName(),
1400                             entryName);
1401                     setIgnored();
1402                     return;
1403                 }
1404                 if (manifestDigestVerified) {
1405                     // No need to verify this entry's corresponding JAR manifest entry because the
1406                     // JAR manifest verifies in full.
1407                     continue;
1408                 }
1409                 // Whole-file digest of JAR manifest hasn't been verified. Thus, we need to verify
1410                 // the digest of the JAR manifest section corresponding to this .SF section.
1411                 ManifestParser.Section manifestSection = entryNameToManifestSection.get(entryName);
1412                 if (manifestSection == null) {
1413                     mResult.addError(
1414                             Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE,
1415                             entryName,
1416                             mSignatureFileEntry.getName());
1417                     setIgnored();
1418                     continue;
1419                 }
1420                 verifyManifestIndividualSectionDigest(
1421                         sfSection,
1422                         createdBySigntool,
1423                         manifestSection,
1424                         manifestBytes,
1425                         minSdkVersion,
1426                         maxSdkVersion);
1427             }
1428             mSigFileEntryNames = sfEntryNames;
1429         }
1430 
1431 
1432         /**
1433          * Returns {@code true} if the whole-file digest of the manifest against the main section of
1434          * the .SF file.
1435          */
verifyManifestDigest( ManifestParser.Section sfMainSection, boolean createdBySigntool, byte[] manifestBytes, int minSdkVersion, int maxSdkVersion)1436         private boolean verifyManifestDigest(
1437                 ManifestParser.Section sfMainSection,
1438                 boolean createdBySigntool,
1439                 byte[] manifestBytes,
1440                 int minSdkVersion,
1441                 int maxSdkVersion) throws NoSuchAlgorithmException {
1442             Collection<NamedDigest> expectedDigests =
1443                     getDigestsToVerify(
1444                             sfMainSection,
1445                             ((createdBySigntool) ? "-Digest" : "-Digest-Manifest"),
1446                             minSdkVersion,
1447                             maxSdkVersion);
1448             boolean digestFound = !expectedDigests.isEmpty();
1449             if (!digestFound) {
1450                 mResult.addWarning(
1451                         Issue.JAR_SIG_NO_MANIFEST_DIGEST_IN_SIG_FILE,
1452                         mSignatureFileEntry.getName());
1453                 return false;
1454             }
1455 
1456             boolean verified = true;
1457             for (NamedDigest expectedDigest : expectedDigests) {
1458                 String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
1459                 byte[] actual = digest(jcaDigestAlgorithm, manifestBytes);
1460                 byte[] expected = expectedDigest.digest;
1461                 if (!Arrays.equals(expected, actual)) {
1462                     mResult.addWarning(
1463                             Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY,
1464                             V1SchemeSigner.MANIFEST_ENTRY_NAME,
1465                             jcaDigestAlgorithm,
1466                             mSignatureFileEntry.getName(),
1467                             Base64.getEncoder().encodeToString(actual),
1468                             Base64.getEncoder().encodeToString(expected));
1469                     verified = false;
1470                 }
1471             }
1472             return verified;
1473         }
1474 
1475         /**
1476          * Verifies the digest of the manifest's main section against the main section of the .SF
1477          * file.
1478          */
verifyManifestMainSectionDigest( ManifestParser.Section sfMainSection, ManifestParser.Section manifestMainSection, byte[] manifestBytes, int minSdkVersion, int maxSdkVersion)1479         private void verifyManifestMainSectionDigest(
1480                 ManifestParser.Section sfMainSection,
1481                 ManifestParser.Section manifestMainSection,
1482                 byte[] manifestBytes,
1483                 int minSdkVersion,
1484                 int maxSdkVersion) throws NoSuchAlgorithmException {
1485             Collection<NamedDigest> expectedDigests =
1486                     getDigestsToVerify(
1487                             sfMainSection,
1488                             "-Digest-Manifest-Main-Attributes",
1489                             minSdkVersion,
1490                             maxSdkVersion);
1491             if (expectedDigests.isEmpty()) {
1492                 return;
1493             }
1494 
1495             for (NamedDigest expectedDigest : expectedDigests) {
1496                 String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
1497                 byte[] actual =
1498                         digest(
1499                                 jcaDigestAlgorithm,
1500                                 manifestBytes,
1501                                 manifestMainSection.getStartOffset(),
1502                                 manifestMainSection.getSizeBytes());
1503                 byte[] expected = expectedDigest.digest;
1504                 if (!Arrays.equals(expected, actual)) {
1505                     mResult.addError(
1506                             Issue.JAR_SIG_MANIFEST_MAIN_SECTION_DIGEST_DID_NOT_VERIFY,
1507                             jcaDigestAlgorithm,
1508                             mSignatureFileEntry.getName(),
1509                             Base64.getEncoder().encodeToString(actual),
1510                             Base64.getEncoder().encodeToString(expected));
1511                 }
1512             }
1513         }
1514 
1515         /**
1516          * Verifies the digest of the manifest's individual section against the corresponding
1517          * individual section of the .SF file.
1518          */
verifyManifestIndividualSectionDigest( ManifestParser.Section sfIndividualSection, boolean createdBySigntool, ManifestParser.Section manifestIndividualSection, byte[] manifestBytes, int minSdkVersion, int maxSdkVersion)1519         private void verifyManifestIndividualSectionDigest(
1520                 ManifestParser.Section sfIndividualSection,
1521                 boolean createdBySigntool,
1522                 ManifestParser.Section manifestIndividualSection,
1523                 byte[] manifestBytes,
1524                 int minSdkVersion,
1525                 int maxSdkVersion) throws NoSuchAlgorithmException {
1526             String entryName = sfIndividualSection.getName();
1527             Collection<NamedDigest> expectedDigests =
1528                     getDigestsToVerify(
1529                             sfIndividualSection, "-Digest", minSdkVersion, maxSdkVersion);
1530             if (expectedDigests.isEmpty()) {
1531                 mResult.addError(
1532                         Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE,
1533                         entryName,
1534                         mSignatureFileEntry.getName());
1535                 return;
1536             }
1537 
1538             int sectionStartIndex = manifestIndividualSection.getStartOffset();
1539             int sectionSizeBytes = manifestIndividualSection.getSizeBytes();
1540             if (createdBySigntool) {
1541                 int sectionEndIndex = sectionStartIndex + sectionSizeBytes;
1542                 if ((manifestBytes[sectionEndIndex - 1] == '\n')
1543                         && (manifestBytes[sectionEndIndex - 2] == '\n')) {
1544                     sectionSizeBytes--;
1545                 }
1546             }
1547             for (NamedDigest expectedDigest : expectedDigests) {
1548                 String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
1549                 byte[] actual =
1550                         digest(
1551                                 jcaDigestAlgorithm,
1552                                 manifestBytes,
1553                                 sectionStartIndex,
1554                                 sectionSizeBytes);
1555                 byte[] expected = expectedDigest.digest;
1556                 if (!Arrays.equals(expected, actual)) {
1557                     mResult.addError(
1558                             Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY,
1559                             entryName,
1560                             jcaDigestAlgorithm,
1561                             mSignatureFileEntry.getName(),
1562                             Base64.getEncoder().encodeToString(actual),
1563                             Base64.getEncoder().encodeToString(expected));
1564                 }
1565             }
1566         }
1567 
checkForStrippedApkSignatures( ManifestParser.Section sfMainSection, Map<Integer, String> supportedApkSigSchemeNames, Set<Integer> foundApkSigSchemeIds)1568         private void checkForStrippedApkSignatures(
1569                 ManifestParser.Section sfMainSection,
1570                 Map<Integer, String> supportedApkSigSchemeNames,
1571                 Set<Integer> foundApkSigSchemeIds) {
1572             String signedWithApkSchemes =
1573                     sfMainSection.getAttributeValue(
1574                             V1SchemeSigner.SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME_STR);
1575             // This field contains a comma-separated list of APK signature scheme IDs which were
1576             // used to sign this APK. Android rejects APKs where an ID is known to the platform but
1577             // the APK didn't verify using that scheme.
1578 
1579             if (signedWithApkSchemes == null) {
1580                 // APK signature (e.g., v2 scheme) stripping protections not enabled.
1581                 if (!foundApkSigSchemeIds.isEmpty()) {
1582                     // APK is signed with an APK signature scheme such as v2 scheme.
1583                     mResult.addWarning(
1584                             Issue.JAR_SIG_NO_APK_SIG_STRIP_PROTECTION,
1585                             mSignatureFileEntry.getName());
1586                 }
1587                 return;
1588             }
1589 
1590             if (supportedApkSigSchemeNames.isEmpty()) {
1591                 return;
1592             }
1593 
1594             Set<Integer> supportedApkSigSchemeIds = supportedApkSigSchemeNames.keySet();
1595             Set<Integer> supportedExpectedApkSigSchemeIds = new HashSet<>(1);
1596             StringTokenizer tokenizer = new StringTokenizer(signedWithApkSchemes, ",");
1597             while (tokenizer.hasMoreTokens()) {
1598                 String idText = tokenizer.nextToken().trim();
1599                 if (idText.isEmpty()) {
1600                     continue;
1601                 }
1602                 int id;
1603                 try {
1604                     id = Integer.parseInt(idText);
1605                 } catch (Exception ignored) {
1606                     continue;
1607                 }
1608                 // This APK was supposed to be signed with the APK signature scheme having
1609                 // this ID.
1610                 if (supportedApkSigSchemeIds.contains(id)) {
1611                     supportedExpectedApkSigSchemeIds.add(id);
1612                 } else {
1613                     mResult.addWarning(
1614                             Issue.JAR_SIG_UNKNOWN_APK_SIG_SCHEME_ID,
1615                             mSignatureFileEntry.getName(),
1616                             id);
1617                 }
1618             }
1619 
1620             for (int id : supportedExpectedApkSigSchemeIds) {
1621                 if (!foundApkSigSchemeIds.contains(id)) {
1622                     String apkSigSchemeName = supportedApkSigSchemeNames.get(id);
1623                     mResult.addError(
1624                             Issue.JAR_SIG_MISSING_APK_SIG_REFERENCED,
1625                             mSignatureFileEntry.getName(),
1626                             id,
1627                             apkSigSchemeName);
1628                 }
1629             }
1630         }
1631     }
1632 
getDigestsToVerify( ManifestParser.Section section, String digestAttrSuffix, int minSdkVersion, int maxSdkVersion)1633     public static Collection<NamedDigest> getDigestsToVerify(
1634             ManifestParser.Section section,
1635             String digestAttrSuffix,
1636             int minSdkVersion,
1637             int maxSdkVersion) {
1638         Decoder base64Decoder = Base64.getDecoder();
1639         List<NamedDigest> result = new ArrayList<>(1);
1640         if (minSdkVersion < AndroidSdkVersion.JELLY_BEAN_MR2) {
1641             // Prior to JB MR2, Android platform's logic for picking a digest algorithm to verify is
1642             // to rely on the ancient Digest-Algorithms attribute which contains
1643             // whitespace-separated list of digest algorithms (defaulting to SHA-1) to try. The
1644             // first digest attribute (with supported digest algorithm) found using the list is
1645             // used.
1646             String algs = section.getAttributeValue("Digest-Algorithms");
1647             if (algs == null) {
1648                 algs = "SHA SHA1";
1649             }
1650             StringTokenizer tokens = new StringTokenizer(algs);
1651             while (tokens.hasMoreTokens()) {
1652                 String alg = tokens.nextToken();
1653                 String attrName = alg + digestAttrSuffix;
1654                 String digestBase64 = section.getAttributeValue(attrName);
1655                 if (digestBase64 == null) {
1656                     // Attribute not found
1657                     continue;
1658                 }
1659                 alg = getCanonicalJcaMessageDigestAlgorithm(alg);
1660                 if ((alg == null)
1661                         || (getMinSdkVersionFromWhichSupportedInManifestOrSignatureFile(alg)
1662                                 > minSdkVersion)) {
1663                     // Unsupported digest algorithm
1664                     continue;
1665                 }
1666                 // Supported digest algorithm
1667                 result.add(new NamedDigest(alg, base64Decoder.decode(digestBase64)));
1668                 break;
1669             }
1670             // No supported digests found -- this will fail to verify on pre-JB MR2 Androids.
1671             if (result.isEmpty()) {
1672                 return result;
1673             }
1674         }
1675 
1676         if (maxSdkVersion >= AndroidSdkVersion.JELLY_BEAN_MR2) {
1677             // On JB MR2 and newer, Android platform picks the strongest algorithm out of:
1678             // SHA-512, SHA-384, SHA-256, SHA-1.
1679             for (String alg : JB_MR2_AND_NEWER_DIGEST_ALGS) {
1680                 String attrName = getJarDigestAttributeName(alg, digestAttrSuffix);
1681                 String digestBase64 = section.getAttributeValue(attrName);
1682                 if (digestBase64 == null) {
1683                     // Attribute not found
1684                     continue;
1685                 }
1686                 byte[] digest = base64Decoder.decode(digestBase64);
1687                 byte[] digestInResult = getDigest(result, alg);
1688                 if ((digestInResult == null) || (!Arrays.equals(digestInResult, digest))) {
1689                     result.add(new NamedDigest(alg, digest));
1690                 }
1691                 break;
1692             }
1693         }
1694 
1695         return result;
1696     }
1697 
1698     private static final String[] JB_MR2_AND_NEWER_DIGEST_ALGS = {
1699             "SHA-512",
1700             "SHA-384",
1701             "SHA-256",
1702             "SHA-1",
1703     };
1704 
getCanonicalJcaMessageDigestAlgorithm(String algorithm)1705     private static String getCanonicalJcaMessageDigestAlgorithm(String algorithm) {
1706         return UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.get(algorithm.toUpperCase(Locale.US));
1707     }
1708 
getMinSdkVersionFromWhichSupportedInManifestOrSignatureFile( String jcaAlgorithmName)1709     public static int getMinSdkVersionFromWhichSupportedInManifestOrSignatureFile(
1710             String jcaAlgorithmName) {
1711         Integer result =
1712                 MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.get(
1713                         jcaAlgorithmName.toUpperCase(Locale.US));
1714         return (result != null) ? result : Integer.MAX_VALUE;
1715     }
1716 
getJarDigestAttributeName( String jcaDigestAlgorithm, String attrNameSuffix)1717     private static String getJarDigestAttributeName(
1718             String jcaDigestAlgorithm, String attrNameSuffix) {
1719         if ("SHA-1".equalsIgnoreCase(jcaDigestAlgorithm)) {
1720             return "SHA1" + attrNameSuffix;
1721         } else {
1722             return jcaDigestAlgorithm + attrNameSuffix;
1723         }
1724     }
1725 
1726     private static final Map<String, String> UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL;
1727     static {
1728         UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL = new HashMap<>(8);
1729         UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("MD5", "MD5");
1730         UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA", "SHA-1");
1731         UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA1", "SHA-1");
1732         UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-1", "SHA-1");
1733         UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-256", "SHA-256");
1734         UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-384", "SHA-384");
1735         UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-512", "SHA-512");
1736     }
1737 
1738     private static final Map<String, Integer>
1739             MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST;
1740     static {
1741         MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST = new HashMap<>(5);
1742         MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("MD5", 0);
1743         MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("SHA-1", 0);
1744         MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("SHA-256", 0);
1745         MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put(
1746                 "SHA-384", AndroidSdkVersion.GINGERBREAD);
1747         MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put(
1748                 "SHA-512", AndroidSdkVersion.GINGERBREAD);
1749     }
1750 
getDigest(Collection<NamedDigest> digests, String jcaDigestAlgorithm)1751     private static byte[] getDigest(Collection<NamedDigest> digests, String jcaDigestAlgorithm) {
1752         for (NamedDigest digest : digests) {
1753             if (digest.jcaDigestAlgorithm.equalsIgnoreCase(jcaDigestAlgorithm)) {
1754                 return digest.digest;
1755             }
1756         }
1757         return null;
1758     }
1759 
parseZipCentralDirectory( DataSource apk, ApkUtils.ZipSections apkSections)1760     public static List<CentralDirectoryRecord> parseZipCentralDirectory(
1761             DataSource apk,
1762             ApkUtils.ZipSections apkSections)
1763                     throws IOException, ApkFormatException {
1764         // Read the ZIP Central Directory
1765         long cdSizeBytes = apkSections.getZipCentralDirectorySizeBytes();
1766         if (cdSizeBytes > Integer.MAX_VALUE) {
1767             throw new ApkFormatException("ZIP Central Directory too large: " + cdSizeBytes);
1768         }
1769         long cdOffset = apkSections.getZipCentralDirectoryOffset();
1770         ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes);
1771         cd.order(ByteOrder.LITTLE_ENDIAN);
1772 
1773         // Parse the ZIP Central Directory
1774         int expectedCdRecordCount = apkSections.getZipCentralDirectoryRecordCount();
1775         List<CentralDirectoryRecord> cdRecords = new ArrayList<>(expectedCdRecordCount);
1776         for (int i = 0; i < expectedCdRecordCount; i++) {
1777             CentralDirectoryRecord cdRecord;
1778             int offsetInsideCd = cd.position();
1779             try {
1780                 cdRecord = CentralDirectoryRecord.getRecord(cd);
1781             } catch (ZipFormatException e) {
1782                 throw new ApkFormatException(
1783                         "Malformed ZIP Central Directory record #" + (i + 1)
1784                                 + " at file offset " + (cdOffset + offsetInsideCd),
1785                         e);
1786             }
1787             String entryName = cdRecord.getName();
1788             if (entryName.endsWith("/")) {
1789                 // Ignore directory entries
1790                 continue;
1791             }
1792             cdRecords.add(cdRecord);
1793         }
1794         // There may be more data in Central Directory, but we don't warn or throw because Android
1795         // ignores unused CD data.
1796 
1797         return cdRecords;
1798     }
1799 
1800     /**
1801      * Returns {@code true} if the provided JAR entry must be mentioned in signed JAR archive's
1802      * manifest for the APK to verify on Android.
1803      */
isJarEntryDigestNeededInManifest(String entryName)1804     private static boolean isJarEntryDigestNeededInManifest(String entryName) {
1805         // NOTE: This logic is different from what's required by the JAR signing scheme. This is
1806         // because Android's APK verification logic differs from that spec. In particular, JAR
1807         // signing spec includes into JAR manifest all files in subdirectories of META-INF and
1808         // any files inside META-INF not related to signatures.
1809         if (entryName.startsWith("META-INF/")) {
1810             return false;
1811         }
1812         return !entryName.endsWith("/");
1813     }
1814 
verifyJarEntriesAgainstManifestAndSigners( DataSource apk, long cdOffsetInApk, Collection<CentralDirectoryRecord> cdRecords, Map<String, ManifestParser.Section> entryNameToManifestSection, List<Signer> signers, int minSdkVersion, int maxSdkVersion, Result result)1815     private static Set<Signer> verifyJarEntriesAgainstManifestAndSigners(
1816             DataSource apk,
1817             long cdOffsetInApk,
1818             Collection<CentralDirectoryRecord> cdRecords,
1819             Map<String, ManifestParser.Section> entryNameToManifestSection,
1820             List<Signer> signers,
1821             int minSdkVersion,
1822             int maxSdkVersion,
1823             Result result) throws ApkFormatException, IOException, NoSuchAlgorithmException {
1824         // Iterate over APK contents as sequentially as possible to improve performance.
1825         List<CentralDirectoryRecord> cdRecordsSortedByLocalFileHeaderOffset =
1826                 new ArrayList<>(cdRecords);
1827         Collections.sort(
1828                 cdRecordsSortedByLocalFileHeaderOffset,
1829                 CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR);
1830         Set<String> manifestEntryNamesMissingFromApk =
1831                 new HashSet<>(entryNameToManifestSection.keySet());
1832         List<Signer> firstSignedEntrySigners = null;
1833         String firstSignedEntryName = null;
1834         for (CentralDirectoryRecord cdRecord : cdRecordsSortedByLocalFileHeaderOffset) {
1835             String entryName = cdRecord.getName();
1836             manifestEntryNamesMissingFromApk.remove(entryName);
1837             if (!isJarEntryDigestNeededInManifest(entryName)) {
1838                 continue;
1839             }
1840 
1841             ManifestParser.Section manifestSection = entryNameToManifestSection.get(entryName);
1842             if (manifestSection == null) {
1843                 result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName);
1844                 continue;
1845             }
1846 
1847             List<Signer> entrySigners = new ArrayList<>(signers.size());
1848             for (Signer signer : signers) {
1849                 if (signer.getSigFileEntryNames().contains(entryName)) {
1850                     entrySigners.add(signer);
1851                 }
1852             }
1853             if (entrySigners.isEmpty()) {
1854                 result.addError(Issue.JAR_SIG_ZIP_ENTRY_NOT_SIGNED, entryName);
1855                 continue;
1856             }
1857             if (firstSignedEntrySigners == null) {
1858                 firstSignedEntrySigners = entrySigners;
1859                 firstSignedEntryName = entryName;
1860             } else if (!entrySigners.equals(firstSignedEntrySigners)) {
1861                 result.addError(
1862                         Issue.JAR_SIG_ZIP_ENTRY_SIGNERS_MISMATCH,
1863                         firstSignedEntryName,
1864                         getSignerNames(firstSignedEntrySigners),
1865                         entryName,
1866                         getSignerNames(entrySigners));
1867                 continue;
1868             }
1869 
1870             List<NamedDigest> expectedDigests =
1871                     new ArrayList<>(
1872                             getDigestsToVerify(
1873                                     manifestSection, "-Digest", minSdkVersion, maxSdkVersion));
1874             if (expectedDigests.isEmpty()) {
1875                 result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName);
1876                 continue;
1877             }
1878 
1879             MessageDigest[] mds = new MessageDigest[expectedDigests.size()];
1880             for (int i = 0; i < expectedDigests.size(); i++) {
1881                 mds[i] = getMessageDigest(expectedDigests.get(i).jcaDigestAlgorithm);
1882             }
1883 
1884             try {
1885                 LocalFileRecord.outputUncompressedData(
1886                         apk,
1887                         cdRecord,
1888                         cdOffsetInApk,
1889                         DataSinks.asDataSink(mds));
1890             } catch (ZipFormatException e) {
1891                 throw new ApkFormatException("Malformed ZIP entry: " + entryName, e);
1892             } catch (IOException e) {
1893                 throw new IOException("Failed to read entry: " + entryName, e);
1894             }
1895 
1896             for (int i = 0; i < expectedDigests.size(); i++) {
1897                 NamedDigest expectedDigest = expectedDigests.get(i);
1898                 byte[] actualDigest = mds[i].digest();
1899                 if (!Arrays.equals(expectedDigest.digest, actualDigest)) {
1900                     result.addError(
1901                             Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY,
1902                             entryName,
1903                             expectedDigest.jcaDigestAlgorithm,
1904                             V1SchemeSigner.MANIFEST_ENTRY_NAME,
1905                             Base64.getEncoder().encodeToString(actualDigest),
1906                             Base64.getEncoder().encodeToString(expectedDigest.digest));
1907                 }
1908             }
1909         }
1910 
1911         if (firstSignedEntrySigners == null) {
1912             result.addError(Issue.JAR_SIG_NO_SIGNED_ZIP_ENTRIES);
1913             return Collections.emptySet();
1914         } else {
1915             return new HashSet<>(firstSignedEntrySigners);
1916         }
1917     }
1918 
getSignerNames(List<Signer> signers)1919     private static List<String> getSignerNames(List<Signer> signers) {
1920         if (signers.isEmpty()) {
1921             return Collections.emptyList();
1922         }
1923         List<String> result = new ArrayList<>(signers.size());
1924         for (Signer signer : signers) {
1925             result.add(signer.getName());
1926         }
1927         return result;
1928     }
1929 
getMessageDigest(String algorithm)1930     private static MessageDigest getMessageDigest(String algorithm)
1931             throws NoSuchAlgorithmException {
1932         return MessageDigest.getInstance(algorithm);
1933     }
1934 
digest(String algorithm, byte[] data, int offset, int length)1935     private static byte[] digest(String algorithm, byte[] data, int offset, int length)
1936             throws NoSuchAlgorithmException {
1937         MessageDigest md = getMessageDigest(algorithm);
1938         md.update(data, offset, length);
1939         return md.digest();
1940     }
1941 
digest(String algorithm, byte[] data)1942     private static byte[] digest(String algorithm, byte[] data) throws NoSuchAlgorithmException {
1943         return getMessageDigest(algorithm).digest(data);
1944     }
1945 
1946     public static class NamedDigest {
1947         public final String jcaDigestAlgorithm;
1948         public final byte[] digest;
1949 
NamedDigest(String jcaDigestAlgorithm, byte[] digest)1950         private NamedDigest(String jcaDigestAlgorithm, byte[] digest) {
1951             this.jcaDigestAlgorithm = jcaDigestAlgorithm;
1952             this.digest = digest;
1953         }
1954     }
1955 
1956     public static class Result {
1957 
1958         /** Whether the APK's JAR signature verifies. */
1959         public boolean verified;
1960 
1961         /** List of APK's signers. These signers are used by Android. */
1962         public final List<SignerInfo> signers = new ArrayList<>();
1963 
1964         /**
1965          * Signers encountered in the APK but not included in the set of the APK's signers. These
1966          * signers are ignored by Android.
1967          */
1968         public final List<SignerInfo> ignoredSigners = new ArrayList<>();
1969 
1970         private final List<IssueWithParams> mWarnings = new ArrayList<>();
1971         private final List<IssueWithParams> mErrors = new ArrayList<>();
1972 
containsErrors()1973         private boolean containsErrors() {
1974             if (!mErrors.isEmpty()) {
1975                 return true;
1976             }
1977             for (SignerInfo signer : signers) {
1978                 if (signer.containsErrors()) {
1979                     return true;
1980                 }
1981             }
1982             return false;
1983         }
1984 
addError(Issue msg, Object... parameters)1985         private void addError(Issue msg, Object... parameters) {
1986             mErrors.add(new IssueWithParams(msg, parameters));
1987         }
1988 
addWarning(Issue msg, Object... parameters)1989         private void addWarning(Issue msg, Object... parameters) {
1990             mWarnings.add(new IssueWithParams(msg, parameters));
1991         }
1992 
getErrors()1993         public List<IssueWithParams> getErrors() {
1994             return mErrors;
1995         }
1996 
getWarnings()1997         public List<IssueWithParams> getWarnings() {
1998             return mWarnings;
1999         }
2000 
2001         public static class SignerInfo {
2002             public final String name;
2003             public final String signatureFileName;
2004             public final String signatureBlockFileName;
2005             public final List<X509Certificate> certChain = new ArrayList<>();
2006 
2007             private final List<IssueWithParams> mWarnings = new ArrayList<>();
2008             private final List<IssueWithParams> mErrors = new ArrayList<>();
2009 
SignerInfo( String name, String signatureBlockFileName, String signatureFileName)2010             private SignerInfo(
2011                     String name, String signatureBlockFileName, String signatureFileName) {
2012                 this.name = name;
2013                 this.signatureBlockFileName = signatureBlockFileName;
2014                 this.signatureFileName = signatureFileName;
2015             }
2016 
containsErrors()2017             private boolean containsErrors() {
2018                 return !mErrors.isEmpty();
2019             }
2020 
addError(Issue msg, Object... parameters)2021             private void addError(Issue msg, Object... parameters) {
2022                 mErrors.add(new IssueWithParams(msg, parameters));
2023             }
2024 
addWarning(Issue msg, Object... parameters)2025             private void addWarning(Issue msg, Object... parameters) {
2026                 mWarnings.add(new IssueWithParams(msg, parameters));
2027             }
2028 
getErrors()2029             public List<IssueWithParams> getErrors() {
2030                 return mErrors;
2031             }
2032 
getWarnings()2033             public List<IssueWithParams> getWarnings() {
2034                 return mWarnings;
2035             }
2036         }
2037     }
2038 
2039     private static class SignedAttributes {
2040         private Map<String, List<Asn1OpaqueObject>> mAttrs;
2041 
SignedAttributes(Collection<Attribute> attrs)2042         public SignedAttributes(Collection<Attribute> attrs) throws Pkcs7DecodingException {
2043             Map<String, List<Asn1OpaqueObject>> result = new HashMap<>(attrs.size());
2044             for (Attribute attr : attrs) {
2045                 if (result.put(attr.attrType, attr.attrValues) != null) {
2046                     throw new Pkcs7DecodingException("Duplicate signed attribute: " + attr.attrType);
2047                 }
2048             }
2049             mAttrs = result;
2050         }
2051 
getSingleValue(String attrOid)2052         private Asn1OpaqueObject getSingleValue(String attrOid) throws Pkcs7DecodingException {
2053             List<Asn1OpaqueObject> values = mAttrs.get(attrOid);
2054             if ((values == null) || (values.isEmpty())) {
2055                 return null;
2056             }
2057             if (values.size() > 1) {
2058                 throw new Pkcs7DecodingException("Attribute " + attrOid + " has multiple values");
2059             }
2060             return values.get(0);
2061         }
2062 
getSingleObjectIdentifierValue(String attrOid)2063         public String getSingleObjectIdentifierValue(String attrOid) throws Pkcs7DecodingException {
2064             Asn1OpaqueObject value = getSingleValue(attrOid);
2065             if (value == null) {
2066                 return null;
2067             }
2068             try {
2069                 return Asn1BerParser.parse(value.getEncoded(), ObjectIdentifierChoice.class).value;
2070             } catch (Asn1DecodingException e) {
2071                 throw new Pkcs7DecodingException("Failed to decode OBJECT IDENTIFIER", e);
2072             }
2073         }
2074 
getSingleOctetStringValue(String attrOid)2075         public byte[] getSingleOctetStringValue(String attrOid) throws Pkcs7DecodingException {
2076             Asn1OpaqueObject value = getSingleValue(attrOid);
2077             if (value == null) {
2078                 return null;
2079             }
2080             try {
2081                 return Asn1BerParser.parse(value.getEncoded(), OctetStringChoice.class).value;
2082             } catch (Asn1DecodingException e) {
2083                 throw new Pkcs7DecodingException("Failed to decode OBJECT IDENTIFIER", e);
2084             }
2085         }
2086     }
2087 
2088     @Asn1Class(type = Asn1Type.CHOICE)
2089     public static class OctetStringChoice {
2090         @Asn1Field(type = Asn1Type.OCTET_STRING)
2091         public byte[] value;
2092     }
2093 
2094     @Asn1Class(type = Asn1Type.CHOICE)
2095     public static class ObjectIdentifierChoice {
2096         @Asn1Field(type = Asn1Type.OBJECT_IDENTIFIER)
2097         public String value;
2098     }
2099 }
2100