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