1 /* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.util.jar; 27 28 import java.io.*; 29 import java.net.URL; 30 import java.util.*; 31 import java.security.*; 32 import java.security.cert.CertificateException; 33 import java.util.zip.ZipEntry; 34 35 import sun.misc.JarIndex; 36 import sun.security.util.ManifestDigester; 37 import sun.security.util.ManifestEntryVerifier; 38 import sun.security.util.SignatureFileVerifier; 39 import sun.security.util.Debug; 40 41 /** 42 * 43 * @author Roland Schemers 44 */ 45 class JarVerifier { 46 47 /* Are we debugging ? */ 48 static final Debug debug = Debug.getInstance("jar"); 49 50 /* a table mapping names to code signers, for jar entries that have 51 had their actual hashes verified */ 52 private Hashtable<String, CodeSigner[]> verifiedSigners; 53 54 /* a table mapping names to code signers, for jar entries that have 55 passed the .SF/.DSA/.EC -> MANIFEST check */ 56 private Hashtable<String, CodeSigner[]> sigFileSigners; 57 58 /* a hash table to hold .SF bytes */ 59 private Hashtable<String, byte[]> sigFileData; 60 61 /** "queue" of pending PKCS7 blocks that we couldn't parse 62 * until we parsed the .SF file */ 63 private ArrayList<SignatureFileVerifier> pendingBlocks; 64 65 /* cache of CodeSigner objects */ 66 private ArrayList<CodeSigner[]> signerCache; 67 68 /* Are we parsing a block? */ 69 private boolean parsingBlockOrSF = false; 70 71 /* Are we done parsing META-INF entries? */ 72 private boolean parsingMeta = true; 73 74 /* Are there are files to verify? */ 75 private boolean anyToVerify = true; 76 77 /* The output stream to use when keeping track of files we are interested 78 in */ 79 private ByteArrayOutputStream baos; 80 81 /** The ManifestDigester object */ 82 private volatile ManifestDigester manDig; 83 84 /** the bytes for the manDig object */ 85 byte manifestRawBytes[] = null; 86 87 /** controls eager signature validation */ 88 boolean eagerValidation; 89 90 /** makes code source singleton instances unique to us */ 91 private Object csdomain = new Object(); 92 93 /** collect -DIGEST-MANIFEST values for blacklist */ 94 private List<Object> manifestDigests; 95 96 public JarVerifier(byte rawBytes[]) { 97 manifestRawBytes = rawBytes; 98 sigFileSigners = new Hashtable<>(); 99 verifiedSigners = new Hashtable<>(); 100 sigFileData = new Hashtable<>(11); 101 pendingBlocks = new ArrayList<>(); 102 baos = new ByteArrayOutputStream(); 103 manifestDigests = new ArrayList<>(); 104 } 105 106 /** 107 * This method scans to see which entry we're parsing and 108 * keeps various state information depending on what type of 109 * file is being parsed. 110 */ 111 public void beginEntry(JarEntry je, ManifestEntryVerifier mev) 112 throws IOException 113 { 114 if (je == null) 115 return; 116 117 if (debug != null) { 118 debug.println("beginEntry "+je.getName()); 119 } 120 121 String name = je.getName(); 122 123 /* 124 * Assumptions: 125 * 1. The manifest should be the first entry in the META-INF directory. 126 * 2. The .SF/.DSA/.EC files follow the manifest, before any normal entries 127 * 3. Any of the following will throw a SecurityException: 128 * a. digest mismatch between a manifest section and 129 * the SF section. 130 * b. digest mismatch between the actual jar entry and the manifest 131 */ 132 133 if (parsingMeta) { 134 String uname = name.toUpperCase(Locale.ENGLISH); 135 if ((uname.startsWith("META-INF/") || 136 uname.startsWith("/META-INF/"))) { 137 138 if (je.isDirectory()) { 139 mev.setEntry(null, je); 140 return; 141 } 142 143 if (uname.equals(JarFile.MANIFEST_NAME) || 144 uname.equals(JarIndex.INDEX_NAME)) { 145 return; 146 } 147 148 if (SignatureFileVerifier.isBlockOrSF(uname)) { 149 /* We parse only DSA, RSA or EC PKCS7 blocks. */ 150 parsingBlockOrSF = true; 151 baos.reset(); 152 mev.setEntry(null, je); 153 return; 154 } 155 156 // If a META-INF entry is not MF or block or SF, they should 157 // be normal entries. According to 2 above, no more block or 158 // SF will appear. Let's doneWithMeta. 159 } 160 } 161 162 if (parsingMeta) { 163 doneWithMeta(); 164 } 165 166 if (je.isDirectory()) { 167 mev.setEntry(null, je); 168 return; 169 } 170 171 // be liberal in what you accept. If the name starts with ./, remove 172 // it as we internally canonicalize it with out the ./. 173 if (name.startsWith("./")) 174 name = name.substring(2); 175 176 // be liberal in what you accept. If the name starts with /, remove 177 // it as we internally canonicalize it with out the /. 178 if (name.startsWith("/")) 179 name = name.substring(1); 180 181 // only set the jev object for entries that have a signature 182 // (either verified or not) 183 if (sigFileSigners.get(name) != null || 184 verifiedSigners.get(name) != null) { 185 mev.setEntry(name, je); 186 return; 187 } 188 189 // don't compute the digest for this entry 190 mev.setEntry(null, je); 191 192 return; 193 } 194 195 /** 196 * update a single byte. 197 */ 198 199 public void update(int b, ManifestEntryVerifier mev) 200 throws IOException 201 { 202 if (b != -1) { 203 if (parsingBlockOrSF) { 204 baos.write(b); 205 } else { 206 mev.update((byte)b); 207 } 208 } else { 209 processEntry(mev); 210 } 211 } 212 213 /** 214 * update an array of bytes. 215 */ 216 217 public void update(int n, byte[] b, int off, int len, 218 ManifestEntryVerifier mev) 219 throws IOException 220 { 221 if (n != -1) { 222 if (parsingBlockOrSF) { 223 baos.write(b, off, n); 224 } else { 225 mev.update(b, off, n); 226 } 227 } else { 228 processEntry(mev); 229 } 230 } 231 232 /** 233 * called when we reach the end of entry in one of the read() methods. 234 */ 235 private void processEntry(ManifestEntryVerifier mev) 236 throws IOException 237 { 238 if (!parsingBlockOrSF) { 239 JarEntry je = mev.getEntry(); 240 if ((je != null) && (je.signers == null)) { 241 je.signers = mev.verify(verifiedSigners, sigFileSigners); 242 je.certs = mapSignersToCertArray(je.signers); 243 } 244 } else { 245 246 try { 247 parsingBlockOrSF = false; 248 249 if (debug != null) { 250 debug.println("processEntry: processing block"); 251 } 252 253 String uname = mev.getEntry().getName() 254 .toUpperCase(Locale.ENGLISH); 255 256 if (uname.endsWith(".SF")) { 257 String key = uname.substring(0, uname.length()-3); 258 byte bytes[] = baos.toByteArray(); 259 // add to sigFileData in case future blocks need it 260 sigFileData.put(key, bytes); 261 // check pending blocks, we can now process 262 // anyone waiting for this .SF file 263 for (SignatureFileVerifier sfv : pendingBlocks) { 264 if (sfv.needSignatureFile(key)) { 265 if (debug != null) { 266 debug.println( 267 "processEntry: processing pending block"); 268 } 269 270 sfv.setSignatureFile(bytes); 271 sfv.process(sigFileSigners, manifestDigests); 272 } 273 } 274 return; 275 } 276 277 // now we are parsing a signature block file 278 279 String key = uname.substring(0, uname.lastIndexOf('.')); 280 281 if (signerCache == null) 282 signerCache = new ArrayList<>(); 283 284 if (manDig == null) { 285 synchronized(manifestRawBytes) { 286 if (manDig == null) { 287 manDig = new ManifestDigester(manifestRawBytes); 288 manifestRawBytes = null; 289 } 290 } 291 } 292 293 SignatureFileVerifier sfv = 294 new SignatureFileVerifier(signerCache, 295 manDig, uname, baos.toByteArray()); 296 297 if (sfv.needSignatureFileBytes()) { 298 // see if we have already parsed an external .SF file 299 byte[] bytes = sigFileData.get(key); 300 301 if (bytes == null) { 302 // put this block on queue for later processing 303 // since we don't have the .SF bytes yet 304 // (uname, block); 305 if (debug != null) { 306 debug.println("adding pending block"); 307 } 308 pendingBlocks.add(sfv); 309 return; 310 } else { 311 sfv.setSignatureFile(bytes); 312 } 313 } 314 sfv.process(sigFileSigners, manifestDigests); 315 316 } catch (IOException | CertificateException | 317 NoSuchAlgorithmException | SignatureException e) { 318 if (debug != null) debug.println("processEntry caught: "+e); 319 // ignore and treat as unsigned 320 } 321 } 322 } 323 324 /** 325 * Return an array of java.security.cert.Certificate objects for 326 * the given file in the jar. 327 * @deprecated 328 */ 329 @Deprecated 330 public java.security.cert.Certificate[] getCerts(String name) 331 { 332 return mapSignersToCertArray(getCodeSigners(name)); 333 } 334 335 public java.security.cert.Certificate[] getCerts(JarFile jar, JarEntry entry) 336 { 337 return mapSignersToCertArray(getCodeSigners(jar, entry)); 338 } 339 340 /** 341 * return an array of CodeSigner objects for 342 * the given file in the jar. this array is not cloned. 343 * 344 */ 345 public CodeSigner[] getCodeSigners(String name) 346 { 347 return verifiedSigners.get(name); 348 } 349 350 public CodeSigner[] getCodeSigners(JarFile jar, JarEntry entry) 351 { 352 String name = entry.getName(); 353 if (eagerValidation && sigFileSigners.get(name) != null) { 354 /* 355 * Force a read of the entry data to generate the 356 * verification hash. 357 */ 358 try { 359 InputStream s = jar.getInputStream(entry); 360 byte[] buffer = new byte[1024]; 361 int n = buffer.length; 362 while (n != -1) { 363 n = s.read(buffer, 0, buffer.length); 364 } 365 s.close(); 366 } catch (IOException e) { 367 } 368 } 369 return getCodeSigners(name); 370 } 371 372 /* 373 * Convert an array of signers into an array of concatenated certificate 374 * arrays. 375 */ 376 private static java.security.cert.Certificate[] mapSignersToCertArray( 377 CodeSigner[] signers) { 378 379 if (signers != null) { 380 ArrayList<java.security.cert.Certificate> certChains = new ArrayList<>(); 381 for (CodeSigner signer : signers) { 382 certChains.addAll( 383 signer.getSignerCertPath().getCertificates()); 384 } 385 386 // Convert into a Certificate[] 387 return certChains.toArray( 388 new java.security.cert.Certificate[certChains.size()]); 389 } 390 return null; 391 } 392 393 /** 394 * returns true if there no files to verify. 395 * should only be called after all the META-INF entries 396 * have been processed. 397 */ 398 boolean nothingToVerify() 399 { 400 return (anyToVerify == false); 401 } 402 403 /** 404 * called to let us know we have processed all the 405 * META-INF entries, and if we re-read one of them, don't 406 * re-process it. Also gets rid of any data structures 407 * we needed when parsing META-INF entries. 408 */ 409 void doneWithMeta() 410 { 411 parsingMeta = false; 412 anyToVerify = !sigFileSigners.isEmpty(); 413 baos = null; 414 sigFileData = null; 415 pendingBlocks = null; 416 signerCache = null; 417 manDig = null; 418 // MANIFEST.MF is always treated as signed and verified, 419 // move its signers from sigFileSigners to verifiedSigners. 420 if (sigFileSigners.containsKey(JarFile.MANIFEST_NAME)) { 421 CodeSigner[] codeSigners = sigFileSigners.remove(JarFile.MANIFEST_NAME); 422 verifiedSigners.put(JarFile.MANIFEST_NAME, codeSigners); 423 } 424 } 425 426 static class VerifierStream extends java.io.InputStream { 427 428 private InputStream is; 429 private JarVerifier jv; 430 private ManifestEntryVerifier mev; 431 private long numLeft; 432 433 VerifierStream(Manifest man, 434 JarEntry je, 435 InputStream is, 436 JarVerifier jv) throws IOException 437 { 438 this.is = is; 439 this.jv = jv; 440 this.mev = new ManifestEntryVerifier(man); 441 this.jv.beginEntry(je, mev); 442 this.numLeft = je.getSize(); 443 if (this.numLeft == 0) 444 this.jv.update(-1, this.mev); 445 } 446 447 public int read() throws IOException 448 { 449 if (numLeft > 0) { 450 int b = is.read(); 451 jv.update(b, mev); 452 numLeft--; 453 if (numLeft == 0) 454 jv.update(-1, mev); 455 return b; 456 } else { 457 return -1; 458 } 459 } 460 461 public int read(byte b[], int off, int len) throws IOException { 462 if ((numLeft > 0) && (numLeft < len)) { 463 len = (int)numLeft; 464 } 465 466 if (numLeft > 0) { 467 int n = is.read(b, off, len); 468 jv.update(n, b, off, len, mev); 469 numLeft -= n; 470 if (numLeft == 0) 471 jv.update(-1, b, off, len, mev); 472 return n; 473 } else { 474 return -1; 475 } 476 } 477 478 public void close() 479 throws IOException 480 { 481 if (is != null) 482 is.close(); 483 is = null; 484 mev = null; 485 jv = null; 486 } 487 488 public int available() throws IOException { 489 return is.available(); 490 } 491 492 } 493 494 // Extended JavaUtilJarAccess CodeSource API Support 495 496 private Map<URL, Map<CodeSigner[], CodeSource>> urlToCodeSourceMap = new HashMap<>(); 497 private Map<CodeSigner[], CodeSource> signerToCodeSource = new HashMap<>(); 498 private URL lastURL; 499 private Map<CodeSigner[], CodeSource> lastURLMap; 500 501 /* 502 * Create a unique mapping from codeSigner cache entries to CodeSource. 503 * In theory, multiple URLs origins could map to a single locally cached 504 * and shared JAR file although in practice there will be a single URL in use. 505 */ 506 private synchronized CodeSource mapSignersToCodeSource(URL url, CodeSigner[] signers) { 507 Map<CodeSigner[], CodeSource> map; 508 if (url == lastURL) { 509 map = lastURLMap; 510 } else { 511 map = urlToCodeSourceMap.get(url); 512 if (map == null) { 513 map = new HashMap<>(); 514 urlToCodeSourceMap.put(url, map); 515 } 516 lastURLMap = map; 517 lastURL = url; 518 } 519 CodeSource cs = map.get(signers); 520 if (cs == null) { 521 cs = new VerifierCodeSource(csdomain, url, signers); 522 signerToCodeSource.put(signers, cs); 523 } 524 return cs; 525 } 526 527 private CodeSource[] mapSignersToCodeSources(URL url, List<CodeSigner[]> signers, boolean unsigned) { 528 List<CodeSource> sources = new ArrayList<>(); 529 530 for (CodeSigner[] signer : signers) { 531 sources.add(mapSignersToCodeSource(url, signer)); 532 } 533 if (unsigned) { 534 sources.add(mapSignersToCodeSource(url, null)); 535 } 536 return sources.toArray(new CodeSource[sources.size()]); 537 } 538 private CodeSigner[] emptySigner = new CodeSigner[0]; 539 540 /* 541 * Match CodeSource to a CodeSigner[] in the signer cache. 542 */ 543 private CodeSigner[] findMatchingSigners(CodeSource cs) { 544 if (cs instanceof VerifierCodeSource) { 545 VerifierCodeSource vcs = (VerifierCodeSource) cs; 546 if (vcs.isSameDomain(csdomain)) { 547 return ((VerifierCodeSource) cs).getPrivateSigners(); 548 } 549 } 550 551 /* 552 * In practice signers should always be optimized above 553 * but this handles a CodeSource of any type, just in case. 554 */ 555 CodeSource[] sources = mapSignersToCodeSources(cs.getLocation(), getJarCodeSigners(), true); 556 List<CodeSource> sourceList = new ArrayList<>(); 557 for (CodeSource source : sources) { 558 sourceList.add(source); 559 } 560 int j = sourceList.indexOf(cs); 561 if (j != -1) { 562 CodeSigner[] match; 563 match = ((VerifierCodeSource) sourceList.get(j)).getPrivateSigners(); 564 if (match == null) { 565 match = emptySigner; 566 } 567 return match; 568 } 569 return null; 570 } 571 572 /* 573 * Instances of this class hold uncopied references to internal 574 * signing data that can be compared by object reference identity. 575 */ 576 private static class VerifierCodeSource extends CodeSource { 577 private static final long serialVersionUID = -9047366145967768825L; 578 579 URL vlocation; 580 CodeSigner[] vsigners; 581 java.security.cert.Certificate[] vcerts; 582 Object csdomain; 583 584 VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) { 585 super(location, signers); 586 this.csdomain = csdomain; 587 vlocation = location; 588 vsigners = signers; // from signerCache 589 } 590 591 VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs) { 592 super(location, certs); 593 this.csdomain = csdomain; 594 vlocation = location; 595 vcerts = certs; // from signerCache 596 } 597 598 /* 599 * All VerifierCodeSource instances are constructed based on 600 * singleton signerCache or signerCacheCert entries for each unique signer. 601 * No CodeSigner<->Certificate[] conversion is required. 602 * We use these assumptions to optimize equality comparisons. 603 */ 604 public boolean equals(Object obj) { 605 if (obj == this) { 606 return true; 607 } 608 if (obj instanceof VerifierCodeSource) { 609 VerifierCodeSource that = (VerifierCodeSource) obj; 610 611 /* 612 * Only compare against other per-signer singletons constructed 613 * on behalf of the same JarFile instance. Otherwise, compare 614 * things the slower way. 615 */ 616 if (isSameDomain(that.csdomain)) { 617 if (that.vsigners != this.vsigners 618 || that.vcerts != this.vcerts) { 619 return false; 620 } 621 if (that.vlocation != null) { 622 return that.vlocation.equals(this.vlocation); 623 } else if (this.vlocation != null) { 624 return this.vlocation.equals(that.vlocation); 625 } else { // both null 626 return true; 627 } 628 } 629 } 630 return super.equals(obj); 631 } 632 633 boolean isSameDomain(Object csdomain) { 634 return this.csdomain == csdomain; 635 } 636 637 private CodeSigner[] getPrivateSigners() { 638 return vsigners; 639 } 640 641 private java.security.cert.Certificate[] getPrivateCertificates() { 642 return vcerts; 643 } 644 } 645 private Map<String, CodeSigner[]> signerMap; 646 647 private synchronized Map<String, CodeSigner[]> signerMap() { 648 if (signerMap == null) { 649 /* 650 * Snapshot signer state so it doesn't change on us. We care 651 * only about the asserted signatures. Verification of 652 * signature validity happens via the JarEntry apis. 653 */ 654 signerMap = new HashMap<>(verifiedSigners.size() + sigFileSigners.size()); 655 signerMap.putAll(verifiedSigners); 656 signerMap.putAll(sigFileSigners); 657 } 658 return signerMap; 659 } 660 661 public synchronized Enumeration<String> entryNames(JarFile jar, final CodeSource[] cs) { 662 final Map<String, CodeSigner[]> map = signerMap(); 663 final Iterator<Map.Entry<String, CodeSigner[]>> itor = map.entrySet().iterator(); 664 boolean matchUnsigned = false; 665 666 /* 667 * Grab a single copy of the CodeSigner arrays. Check 668 * to see if we can optimize CodeSigner equality test. 669 */ 670 List<CodeSigner[]> req = new ArrayList<>(cs.length); 671 for (CodeSource c : cs) { 672 CodeSigner[] match = findMatchingSigners(c); 673 if (match != null) { 674 if (match.length > 0) { 675 req.add(match); 676 } else { 677 matchUnsigned = true; 678 } 679 } else { 680 matchUnsigned = true; 681 } 682 } 683 684 final List<CodeSigner[]> signersReq = req; 685 final Enumeration<String> enum2 = (matchUnsigned) ? unsignedEntryNames(jar) : emptyEnumeration; 686 687 return new Enumeration<String>() { 688 689 String name; 690 691 public boolean hasMoreElements() { 692 if (name != null) { 693 return true; 694 } 695 696 while (itor.hasNext()) { 697 Map.Entry<String, CodeSigner[]> e = itor.next(); 698 if (signersReq.contains(e.getValue())) { 699 name = e.getKey(); 700 return true; 701 } 702 } 703 while (enum2.hasMoreElements()) { 704 name = enum2.nextElement(); 705 return true; 706 } 707 return false; 708 } 709 710 public String nextElement() { 711 if (hasMoreElements()) { 712 String value = name; 713 name = null; 714 return value; 715 } 716 throw new NoSuchElementException(); 717 } 718 }; 719 } 720 721 /* 722 * Like entries() but screens out internal JAR mechanism entries 723 * and includes signed entries with no ZIP data. 724 */ 725 public Enumeration<JarEntry> entries2(final JarFile jar, Enumeration<? extends ZipEntry> e) { 726 final Map<String, CodeSigner[]> map = new HashMap<>(); 727 map.putAll(signerMap()); 728 final Enumeration<? extends ZipEntry> enum_ = e; 729 return new Enumeration<JarEntry>() { 730 731 Enumeration<String> signers = null; 732 JarEntry entry; 733 734 public boolean hasMoreElements() { 735 if (entry != null) { 736 return true; 737 } 738 while (enum_.hasMoreElements()) { 739 ZipEntry ze = enum_.nextElement(); 740 if (JarVerifier.isSigningRelated(ze.getName())) { 741 continue; 742 } 743 entry = jar.newEntry(ze); 744 return true; 745 } 746 if (signers == null) { 747 signers = Collections.enumeration(map.keySet()); 748 } 749 while (signers.hasMoreElements()) { 750 String name = signers.nextElement(); 751 entry = jar.newEntry(new ZipEntry(name)); 752 return true; 753 } 754 755 // Any map entries left? 756 return false; 757 } 758 759 public JarEntry nextElement() { 760 if (hasMoreElements()) { 761 JarEntry je = entry; 762 map.remove(je.getName()); 763 entry = null; 764 return je; 765 } 766 throw new NoSuchElementException(); 767 } 768 }; 769 } 770 private Enumeration<String> emptyEnumeration = new Enumeration<String>() { 771 772 public boolean hasMoreElements() { 773 return false; 774 } 775 776 public String nextElement() { 777 throw new NoSuchElementException(); 778 } 779 }; 780 781 // true if file is part of the signature mechanism itself 782 static boolean isSigningRelated(String name) { 783 return SignatureFileVerifier.isSigningRelated(name); 784 } 785 786 private Enumeration<String> unsignedEntryNames(JarFile jar) { 787 final Map<String, CodeSigner[]> map = signerMap(); 788 final Enumeration<JarEntry> entries = jar.entries(); 789 return new Enumeration<String>() { 790 791 String name; 792 793 /* 794 * Grab entries from ZIP directory but screen out 795 * metadata. 796 */ 797 public boolean hasMoreElements() { 798 if (name != null) { 799 return true; 800 } 801 while (entries.hasMoreElements()) { 802 String value; 803 ZipEntry e = entries.nextElement(); 804 value = e.getName(); 805 if (e.isDirectory() || isSigningRelated(value)) { 806 continue; 807 } 808 if (map.get(value) == null) { 809 name = value; 810 return true; 811 } 812 } 813 return false; 814 } 815 816 public String nextElement() { 817 if (hasMoreElements()) { 818 String value = name; 819 name = null; 820 return value; 821 } 822 throw new NoSuchElementException(); 823 } 824 }; 825 } 826 private List<CodeSigner[]> jarCodeSigners; 827 828 private synchronized List<CodeSigner[]> getJarCodeSigners() { 829 CodeSigner[] signers; 830 if (jarCodeSigners == null) { 831 HashSet<CodeSigner[]> set = new HashSet<>(); 832 set.addAll(signerMap().values()); 833 jarCodeSigners = new ArrayList<>(); 834 jarCodeSigners.addAll(set); 835 } 836 return jarCodeSigners; 837 } 838 839 public synchronized CodeSource[] getCodeSources(JarFile jar, URL url) { 840 boolean hasUnsigned = unsignedEntryNames(jar).hasMoreElements(); 841 842 return mapSignersToCodeSources(url, getJarCodeSigners(), hasUnsigned); 843 } 844 845 public CodeSource getCodeSource(URL url, String name) { 846 CodeSigner[] signers; 847 848 signers = signerMap().get(name); 849 return mapSignersToCodeSource(url, signers); 850 } 851 852 public CodeSource getCodeSource(URL url, JarFile jar, JarEntry je) { 853 CodeSigner[] signers; 854 855 return mapSignersToCodeSource(url, getCodeSigners(jar, je)); 856 } 857 858 public void setEagerValidation(boolean eager) { 859 eagerValidation = eager; 860 } 861 862 public synchronized List<Object> getManifestDigests() { 863 return Collections.unmodifiableList(manifestDigests); 864 } 865 866 static CodeSource getUnsignedCS(URL url) { 867 return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null); 868 } 869 }