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