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