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