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