1 /* 2 * Copyright (c) 1997, 2012, 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.security.util.ManifestDigester; 36 import sun.security.util.ManifestEntryVerifier; 37 import sun.security.util.SignatureFileVerifier; 38 import sun.security.util.Debug; 39 40 /** 41 * 42 * @author Roland Schemers 43 */ 44 class JarVerifier { 45 46 /* Are we debugging ? */ 47 static final Debug debug = Debug.getInstance("jar"); 48 49 /* a table mapping names to code signers, for jar entries that have 50 had their actual hashes verified */ 51 private Hashtable<String, CodeSigner[]> verifiedSigners; 52 53 /* a table mapping names to code signers, for jar entries that have 54 passed the .SF/.DSA/.EC -> MANIFEST check */ 55 private Hashtable<String, CodeSigner[]> sigFileSigners; 56 57 /* a hash table to hold .SF bytes */ 58 private Hashtable<String, byte[]> sigFileData; 59 60 /** "queue" of pending PKCS7 blocks that we couldn't parse 61 * until we parsed the .SF file */ 62 private ArrayList<SignatureFileVerifier> pendingBlocks; 63 64 /* cache of CodeSigner objects */ 65 private ArrayList<CodeSigner[]> signerCache; 66 67 /* Are we parsing a block? */ 68 private boolean parsingBlockOrSF = false; 69 70 /* Are we done parsing META-INF entries? */ 71 private boolean parsingMeta = true; 72 73 /* Are there are files to verify? */ 74 private boolean anyToVerify = true; 75 76 /* The output stream to use when keeping track of files we are interested 77 in */ 78 private ByteArrayOutputStream baos; 79 80 /** The ManifestDigester object */ 81 private volatile ManifestDigester manDig; 82 83 /** the bytes for the manDig object */ 84 byte manifestRawBytes[] = null; 85 86 /** controls eager signature validation */ 87 boolean eagerValidation; 88 89 /** makes code source singleton instances unique to us */ 90 private Object csdomain = new Object(); 91 92 /** collect -DIGEST-MANIFEST values for blacklist */ 93 private List<Object> manifestDigests; 94 95 public JarVerifier(byte rawBytes[]) { 96 manifestRawBytes = rawBytes; 97 sigFileSigners = new Hashtable<>(); 98 verifiedSigners = new Hashtable<>(); 99 sigFileData = new Hashtable<>(11); 100 pendingBlocks = new ArrayList<>(); 101 baos = new ByteArrayOutputStream(); 102 manifestDigests = new ArrayList<>(); 103 } 104 105 /** 106 * This method scans to see which entry we're parsing and 107 * keeps various state information depending on what type of 108 * file is being parsed. 109 */ 110 public void beginEntry(JarEntry je, ManifestEntryVerifier mev) 111 throws IOException 112 { 113 if (je == null) 114 return; 115 116 if (debug != null) { 117 debug.println("beginEntry "+je.getName()); 118 } 119 120 String name = je.getName(); 121 122 /* 123 * Assumptions: 124 * 1. The manifest should be the first entry in the META-INF directory. 125 * 2. The .SF/.DSA/.EC files follow the manifest, before any normal entries 126 * 3. Any of the following will throw a SecurityException: 127 * a. digest mismatch between a manifest section and 128 * the SF section. 129 * b. digest mismatch between the actual jar entry and the manifest 130 */ 131 132 if (parsingMeta) { 133 String uname = name.toUpperCase(Locale.ENGLISH); 134 if ((uname.startsWith("META-INF/") || 135 uname.startsWith("/META-INF/"))) { 136 137 if (je.isDirectory()) { 138 mev.setEntry(null, je); 139 return; 140 } 141 142 if (SignatureFileVerifier.isBlockOrSF(uname)) { 143 /* We parse only DSA, RSA or EC PKCS7 blocks. */ 144 parsingBlockOrSF = true; 145 baos.reset(); 146 mev.setEntry(null, je); 147 } 148 return; 149 } 150 } 151 152 if (parsingMeta) { 153 doneWithMeta(); 154 } 155 156 if (je.isDirectory()) { 157 mev.setEntry(null, je); 158 return; 159 } 160 161 // be liberal in what you accept. If the name starts with ./, remove 162 // it as we internally canonicalize it with out the ./. 163 if (name.startsWith("./")) 164 name = name.substring(2); 165 166 // be liberal in what you accept. If the name starts with /, remove 167 // it as we internally canonicalize it with out the /. 168 if (name.startsWith("/")) 169 name = name.substring(1); 170 171 // only set the jev object for entries that have a signature 172 if (sigFileSigners.get(name) != null) { 173 mev.setEntry(name, je); 174 return; 175 } 176 177 // don't compute the digest for this entry 178 mev.setEntry(null, je); 179 180 return; 181 } 182 183 /** 184 * update a single byte. 185 */ 186 187 public void update(int b, ManifestEntryVerifier mev) 188 throws IOException 189 { 190 if (b != -1) { 191 if (parsingBlockOrSF) { 192 baos.write(b); 193 } else { 194 mev.update((byte)b); 195 } 196 } else { 197 processEntry(mev); 198 } 199 } 200 201 /** 202 * update an array of bytes. 203 */ 204 205 public void update(int n, byte[] b, int off, int len, 206 ManifestEntryVerifier mev) 207 throws IOException 208 { 209 if (n != -1) { 210 if (parsingBlockOrSF) { 211 baos.write(b, off, n); 212 } else { 213 mev.update(b, off, n); 214 } 215 } else { 216 processEntry(mev); 217 } 218 } 219 220 /** 221 * called when we reach the end of entry in one of the read() methods. 222 */ 223 private void processEntry(ManifestEntryVerifier mev) 224 throws IOException 225 { 226 if (!parsingBlockOrSF) { 227 JarEntry je = mev.getEntry(); 228 if ((je != null) && (je.signers == null)) { 229 je.signers = mev.verify(verifiedSigners, sigFileSigners); 230 je.certs = mapSignersToCertArray(je.signers); 231 } 232 } else { 233 234 try { 235 parsingBlockOrSF = false; 236 237 if (debug != null) { 238 debug.println("processEntry: processing block"); 239 } 240 241 String uname = mev.getEntry().getName() 242 .toUpperCase(Locale.ENGLISH); 243 244 if (uname.endsWith(".SF")) { 245 String key = uname.substring(0, uname.length()-3); 246 byte bytes[] = baos.toByteArray(); 247 // add to sigFileData in case future blocks need it 248 sigFileData.put(key, bytes); 249 // check pending blocks, we can now process 250 // anyone waiting for this .SF file 251 Iterator<SignatureFileVerifier> it = pendingBlocks.iterator(); 252 while (it.hasNext()) { 253 SignatureFileVerifier sfv = it.next(); 254 if (sfv.needSignatureFile(key)) { 255 if (debug != null) { 256 debug.println( 257 "processEntry: processing pending block"); 258 } 259 260 sfv.setSignatureFile(bytes); 261 sfv.process(sigFileSigners, manifestDigests); 262 } 263 } 264 return; 265 } 266 267 // now we are parsing a signature block file 268 269 String key = uname.substring(0, uname.lastIndexOf(".")); 270 271 if (signerCache == null) 272 signerCache = new ArrayList<>(); 273 274 if (manDig == null) { 275 synchronized(manifestRawBytes) { 276 if (manDig == null) { 277 manDig = new ManifestDigester(manifestRawBytes); 278 manifestRawBytes = null; 279 } 280 } 281 } 282 283 SignatureFileVerifier sfv = 284 new SignatureFileVerifier(signerCache, 285 manDig, uname, baos.toByteArray()); 286 287 if (sfv.needSignatureFileBytes()) { 288 // see if we have already parsed an external .SF file 289 byte[] bytes = sigFileData.get(key); 290 291 if (bytes == null) { 292 // put this block on queue for later processing 293 // since we don't have the .SF bytes yet 294 // (uname, block); 295 if (debug != null) { 296 debug.println("adding pending block"); 297 } 298 pendingBlocks.add(sfv); 299 return; 300 } else { 301 sfv.setSignatureFile(bytes); 302 } 303 } 304 sfv.process(sigFileSigners, manifestDigests); 305 306 } catch (IOException ioe) { 307 // e.g. sun.security.pkcs.ParsingException 308 if (debug != null) debug.println("processEntry caught: "+ioe); 309 // ignore and treat as unsigned 310 } catch (SignatureException se) { 311 if (debug != null) debug.println("processEntry caught: "+se); 312 // ignore and treat as unsigned 313 } catch (NoSuchAlgorithmException nsae) { 314 if (debug != null) debug.println("processEntry caught: "+nsae); 315 // ignore and treat as unsigned 316 } catch (CertificateException ce) { 317 if (debug != null) debug.println("processEntry caught: "+ce); 318 // ignore and treat as unsigned 319 } 320 } 321 } 322 323 /** 324 * Return an array of java.security.cert.Certificate objects for 325 * the given file in the jar. 326 * @deprecated 327 */ 328 @Deprecated 329 public java.security.cert.Certificate[] getCerts(String name) 330 { 331 return mapSignersToCertArray(getCodeSigners(name)); 332 } 333 334 public java.security.cert.Certificate[] getCerts(JarFile jar, JarEntry entry) 335 { 336 return mapSignersToCertArray(getCodeSigners(jar, entry)); 337 } 338 339 /** 340 * return an array of CodeSigner objects for 341 * the given file in the jar. this array is not cloned. 342 * 343 */ 344 public CodeSigner[] getCodeSigners(String name) 345 { 346 return verifiedSigners.get(name); 347 } 348 349 public CodeSigner[] getCodeSigners(JarFile jar, JarEntry entry) 350 { 351 String name = entry.getName(); 352 if (eagerValidation && sigFileSigners.get(name) != null) { 353 /* 354 * Force a read of the entry data to generate the 355 * verification hash. 356 */ 357 try { 358 InputStream s = jar.getInputStream(entry); 359 byte[] buffer = new byte[1024]; 360 int n = buffer.length; 361 while (n != -1) { 362 n = s.read(buffer, 0, buffer.length); 363 } 364 s.close(); 365 } catch (IOException e) { 366 } 367 } 368 return getCodeSigners(name); 369 } 370 371 /* 372 * Convert an array of signers into an array of concatenated certificate 373 * arrays. 374 */ 375 private static java.security.cert.Certificate[] mapSignersToCertArray( 376 CodeSigner[] signers) { 377 378 if (signers != null) { 379 ArrayList<java.security.cert.Certificate> certChains = new ArrayList<>(); 380 for (int i = 0; i < signers.length; i++) { 381 certChains.addAll( 382 signers[i].getSignerCertPath().getCertificates()); 383 } 384 385 // Convert into a Certificate[] 386 return certChains.toArray( 387 new java.security.cert.Certificate[certChains.size()]); 388 } 389 return null; 390 } 391 392 /** 393 * returns true if there no files to verify. 394 * should only be called after all the META-INF entries 395 * have been processed. 396 */ 397 boolean nothingToVerify() 398 { 399 return (anyToVerify == false); 400 } 401 402 /** 403 * called to let us know we have processed all the 404 * META-INF entries, and if we re-read one of them, don't 405 * re-process it. Also gets rid of any data structures 406 * we needed when parsing META-INF entries. 407 */ 408 void doneWithMeta() 409 { 410 parsingMeta = false; 411 anyToVerify = !sigFileSigners.isEmpty(); 412 baos = null; 413 sigFileData = null; 414 pendingBlocks = null; 415 signerCache = null; 416 manDig = null; 417 // MANIFEST.MF is always treated as signed and verified, 418 // move its signers from sigFileSigners to verifiedSigners. 419 if (sigFileSigners.containsKey(JarFile.MANIFEST_NAME)) { 420 CodeSigner[] codeSigners = sigFileSigners.remove(JarFile.MANIFEST_NAME); 421 verifiedSigners.put(JarFile.MANIFEST_NAME, codeSigners); 422 } 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<URL, Map<CodeSigner[], CodeSource>> urlToCodeSourceMap = new HashMap<>(); 496 private Map<CodeSigner[], CodeSource> signerToCodeSource = new HashMap<>(); 497 private URL lastURL; 498 private Map<CodeSigner[], CodeSource> 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<CodeSigner[], CodeSource> map; 507 if (url == lastURL) { 508 map = lastURLMap; 509 } else { 510 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 = 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<CodeSigner[]> signers, boolean unsigned) { 527 List<CodeSource> sources = new ArrayList<>(); 528 529 for (int i = 0; i < signers.size(); i++) { 530 sources.add(mapSignersToCodeSource(url, signers.get(i))); 531 } 532 if (unsigned) { 533 sources.add(mapSignersToCodeSource(url, null)); 534 } 535 return 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<CodeSource> 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 private static final long serialVersionUID = -9047366145967768825L; 577 578 URL vlocation; 579 CodeSigner[] vsigners; 580 java.security.cert.Certificate[] vcerts; 581 Object csdomain; 582 583 VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) { 584 super(location, signers); 585 this.csdomain = csdomain; 586 vlocation = location; 587 vsigners = signers; // from signerCache 588 } 589 590 VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs) { 591 super(location, certs); 592 this.csdomain = csdomain; 593 vlocation = location; 594 vcerts = certs; // from signerCache 595 } 596 597 /* 598 * All VerifierCodeSource instances are constructed based on 599 * singleton signerCache or signerCacheCert entries for each unique signer. 600 * No CodeSigner<->Certificate[] conversion is required. 601 * We use these assumptions to optimize equality comparisons. 602 */ 603 public boolean equals(Object obj) { 604 if (obj == this) { 605 return true; 606 } 607 if (obj instanceof VerifierCodeSource) { 608 VerifierCodeSource that = (VerifierCodeSource) obj; 609 610 /* 611 * Only compare against other per-signer singletons constructed 612 * on behalf of the same JarFile instance. Otherwise, compare 613 * things the slower way. 614 */ 615 if (isSameDomain(that.csdomain)) { 616 if (that.vsigners != this.vsigners 617 || that.vcerts != this.vcerts) { 618 return false; 619 } 620 if (that.vlocation != null) { 621 return that.vlocation.equals(this.vlocation); 622 } else if (this.vlocation != null) { 623 return this.vlocation.equals(that.vlocation); 624 } else { // both null 625 return true; 626 } 627 } 628 } 629 return super.equals(obj); 630 } 631 632 boolean isSameDomain(Object csdomain) { 633 return this.csdomain == csdomain; 634 } 635 636 private CodeSigner[] getPrivateSigners() { 637 return vsigners; 638 } 639 640 private java.security.cert.Certificate[] getPrivateCertificates() { 641 return vcerts; 642 } 643 } 644 private Map<String, CodeSigner[]> signerMap; 645 646 private synchronized Map<String, CodeSigner[]> signerMap() { 647 if (signerMap == null) { 648 /* 649 * Snapshot signer state so it doesn't change on us. We care 650 * only about the asserted signatures. Verification of 651 * signature validity happens via the JarEntry apis. 652 */ 653 signerMap = new HashMap<>(verifiedSigners.size() + sigFileSigners.size()); 654 signerMap.putAll(verifiedSigners); 655 signerMap.putAll(sigFileSigners); 656 } 657 return signerMap; 658 } 659 660 public synchronized Enumeration<String> entryNames(JarFile jar, final CodeSource[] cs) { 661 final Map<String, CodeSigner[]> map = signerMap(); 662 final Iterator<Map.Entry<String, CodeSigner[]>> itor = map.entrySet().iterator(); 663 boolean matchUnsigned = false; 664 665 /* 666 * Grab a single copy of the CodeSigner arrays. Check 667 * to see if we can optimize CodeSigner equality test. 668 */ 669 List<CodeSigner[]> req = new ArrayList<>(cs.length); 670 for (int i = 0; i < cs.length; i++) { 671 CodeSigner[] match = findMatchingSigners(cs[i]); 672 if (match != null) { 673 if (match.length > 0) { 674 req.add(match); 675 } else { 676 matchUnsigned = true; 677 } 678 } 679 } 680 681 final List<CodeSigner[]> signersReq = req; 682 final Enumeration<String> enum2 = (matchUnsigned) ? unsignedEntryNames(jar) : emptyEnumeration; 683 684 return new Enumeration<String>() { 685 686 String name; 687 688 public boolean hasMoreElements() { 689 if (name != null) { 690 return true; 691 } 692 693 while (itor.hasNext()) { 694 Map.Entry<String, CodeSigner[]> e = itor.next(); 695 if (signersReq.contains(e.getValue())) { 696 name = e.getKey(); 697 return true; 698 } 699 } 700 while (enum2.hasMoreElements()) { 701 name = enum2.nextElement(); 702 return true; 703 } 704 return false; 705 } 706 707 public String nextElement() { 708 if (hasMoreElements()) { 709 String value = name; 710 name = null; 711 return value; 712 } 713 throw new NoSuchElementException(); 714 } 715 }; 716 } 717 718 /* 719 * Like entries() but screens out internal JAR mechanism entries 720 * and includes signed entries with no ZIP data. 721 */ 722 public Enumeration<JarEntry> entries2(final JarFile jar, Enumeration<? extends ZipEntry> e) { 723 final Map<String, CodeSigner[]> map = new HashMap<>(); 724 map.putAll(signerMap()); 725 final Enumeration<? extends ZipEntry> enum_ = e; 726 return new Enumeration<JarEntry>() { 727 728 Enumeration<String> signers = null; 729 JarEntry entry; 730 731 public boolean hasMoreElements() { 732 if (entry != null) { 733 return true; 734 } 735 while (enum_.hasMoreElements()) { 736 ZipEntry ze = enum_.nextElement(); 737 if (JarVerifier.isSigningRelated(ze.getName())) { 738 continue; 739 } 740 entry = jar.newEntry(ze); 741 return true; 742 } 743 if (signers == null) { 744 signers = Collections.enumeration(map.keySet()); 745 } 746 while (signers.hasMoreElements()) { 747 String name = signers.nextElement(); 748 entry = jar.newEntry(new ZipEntry(name)); 749 return true; 750 } 751 752 // Any map entries left? 753 return false; 754 } 755 756 public JarEntry nextElement() { 757 if (hasMoreElements()) { 758 JarEntry je = entry; 759 map.remove(je.getName()); 760 entry = null; 761 return je; 762 } 763 throw new NoSuchElementException(); 764 } 765 }; 766 } 767 private Enumeration<String> emptyEnumeration = new Enumeration<String>() { 768 769 public boolean hasMoreElements() { 770 return false; 771 } 772 773 public String nextElement() { 774 throw new NoSuchElementException(); 775 } 776 }; 777 778 // true if file is part of the signature mechanism itself 779 static boolean isSigningRelated(String name) { 780 name = name.toUpperCase(Locale.ENGLISH); 781 if (!name.startsWith("META-INF/")) { 782 return false; 783 } 784 name = name.substring(9); 785 if (name.indexOf('/') != -1) { 786 return false; 787 } 788 if (name.endsWith(".DSA") 789 || name.endsWith(".RSA") 790 || name.endsWith(".SF") 791 || name.endsWith(".EC") 792 || name.startsWith("SIG-") 793 || name.equals("MANIFEST.MF")) { 794 return true; 795 } 796 return false; 797 } 798 799 private Enumeration<String> unsignedEntryNames(JarFile jar) { 800 final Map<String, CodeSigner[]> map = signerMap(); 801 final Enumeration<JarEntry> entries = jar.entries(); 802 return new Enumeration<String>() { 803 804 String name; 805 806 /* 807 * Grab entries from ZIP directory but screen out 808 * metadata. 809 */ 810 public boolean hasMoreElements() { 811 if (name != null) { 812 return true; 813 } 814 while (entries.hasMoreElements()) { 815 String value; 816 ZipEntry e = entries.nextElement(); 817 value = e.getName(); 818 if (e.isDirectory() || isSigningRelated(value)) { 819 continue; 820 } 821 if (map.get(value) == null) { 822 name = value; 823 return true; 824 } 825 } 826 return false; 827 } 828 829 public String nextElement() { 830 if (hasMoreElements()) { 831 String value = name; 832 name = null; 833 return value; 834 } 835 throw new NoSuchElementException(); 836 } 837 }; 838 } 839 private List<CodeSigner[]> jarCodeSigners; 840 841 private synchronized List<CodeSigner[]> getJarCodeSigners() { 842 CodeSigner[] signers; 843 if (jarCodeSigners == null) { 844 HashSet<CodeSigner[]> set = new HashSet<>(); 845 set.addAll(signerMap().values()); 846 jarCodeSigners = new ArrayList<>(); 847 jarCodeSigners.addAll(set); 848 } 849 return jarCodeSigners; 850 } 851 852 public synchronized CodeSource[] getCodeSources(JarFile jar, URL url) { 853 boolean hasUnsigned = unsignedEntryNames(jar).hasMoreElements(); 854 855 return mapSignersToCodeSources(url, getJarCodeSigners(), hasUnsigned); 856 } 857 858 public CodeSource getCodeSource(URL url, String name) { 859 CodeSigner[] signers; 860 861 signers = signerMap().get(name); 862 return mapSignersToCodeSource(url, signers); 863 } 864 865 public CodeSource getCodeSource(URL url, JarFile jar, JarEntry je) { 866 CodeSigner[] signers; 867 868 return mapSignersToCodeSource(url, getCodeSigners(jar, je)); 869 } 870 871 public void setEagerValidation(boolean eager) { 872 eagerValidation = eager; 873 } 874 875 public synchronized List<Object> getManifestDigests() { 876 return Collections.unmodifiableList(manifestDigests); 877 } 878 879 static CodeSource getUnsignedCS(URL url) { 880 return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null); 881 } 882 }