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