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