122 |
private Manifest manifest; |
private Manifest manifest; |
123 |
|
|
124 |
/** Whether to verify the manifest and all entries. */ |
/** Whether to verify the manifest and all entries. */ |
125 |
private boolean verify; |
boolean verify; |
126 |
|
|
127 |
/** Whether the has already been loaded. */ |
/** Whether the has already been loaded. */ |
128 |
private boolean manifestRead = false; |
private boolean manifestRead = false; |
129 |
|
|
130 |
/** Whether the signature files have been loaded. */ |
/** Whether the signature files have been loaded. */ |
131 |
private boolean signaturesRead = false; |
boolean signaturesRead = false; |
132 |
|
|
133 |
/** A map between entry names and booleans, signaling whether or |
/** |
134 |
not that entry has been verified. */ |
* A map between entry names and booleans, signaling whether or |
135 |
private HashMap verified = new HashMap(); |
* not that entry has been verified. |
136 |
|
* Only be accessed with lock on this JarFile*/ |
137 |
|
HashMap verified = new HashMap(); |
138 |
|
|
139 |
/** A mapping from entry name to certificates, if any. */ |
/** |
140 |
private HashMap entryCerts; |
* A mapping from entry name to certificates, if any. |
141 |
|
* Only accessed with lock on this JarFile. |
142 |
|
*/ |
143 |
|
HashMap entryCerts; |
144 |
|
|
145 |
private static boolean DEBUG = false; |
static boolean DEBUG = false; |
146 |
private static void debug(Object msg) |
static void debug(Object msg) |
147 |
{ |
{ |
148 |
System.err.print(JarFile.class.getName()); |
System.err.print(JarFile.class.getName()); |
149 |
System.err.print(" >>> "); |
System.err.print(" >>> "); |
308 |
*/ |
*/ |
309 |
public Enumeration entries() throws IllegalStateException |
public Enumeration entries() throws IllegalStateException |
310 |
{ |
{ |
311 |
return new JarEnumeration(super.entries()); |
return new JarEnumeration(super.entries(), this); |
312 |
} |
} |
313 |
|
|
314 |
/** |
/** |
315 |
* Wraps a given Zip Entries Enumeration. For every zip entry a |
* Wraps a given Zip Entries Enumeration. For every zip entry a |
316 |
* JarEntry is created and the corresponding Attributes are looked up. |
* JarEntry is created and the corresponding Attributes are looked up. |
317 |
*/ |
*/ |
318 |
private class JarEnumeration implements Enumeration |
private static class JarEnumeration implements Enumeration |
319 |
{ |
{ |
320 |
|
|
321 |
private final Enumeration entries; |
private final Enumeration entries; |
322 |
|
private final JarFile jarfile; |
323 |
|
|
324 |
JarEnumeration(Enumeration e) |
JarEnumeration(Enumeration e, JarFile f) |
325 |
{ |
{ |
326 |
entries = e; |
entries = e; |
327 |
|
jarfile = f; |
328 |
} |
} |
329 |
|
|
330 |
public boolean hasMoreElements() |
public boolean hasMoreElements() |
339 |
Manifest manifest; |
Manifest manifest; |
340 |
try |
try |
341 |
{ |
{ |
342 |
manifest = getManifest(); |
manifest = jarfile.getManifest(); |
343 |
} |
} |
344 |
catch (IOException ioe) |
catch (IOException ioe) |
345 |
{ |
{ |
351 |
jar.attr = manifest.getAttributes(jar.getName()); |
jar.attr = manifest.getAttributes(jar.getName()); |
352 |
} |
} |
353 |
|
|
354 |
if (!signaturesRead) |
synchronized(jarfile) |
355 |
try |
{ |
356 |
{ |
if (!jarfile.signaturesRead) |
357 |
readSignatures(); |
try |
358 |
} |
{ |
359 |
catch (IOException ioe) |
jarfile.readSignatures(); |
360 |
{ |
} |
361 |
if (DEBUG) |
catch (IOException ioe) |
362 |
{ |
{ |
363 |
debug(ioe); |
if (JarFile.DEBUG) |
364 |
ioe.printStackTrace(); |
{ |
365 |
} |
JarFile.debug(ioe); |
366 |
signaturesRead = true; // fudge it. |
ioe.printStackTrace(); |
367 |
} |
} |
368 |
|
jarfile.signaturesRead = true; // fudge it. |
369 |
// Include the certificates only if we have asserted that the |
} |
370 |
// signatures are valid. This means the certificates will not be |
|
371 |
// available if the entry hasn't been read yet. |
// Include the certificates only if we have asserted that the |
372 |
if (entryCerts != null && verified.containsKey(zip.getName()) |
// signatures are valid. This means the certificates will not be |
373 |
&& ((Boolean) verified.get(zip.getName())).booleanValue()) |
// available if the entry hasn't been read yet. |
374 |
{ |
if (jarfile.entryCerts != null |
375 |
Set certs = (Set) entryCerts.get(jar.getName()); |
&& jarfile.verified.get(zip.getName()) == Boolean.TRUE) |
376 |
if (certs != null) |
{ |
377 |
jar.certs = (Certificate[]) |
Set certs = (Set) jarfile.entryCerts.get(jar.getName()); |
378 |
certs.toArray(new Certificate[certs.size()]); |
if (certs != null) |
379 |
} |
jar.certs = (Certificate[]) |
380 |
|
certs.toArray(new Certificate[certs.size()]); |
381 |
|
} |
382 |
|
} |
383 |
return jar; |
return jar; |
384 |
} |
} |
385 |
} |
} |
389 |
* It actually returns a JarEntry not a zipEntry |
* It actually returns a JarEntry not a zipEntry |
390 |
* @param name XXX |
* @param name XXX |
391 |
*/ |
*/ |
392 |
public ZipEntry getEntry(String name) |
public synchronized ZipEntry getEntry(String name) |
393 |
{ |
{ |
394 |
ZipEntry entry = super.getEntry(name); |
ZipEntry entry = super.getEntry(name); |
395 |
if (entry != null) |
if (entry != null) |
410 |
jarEntry.attr = manifest.getAttributes(name); |
jarEntry.attr = manifest.getAttributes(name); |
411 |
} |
} |
412 |
|
|
413 |
if (!signaturesRead) |
if (!signaturesRead) |
414 |
try |
try |
415 |
{ |
{ |
416 |
readSignatures(); |
readSignatures(); |
417 |
} |
} |
418 |
catch (IOException ioe) |
catch (IOException ioe) |
419 |
{ |
{ |
420 |
if (DEBUG) |
if (DEBUG) |
421 |
{ |
{ |
422 |
debug(ioe); |
debug(ioe); |
423 |
ioe.printStackTrace(); |
ioe.printStackTrace(); |
424 |
} |
} |
425 |
signaturesRead = true; |
signaturesRead = true; |
426 |
} |
} |
427 |
// See the comments in the JarEnumeration for why we do this |
// See the comments in the JarEnumeration for why we do this |
428 |
// check. |
// check. |
429 |
if (DEBUG) |
if (DEBUG) |
430 |
debug("entryCerts=" + entryCerts + " verified " + name |
debug("entryCerts=" + entryCerts + " verified " + name |
431 |
+ " ? " + verified.get(name)); |
+ " ? " + verified.get(name)); |
432 |
if (entryCerts != null && verified.containsKey(name) |
if (entryCerts != null && verified.get(name) == Boolean.TRUE) |
433 |
&& ((Boolean) verified.get(name)).booleanValue()) |
{ |
434 |
{ |
Set certs = (Set) entryCerts.get(name); |
435 |
Set certs = (Set) entryCerts.get(name); |
if (certs != null) |
436 |
if (certs != null) |
jarEntry.certs = (Certificate[]) |
437 |
jarEntry.certs = (Certificate[]) |
certs.toArray(new Certificate[certs.size()]); |
|
certs.toArray(new Certificate[certs.size()]); |
|
438 |
} |
} |
439 |
return jarEntry; |
return jarEntry; |
440 |
} |
} |
458 |
{ |
{ |
459 |
if (DEBUG) |
if (DEBUG) |
460 |
debug("reading and verifying " + entry); |
debug("reading and verifying " + entry); |
461 |
return new EntryInputStream(entry, super.getInputStream(entry)); |
return new EntryInputStream(entry, super.getInputStream(entry), this); |
462 |
} |
} |
463 |
else |
else |
464 |
{ |
{ |
465 |
if (DEBUG) |
if (DEBUG) |
466 |
debug("reading already verified entry " + entry); |
debug("reading already verified entry " + entry); |
467 |
if (!((Boolean) verified.get(entry.getName())).booleanValue()) |
if (verify && verified.get(entry.getName()) == Boolean.FALSE) |
468 |
throw new ZipException("digest for " + entry + " is invalid"); |
throw new ZipException("digest for " + entry + " is invalid"); |
469 |
return super.getInputStream(entry); |
return super.getInputStream(entry); |
470 |
} |
} |
488 |
* Returns the manifest for this JarFile or null when the JarFile does not |
* Returns the manifest for this JarFile or null when the JarFile does not |
489 |
* contain a manifest file. |
* contain a manifest file. |
490 |
*/ |
*/ |
491 |
public Manifest getManifest() throws IOException |
public synchronized Manifest getManifest() throws IOException |
492 |
{ |
{ |
493 |
if (!manifestRead) |
if (!manifestRead) |
494 |
manifest = readManifest(); |
manifest = readManifest(); |
496 |
return manifest; |
return manifest; |
497 |
} |
} |
498 |
|
|
499 |
|
// Only called with lock on this JarFile. |
500 |
private void readSignatures() throws IOException |
private void readSignatures() throws IOException |
501 |
{ |
{ |
502 |
Map pkcs7Dsa = new HashMap(); |
Map pkcs7Dsa = new HashMap(); |
882 |
/** |
/** |
883 |
* A utility class that verifies jar entries as they are read. |
* A utility class that verifies jar entries as they are read. |
884 |
*/ |
*/ |
885 |
private class EntryInputStream extends FilterInputStream |
private static class EntryInputStream extends FilterInputStream |
886 |
{ |
{ |
887 |
|
private final JarFile jarfile; |
888 |
private final long length; |
private final long length; |
889 |
private long pos; |
private long pos; |
890 |
private final ZipEntry entry; |
private final ZipEntry entry; |
892 |
private final MessageDigest[] md; |
private final MessageDigest[] md; |
893 |
private boolean checked; |
private boolean checked; |
894 |
|
|
895 |
EntryInputStream(final ZipEntry entry, final InputStream in) |
EntryInputStream(final ZipEntry entry, |
896 |
|
final InputStream in, |
897 |
|
final JarFile jar) |
898 |
throws IOException |
throws IOException |
899 |
{ |
{ |
900 |
super(in); |
super(in); |
901 |
this.entry = entry; |
this.entry = entry; |
902 |
|
this.jarfile = jar; |
903 |
|
|
904 |
length = entry.getSize(); |
length = entry.getSize(); |
905 |
pos = 0; |
pos = 0; |
906 |
checked = false; |
checked = false; |
907 |
|
|
908 |
Attributes attr = manifest.getAttributes(entry.getName()); |
Attributes attr; |
909 |
|
Manifest manifest = jarfile.getManifest(); |
910 |
|
if (manifest != null) |
911 |
|
attr = manifest.getAttributes(entry.getName()); |
912 |
|
else |
913 |
|
attr = null; |
914 |
if (DEBUG) |
if (DEBUG) |
915 |
debug("verifying entry " + entry + " attr=" + attr); |
debug("verifying entry " + entry + " attr=" + attr); |
916 |
if (attr == null) |
if (attr == null) |
1030 |
+ " comp=" + new java.math.BigInteger(hash).toString(16)); |
+ " comp=" + new java.math.BigInteger(hash).toString(16)); |
1031 |
if (!Arrays.equals(hash, hashes[i])) |
if (!Arrays.equals(hash, hashes[i])) |
1032 |
{ |
{ |
1033 |
if (DEBUG) |
synchronized(jarfile) |
1034 |
debug(entry + " could NOT be verified"); |
{ |
1035 |
verified.put(entry.getName(), Boolean.FALSE); |
if (DEBUG) |
1036 |
return; |
debug(entry + " could NOT be verified"); |
1037 |
// XXX ??? what do we do here? |
jarfile.verified.put(entry.getName(), Boolean.FALSE); |
1038 |
// throw new ZipException("message digest mismatch"); |
} |
1039 |
|
return; |
1040 |
|
// XXX ??? what do we do here? |
1041 |
|
// throw new ZipException("message digest mismatch"); |
1042 |
} |
} |
1043 |
} |
} |
1044 |
if (DEBUG) |
|
1045 |
debug(entry + " has been VERIFIED"); |
synchronized(jarfile) |
1046 |
verified.put(entry.getName(), Boolean.TRUE); |
{ |
1047 |
|
if (DEBUG) |
1048 |
|
debug(entry + " has been VERIFIED"); |
1049 |
|
jarfile.verified.put(entry.getName(), Boolean.TRUE); |
1050 |
|
} |
1051 |
} |
} |
1052 |
} |
} |
1053 |
} |
} |