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