1 /*
2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
3  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4  *
5  *
6  *
7  *
8  *
9  *
10  *
11  *
12  *
13  *
14  *
15  *
16  *
17  *
18  *
19  *
20  *
21  *
22  *
23  *
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     /** 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                     Iterator<SignatureFileVerifier> it = pendingBlocks.iterator();
266                     while (it.hasNext()) {
267                         SignatureFileVerifier sfv = it.next();
268                         if (sfv.needSignatureFile(key)) {
269                             if (debug != null) {
270                                 debug.println(
271                                  "processEntry: processing pending block");
272                             }
273
274                             sfv.setSignatureFile(bytes);
275                             sfv.process(sigFileSigners, manifestDigests);
276                         }
277                     }
278                     return;
279                 }
280
281                 // now we are parsing a signature block file
282
283                 String key = uname.substring(0, uname.lastIndexOf("."));
284
285                 if (signerCache == null)
286                     signerCache = new ArrayList<>();
287
288                 if (manDig == null) {
289                     synchronized(manifestRawBytes) {
290                         if (manDig == null) {
291                             manDig = new ManifestDigester(manifestRawBytes);
292                             manifestRawBytes = null;
293                         }
294                     }
295                 }
296
297                 SignatureFileVerifier sfv =
298                   new SignatureFileVerifier(signerCache,
299                                             manDig, uname, baos.toByteArray());
300
301                 if (sfv.needSignatureFileBytes()) {
302                     // see if we have already parsed an external .SF file
303                     byte[] bytes = sigFileData.get(key);
304
305                     if (bytes == null) {
306                         // put this block on queue for later processing
307                         // since we don't have the .SF bytes yet
308                         // (uname, block);
309                         if (debug != null) {
310                             debug.println("adding pending block");
311                         }
312                         pendingBlocks.add(sfv);
313                         return;
314                     } else {
315                         sfv.setSignatureFile(bytes);
316                     }
317                 }
318                 sfv.process(sigFileSigners, manifestDigests);
319
320             } catch (IOException ioe) {
321                 // e.g. sun.security.pkcs.ParsingException
322                 if (debug != null) debug.println("processEntry caught: "+ioe);
323                 // ignore and treat as unsigned
324             } catch (SignatureException se) {
325                 if (debug != null) debug.println("processEntry caught: "+se);
326                 // ignore and treat as unsigned
327             } catch (NoSuchAlgorithmException nsae) {
328                 if (debug != null) debug.println("processEntry caught: "+nsae);
329                 // ignore and treat as unsigned
330             } catch (CertificateException ce) {
331                 if (debug != null) debug.println("processEntry caught: "+ce);
332                 // ignore and treat as unsigned
333             }
334         }
335     }
336
337     /**
338      * Return an array of java.security.cert.Certificate objects for
339      * the given file in the jar.
340      * @deprecated
341      */

342     @Deprecated
343     public java.security.cert.Certificate[] getCerts(String name)
344     {
345         return mapSignersToCertArray(getCodeSigners(name));
346     }
347
348     public java.security.cert.Certificate[] getCerts(JarFile jar, JarEntry entry)
349     {
350         return mapSignersToCertArray(getCodeSigners(jar, entry));
351     }
352
353     /**
354      * return an array of CodeSigner objects for
355      * the given file in the jar. this array is not cloned.
356      *
357      */

358     public CodeSigner[] getCodeSigners(String name)
359     {
360         return verifiedSigners.get(name);
361     }
362
363     public CodeSigner[] getCodeSigners(JarFile jar, JarEntry entry)
364     {
365         String name = entry.getName();
366         if (eagerValidation && sigFileSigners.get(name) != null) {
367             /*
368              * Force a read of the entry data to generate the
369              * verification hash.
370              */

371             try {
372                 InputStream s = jar.getInputStream(entry);
373                 byte[] buffer = new byte[1024];
374                 int n = buffer.length;
375                 while (n != -1) {
376                     n = s.read(buffer, 0, buffer.length);
377                 }
378                 s.close();
379             } catch (IOException e) {
380             }
381         }
382         return getCodeSigners(name);
383     }
384
385     /*
386      * Convert an array of signers into an array of concatenated certificate
387      * arrays.
388      */

389     private static java.security.cert.Certificate[] mapSignersToCertArray(
390         CodeSigner[] signers) {
391
392         if (signers != null) {
393             ArrayList<java.security.cert.Certificate> certChains = new ArrayList<>();
394             for (int i = 0; i < signers.length; i++) {
395                 certChains.addAll(
396                     signers[i].getSignerCertPath().getCertificates());
397             }
398
399             // Convert into a Certificate[]
400             return certChains.toArray(
401                     new java.security.cert.Certificate[certChains.size()]);
402         }
403         return null;
404     }
405
406     /**
407      * returns true if there no files to verify.
408      * should only be called after all the META-INF entries
409      * have been processed.
410      */

411     boolean nothingToVerify()
412     {
413         return (anyToVerify == false);
414     }
415
416     /**
417      * called to let us know we have processed all the
418      * META-INF entries, and if we re-read one of them, don't
419      * re-process it. Also gets rid of any data structures
420      * we needed when parsing META-INF entries.
421      */

422     void doneWithMeta()
423     {
424         parsingMeta = false;
425         anyToVerify = !sigFileSigners.isEmpty();
426         baos = null;
427         sigFileData = null;
428         pendingBlocks = null;
429         signerCache = null;
430         manDig = null;
431         // MANIFEST.MF is always treated as signed and verified,
432         // move its signers from sigFileSigners to verifiedSigners.
433         if (sigFileSigners.containsKey(JarFile.MANIFEST_NAME)) {
434             CodeSigner[] codeSigners = sigFileSigners.remove(JarFile.MANIFEST_NAME);
435             verifiedSigners.put(JarFile.MANIFEST_NAME, codeSigners);
436         }
437     }
438
439     static class VerifierStream extends java.io.InputStream {
440
441         private InputStream is;
442         private JarVerifier jv;
443         private ManifestEntryVerifier mev;
444         private long numLeft;
445
446         VerifierStream(Manifest man,
447                        JarEntry je,
448                        InputStream is,
449                        JarVerifier jv) throws IOException
450         {
451             this.is = is;
452             this.jv = jv;
453             this.mev = new ManifestEntryVerifier(man);
454             this.jv.beginEntry(je, mev);
455             this.numLeft = je.getSize();
456             if (this.numLeft == 0)
457                 this.jv.update(-1, this.mev);
458         }
459
460         public int read() throws IOException
461         {
462             if (numLeft > 0) {
463                 int b = is.read();
464                 jv.update(b, mev);
465                 numLeft--;
466                 if (numLeft == 0)
467                     jv.update(-1, mev);
468                 return b;
469             } else {
470                 return -1;
471             }
472         }
473
474         public int read(byte b[], int off, int len) throws IOException {
475             if ((numLeft > 0) && (numLeft < len)) {
476                 len = (int)numLeft;
477             }
478
479             if (numLeft > 0) {
480                 int n = is.read(b, off, len);
481                 jv.update(n, b, off, len, mev);
482                 numLeft -= n;
483                 if (numLeft == 0)
484                     jv.update(-1, b, off, len, mev);
485                 return n;
486             } else {
487                 return -1;
488             }
489         }
490
491         public void close()
492             throws IOException
493         {
494             if (is != null)
495                 is.close();
496             is = null;
497             mev = null;
498             jv = null;
499         }
500
501         public int available() throws IOException {
502             return is.available();
503         }
504
505     }
506
507     // Extended JavaUtilJarAccess CodeSource API Support
508
509     private Map<URL, Map<CodeSigner[], CodeSource>> urlToCodeSourceMap = new HashMap<>();
510     private Map<CodeSigner[], CodeSource> signerToCodeSource = new HashMap<>();
511     private URL lastURL;
512     private Map<CodeSigner[], CodeSource> lastURLMap;
513
514     /*
515      * Create a unique mapping from codeSigner cache entries to CodeSource.
516      * In theory, multiple URLs origins could map to a single locally cached
517      * and shared JAR file although in practice there will be a single URL in use.
518      */

519     private synchronized CodeSource mapSignersToCodeSource(URL url, CodeSigner[] signers) {
520         Map<CodeSigner[], CodeSource> map;
521         if (url == lastURL) {
522             map = lastURLMap;
523         } else {
524             map = urlToCodeSourceMap.get(url);
525             if (map == null) {
526                 map = new HashMap<>();
527                 urlToCodeSourceMap.put(url, map);
528             }
529             lastURLMap = map;
530             lastURL = url;
531         }
532         CodeSource cs = map.get(signers);
533         if (cs == null) {
534             cs = new VerifierCodeSource(csdomain, url, signers);
535             signerToCodeSource.put(signers, cs);
536         }
537         return cs;
538     }
539
540     private CodeSource[] mapSignersToCodeSources(URL url, List<CodeSigner[]> signers, boolean unsigned) {
541         List<CodeSource> sources = new ArrayList<>();
542
543         for (int i = 0; i < signers.size(); i++) {
544             sources.add(mapSignersToCodeSource(url, signers.get(i)));
545         }
546         if (unsigned) {
547             sources.add(mapSignersToCodeSource(url, null));
548         }
549         return sources.toArray(new CodeSource[sources.size()]);
550     }
551     private CodeSigner[] emptySigner = new CodeSigner[0];
552
553     /*
554      * Match CodeSource to a CodeSigner[] in the signer cache.
555      */

556     private CodeSigner[] findMatchingSigners(CodeSource cs) {
557         if (cs instanceof VerifierCodeSource) {
558             VerifierCodeSource vcs = (VerifierCodeSource) cs;
559             if (vcs.isSameDomain(csdomain)) {
560                 return ((VerifierCodeSource) cs).getPrivateSigners();
561             }
562         }
563
564         /*
565          * In practice signers should always be optimized above
566          * but this handles a CodeSource of any type, just in case.
567          */

568         CodeSource[] sources = mapSignersToCodeSources(cs.getLocation(), getJarCodeSigners(), true);
569         List<CodeSource> sourceList = new ArrayList<>();
570         for (int i = 0; i < sources.length; i++) {
571             sourceList.add(sources[i]);
572         }
573         int j = sourceList.indexOf(cs);
574         if (j != -1) {
575             CodeSigner[] match;
576             match = ((VerifierCodeSource) sourceList.get(j)).getPrivateSigners();
577             if (match == null) {
578                 match = emptySigner;
579             }
580             return match;
581         }
582         return null;
583     }
584
585     /*
586      * Instances of this class hold uncopied references to internal
587      * signing data that can be compared by object reference identity.
588      */

589     private static class VerifierCodeSource extends CodeSource {
590         private static final long serialVersionUID = -9047366145967768825L;
591
592         URL vlocation;
593         CodeSigner[] vsigners;
594         java.security.cert.Certificate[] vcerts;
595         Object csdomain;
596
597         VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) {
598             super(location, signers);
599             this.csdomain = csdomain;
600             vlocation = location;
601             vsigners = signers; // from signerCache
602         }
603
604         VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs) {
605             super(location, certs);
606             this.csdomain = csdomain;
607             vlocation = location;
608             vcerts = certs; // from signerCache
609         }
610
611         /*
612          * All VerifierCodeSource instances are constructed based on
613          * singleton signerCache or signerCacheCert entries for each unique signer.
614          * No CodeSigner<->Certificate[] conversion is required.
615          * We use these assumptions to optimize equality comparisons.
616          */

617         public boolean equals(Object obj) {
618             if (obj == this) {
619                 return true;
620             }
621             if (obj instanceof VerifierCodeSource) {
622                 VerifierCodeSource that = (VerifierCodeSource) obj;
623
624                 /*
625                  * Only compare against other per-signer singletons constructed
626                  * on behalf of the same JarFile instance. Otherwise, compare
627                  * things the slower way.
628                  */

629                 if (isSameDomain(that.csdomain)) {
630                     if (that.vsigners != this.vsigners
631                             || that.vcerts != this.vcerts) {
632                         return false;
633                     }
634                     if (that.vlocation != null) {
635                         return that.vlocation.equals(this.vlocation);
636                     } else if (this.vlocation != null) {
637                         return this.vlocation.equals(that.vlocation);
638                     } else { // both null
639                         return true;
640                     }
641                 }
642             }
643             return super.equals(obj);
644         }
645
646         boolean isSameDomain(Object csdomain) {
647             return this.csdomain == csdomain;
648         }
649
650         private CodeSigner[] getPrivateSigners() {
651             return vsigners;
652         }
653
654         private java.security.cert.Certificate[] getPrivateCertificates() {
655             return vcerts;
656         }
657     }
658     private Map<String, CodeSigner[]> signerMap;
659
660     private synchronized Map<String, CodeSigner[]> signerMap() {
661         if (signerMap == null) {
662             /*
663              * Snapshot signer state so it doesn't change on us. We care
664              * only about the asserted signatures. Verification of
665              * signature validity happens via the JarEntry apis.
666              */

667             signerMap = new HashMap<>(verifiedSigners.size() + sigFileSigners.size());
668             signerMap.putAll(verifiedSigners);
669             signerMap.putAll(sigFileSigners);
670         }
671         return signerMap;
672     }
673
674     public synchronized Enumeration<String> entryNames(JarFile jar, final CodeSource[] cs) {
675         final Map<String, CodeSigner[]> map = signerMap();
676         final Iterator<Map.Entry<String, CodeSigner[]>> itor = map.entrySet().iterator();
677         boolean matchUnsigned = false;
678
679         /*
680          * Grab a single copy of the CodeSigner arrays. Check
681          * to see if we can optimize CodeSigner equality test.
682          */

683         List<CodeSigner[]> req = new ArrayList<>(cs.length);
684         for (int i = 0; i < cs.length; i++) {
685             CodeSigner[] match = findMatchingSigners(cs[i]);
686             if (match != null) {
687                 if (match.length > 0) {
688                     req.add(match);
689                 } else {
690                     matchUnsigned = true;
691                 }
692             } else {
693                 matchUnsigned = true;
694             }
695         }
696
697         final List<CodeSigner[]> signersReq = req;
698         final Enumeration<String> enum2 = (matchUnsigned) ? unsignedEntryNames(jar) : emptyEnumeration;
699
700         return new Enumeration<String>() {
701
702             String name;
703
704             public boolean hasMoreElements() {
705                 if (name != null) {
706                     return true;
707                 }
708
709                 while (itor.hasNext()) {
710                     Map.Entry<String, CodeSigner[]> e = itor.next();
711                     if (signersReq.contains(e.getValue())) {
712                         name = e.getKey();
713                         return true;
714                     }
715                 }
716                 while (enum2.hasMoreElements()) {
717                     name = enum2.nextElement();
718                     return true;
719                 }
720                 return false;
721             }
722
723             public String nextElement() {
724                 if (hasMoreElements()) {
725                     String value = name;
726                     name = null;
727                     return value;
728                 }
729                 throw new NoSuchElementException();
730             }
731         };
732     }
733
734     /*
735      * Like entries() but screens out internal JAR mechanism entries
736      * and includes signed entries with no ZIP data.
737      */

738     public Enumeration<JarEntry> entries2(final JarFile jar, Enumeration<? extends ZipEntry> e) {
739         final Map<String, CodeSigner[]> map = new HashMap<>();
740         map.putAll(signerMap());
741         final Enumeration<? extends ZipEntry> enum_ = e;
742         return new Enumeration<JarEntry>() {
743
744             Enumeration<String> signers = null;
745             JarEntry entry;
746
747             public boolean hasMoreElements() {
748                 if (entry != null) {
749                     return true;
750                 }
751                 while (enum_.hasMoreElements()) {
752                     ZipEntry ze = enum_.nextElement();
753                     if (JarVerifier.isSigningRelated(ze.getName())) {
754                         continue;
755                     }
756                     entry = jar.newEntry(ze);
757                     return true;
758                 }
759                 if (signers == null) {
760                     signers = Collections.enumeration(map.keySet());
761                 }
762                 while (signers.hasMoreElements()) {
763                     String name = signers.nextElement();
764                     entry = jar.newEntry(new ZipEntry(name));
765                     return true;
766                 }
767
768                 // Any map entries left?
769                 return false;
770             }
771
772             public JarEntry nextElement() {
773                 if (hasMoreElements()) {
774                     JarEntry je = entry;
775                     map.remove(je.getName());
776                     entry = null;
777                     return je;
778                 }
779                 throw new NoSuchElementException();
780             }
781         };
782     }
783     private Enumeration<String> emptyEnumeration = new Enumeration<String>() {
784
785         public boolean hasMoreElements() {
786             return false;
787         }
788
789         public String nextElement() {
790             throw new NoSuchElementException();
791         }
792     };
793
794     // true if file is part of the signature mechanism itself
795     static boolean isSigningRelated(String name) {
796         return SignatureFileVerifier.isSigningRelated(name);
797     }
798
799     private Enumeration<String> unsignedEntryNames(JarFile jar) {
800         final Map<String, CodeSigner[]> map = signerMap();
801         final Enumeration<JarEntry> entries = jar.entries();
802         return new Enumeration<String>() {
803
804             String name;
805
806             /*
807              * Grab entries from ZIP directory but screen out
808              * metadata.
809              */

810             public boolean hasMoreElements() {
811                 if (name != null) {
812                     return true;
813                 }
814                 while (entries.hasMoreElements()) {
815                     String value;
816                     ZipEntry e = entries.nextElement();
817                     value = e.getName();
818                     if (e.isDirectory() || isSigningRelated(value)) {
819                         continue;
820                     }
821                     if (map.get(value) == null) {
822                         name = value;
823                         return true;
824                     }
825                 }
826                 return false;
827             }
828
829             public String nextElement() {
830                 if (hasMoreElements()) {
831                     String value = name;
832                     name = null;
833                     return value;
834                 }
835                 throw new NoSuchElementException();
836             }
837         };
838     }
839     private List<CodeSigner[]> jarCodeSigners;
840
841     private synchronized List<CodeSigner[]> getJarCodeSigners() {
842         CodeSigner[] signers;
843         if (jarCodeSigners == null) {
844             HashSet<CodeSigner[]> set = new HashSet<>();
845             set.addAll(signerMap().values());
846             jarCodeSigners = new ArrayList<>();
847             jarCodeSigners.addAll(set);
848         }
849         return jarCodeSigners;
850     }
851
852     public synchronized CodeSource[] getCodeSources(JarFile jar, URL url) {
853         boolean hasUnsigned = unsignedEntryNames(jar).hasMoreElements();
854
855         return mapSignersToCodeSources(url, getJarCodeSigners(), hasUnsigned);
856     }
857
858     public CodeSource getCodeSource(URL url, String name) {
859         CodeSigner[] signers;
860
861         signers = signerMap().get(name);
862         return mapSignersToCodeSource(url, signers);
863     }
864
865     public CodeSource getCodeSource(URL url, JarFile jar, JarEntry je) {
866         CodeSigner[] signers;
867
868         return mapSignersToCodeSource(url, getCodeSigners(jar, je));
869     }
870
871     public void setEagerValidation(boolean eager) {
872         eagerValidation = eager;
873     }
874
875     public synchronized List<Object> getManifestDigests() {
876         return Collections.unmodifiableList(manifestDigests);
877     }
878
879     static CodeSource getUnsignedCS(URL url) {
880         return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null);
881     }
882 }
883
Powered by JavaMelody