1 /* JarFile.java - Representation of a jar file
2 Copyright (C) 2000, 2003, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package java
.util
.jar
;
41 import gnu
.java
.io
.Base64InputStream
;
42 import gnu
.java
.security
.OID
;
43 import gnu
.java
.security
.pkcs
.PKCS7SignedData
;
44 import gnu
.java
.security
.pkcs
.SignerInfo
;
46 import java
.io
.ByteArrayOutputStream
;
48 import java
.io
.FileNotFoundException
;
49 import java
.io
.FilterInputStream
;
50 import java
.io
.IOException
;
51 import java
.io
.InputStream
;
52 import java
.security
.InvalidKeyException
;
53 import java
.security
.MessageDigest
;
54 import java
.security
.NoSuchAlgorithmException
;
55 import java
.security
.Signature
;
56 import java
.security
.SignatureException
;
57 import java
.security
.cert
.CRLException
;
58 import java
.security
.cert
.Certificate
;
59 import java
.security
.cert
.CertificateException
;
60 import java
.security
.cert
.X509Certificate
;
61 import java
.util
.Arrays
;
62 import java
.util
.Enumeration
;
63 import java
.util
.HashMap
;
64 import java
.util
.HashSet
;
65 import java
.util
.Iterator
;
66 import java
.util
.LinkedList
;
67 import java
.util
.List
;
70 import java
.util
.zip
.ZipEntry
;
71 import java
.util
.zip
.ZipException
;
72 import java
.util
.zip
.ZipFile
;
75 * Representation of a jar file.
77 * Note that this class is not a subclass of java.io.File but a subclass of
78 * java.util.zip.ZipFile and you can only read JarFiles with it (although
79 * there are constructors that take a File object).
82 * @author Mark Wielaard (mark@klomp.org)
83 * @author Casey Marshall (csm@gnu.org) wrote the certificate and entry
86 public class JarFile
extends ZipFile
90 /** The name of the manifest entry: META-INF/MANIFEST.MF */
91 public static final String MANIFEST_NAME
= "META-INF/MANIFEST.MF";
93 /** The META-INF directory entry. */
94 private static final String META_INF
= "META-INF/";
96 /** The suffix for PKCS7 DSA signature entries. */
97 private static final String PKCS7_DSA_SUFFIX
= ".DSA";
99 /** The suffix for PKCS7 RSA signature entries. */
100 private static final String PKCS7_RSA_SUFFIX
= ".RSA";
102 /** The suffix for digest attributes. */
103 private static final String DIGEST_KEY_SUFFIX
= "-Digest";
105 /** The suffix for signature files. */
106 private static final String SF_SUFFIX
= ".SF";
109 private static final OID MD2_OID
= new OID("1.2.840.113549.2.2");
110 private static final OID MD4_OID
= new OID("1.2.840.113549.2.4");
111 private static final OID MD5_OID
= new OID("1.2.840.113549.2.5");
112 private static final OID SHA1_OID
= new OID("1.3.14.3.2.26");
113 private static final OID DSA_ENCRYPTION_OID
= new OID("1.2.840.10040.4.1");
114 private static final OID RSA_ENCRYPTION_OID
= new OID("1.2.840.113549.1.1.1");
117 * The manifest of this file, if any, otherwise null.
118 * Read when first needed.
120 private Manifest manifest
;
122 /** Whether to verify the manifest and all entries. */
125 /** Whether the has already been loaded. */
126 private boolean manifestRead
= false;
128 /** Whether the signature files have been loaded. */
129 boolean signaturesRead
= false;
132 * A map between entry names and booleans, signaling whether or
133 * not that entry has been verified.
134 * Only be accessed with lock on this JarFile*/
135 HashMap verified
= new HashMap();
138 * A mapping from entry name to certificates, if any.
139 * Only accessed with lock on this JarFile.
143 static boolean DEBUG
= false;
144 static void debug(Object msg
)
146 System
.err
.print(JarFile
.class.getName());
147 System
.err
.print(" >>> ");
148 System
.err
.println(msg
);
154 * Creates a new JarFile. All jar entries are verified (when a Manifest file
155 * for this JarFile exists). You need to actually open and read the complete
156 * jar entry (with <code>getInputStream()</code>) to check its signature.
158 * @param fileName the name of the file to open
159 * @exception FileNotFoundException if the fileName cannot be found
160 * @exception IOException if another IO exception occurs while reading
162 public JarFile(String fileName
) throws FileNotFoundException
, IOException
164 this(fileName
, true);
168 * Creates a new JarFile. If verify is true then all jar entries are
169 * verified (when a Manifest file for this JarFile exists). You need to
170 * actually open and read the complete jar entry
171 * (with <code>getInputStream()</code>) to check its signature.
173 * @param fileName the name of the file to open
174 * @param verify checks manifest and entries when true and a manifest
175 * exists, when false no checks are made
176 * @exception FileNotFoundException if the fileName cannot be found
177 * @exception IOException if another IO exception occurs while reading
179 public JarFile(String fileName
, boolean verify
) throws
180 FileNotFoundException
, IOException
185 manifest
= readManifest();
191 * Creates a new JarFile. All jar entries are verified (when a Manifest file
192 * for this JarFile exists). You need to actually open and read the complete
193 * jar entry (with <code>getInputStream()</code>) to check its signature.
195 * @param file the file to open as a jar file
196 * @exception FileNotFoundException if the file does not exits
197 * @exception IOException if another IO exception occurs while reading
199 public JarFile(File file
) throws FileNotFoundException
, IOException
205 * Creates a new JarFile. If verify is true then all jar entries are
206 * verified (when a Manifest file for this JarFile exists). You need to
207 * actually open and read the complete jar entry
208 * (with <code>getInputStream()</code>) to check its signature.
210 * @param file the file to open to open as a jar file
211 * @param verify checks manifest and entries when true and a manifest
212 * exists, when false no checks are made
213 * @exception FileNotFoundException if file does not exist
214 * @exception IOException if another IO exception occurs while reading
216 public JarFile(File file
, boolean verify
) throws FileNotFoundException
,
222 manifest
= readManifest();
228 * Creates a new JarFile with the indicated mode. If verify is true then
229 * all jar entries are verified (when a Manifest file for this JarFile
230 * exists). You need to actually open and read the complete jar entry
231 * (with <code>getInputStream()</code>) to check its signature.
232 * manifest and if the manifest exists and verify is true verfies it.
234 * @param file the file to open to open as a jar file
235 * @param verify checks manifest and entries when true and a manifest
236 * exists, when false no checks are made
237 * @param mode either ZipFile.OPEN_READ or
238 * (ZipFile.OPEN_READ | ZipFile.OPEN_DELETE)
239 * @exception FileNotFoundException if the file does not exist
240 * @exception IOException if another IO exception occurs while reading
241 * @exception IllegalArgumentException when given an illegal mode
245 public JarFile(File file
, boolean verify
, int mode
) throws
246 FileNotFoundException
, IOException
, IllegalArgumentException
251 manifest
= readManifest();
259 * XXX - should verify the manifest file
261 private void verify()
263 // only check if manifest is not null
264 if (manifest
== null)
271 // XXX - verify manifest
275 * Parses and returns the manifest if it exists, otherwise returns null.
277 private Manifest
readManifest()
281 ZipEntry manEntry
= super.getEntry(MANIFEST_NAME
);
282 if (manEntry
!= null)
284 InputStream in
= super.getInputStream(manEntry
);
286 return new Manifest(in
);
294 catch (IOException ioe
)
302 * Returns a enumeration of all the entries in the JarFile.
303 * Note that also the Jar META-INF entries are returned.
305 * @exception IllegalStateException when the JarFile is already closed
307 public Enumeration
entries() throws IllegalStateException
309 return new JarEnumeration(super.entries(), this);
313 * Wraps a given Zip Entries Enumeration. For every zip entry a
314 * JarEntry is created and the corresponding Attributes are looked up.
316 private static class JarEnumeration
implements Enumeration
319 private final Enumeration entries
;
320 private final JarFile jarfile
;
322 JarEnumeration(Enumeration e
, JarFile f
)
328 public boolean hasMoreElements()
330 return entries
.hasMoreElements();
333 public Object
nextElement()
335 ZipEntry zip
= (ZipEntry
) entries
.nextElement();
336 JarEntry jar
= new JarEntry(zip
);
340 manifest
= jarfile
.getManifest();
342 catch (IOException ioe
)
347 if (manifest
!= null)
349 jar
.attr
= manifest
.getAttributes(jar
.getName());
352 synchronized(jarfile
)
354 if (!jarfile
.signaturesRead
)
357 jarfile
.readSignatures();
359 catch (IOException ioe
)
364 ioe
.printStackTrace();
366 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.
372 if (jarfile
.entryCerts
!= null
373 && jarfile
.verified
.get(zip
.getName()) == Boolean
.TRUE
)
375 Set certs
= (Set
) jarfile
.entryCerts
.get(jar
.getName());
377 jar
.certs
= (Certificate
[])
378 certs
.toArray(new Certificate
[certs
.size()]);
387 * It actually returns a JarEntry not a zipEntry
390 public synchronized ZipEntry
getEntry(String name
)
392 ZipEntry entry
= super.getEntry(name
);
395 JarEntry jarEntry
= new JarEntry(entry
);
399 manifest
= getManifest();
401 catch (IOException ioe
)
406 if (manifest
!= null)
408 jarEntry
.attr
= manifest
.getAttributes(name
);
416 catch (IOException ioe
)
421 ioe
.printStackTrace();
423 signaturesRead
= true;
425 // See the comments in the JarEnumeration for why we do this
428 debug("entryCerts=" + entryCerts
+ " verified " + name
429 + " ? " + verified
.get(name
));
430 if (entryCerts
!= null && verified
.get(name
) == Boolean
.TRUE
)
432 Set certs
= (Set
) entryCerts
.get(name
);
434 jarEntry
.certs
= (Certificate
[])
435 certs
.toArray(new Certificate
[certs
.size()]);
443 * Returns an input stream for the given entry. If configured to
444 * verify entries, the input stream returned will verify them while
445 * the stream is read, but only on the first time.
447 * @param entry The entry to get the input stream for.
448 * @exception ZipException XXX
449 * @exception IOException XXX
451 public synchronized InputStream
getInputStream(ZipEntry entry
) throws
452 ZipException
, IOException
454 // If we haven't verified the hash, do it now.
455 if (!verified
.containsKey(entry
.getName()) && verify
)
458 debug("reading and verifying " + entry
);
459 return new EntryInputStream(entry
, super.getInputStream(entry
), this);
464 debug("reading already verified entry " + entry
);
465 if (verify
&& verified
.get(entry
.getName()) == Boolean
.FALSE
)
466 throw new ZipException("digest for " + entry
+ " is invalid");
467 return super.getInputStream(entry
);
472 * Returns the JarEntry that belongs to the name if such an entry
473 * exists in the JarFile. Returns null otherwise
474 * Convenience method that just casts the result from <code>getEntry</code>
477 * @param name the jar entry name to look up
478 * @return the JarEntry if it exists, null otherwise
480 public JarEntry
getJarEntry(String name
)
482 return (JarEntry
) getEntry(name
);
486 * Returns the manifest for this JarFile or null when the JarFile does not
487 * contain a manifest file.
489 public synchronized Manifest
getManifest() throws IOException
492 manifest
= readManifest();
497 // Only called with lock on this JarFile.
498 private void readSignatures() throws IOException
500 Map pkcs7Dsa
= new HashMap();
501 Map pkcs7Rsa
= new HashMap();
502 Map sigFiles
= new HashMap();
504 // Phase 1: Read all signature files. These contain the user
505 // certificates as well as the signatures themselves.
506 for (Enumeration e
= super.entries(); e
.hasMoreElements(); )
508 ZipEntry ze
= (ZipEntry
) e
.nextElement();
509 String name
= ze
.getName();
510 if (name
.startsWith(META_INF
))
512 String alias
= name
.substring(META_INF
.length());
513 if (alias
.lastIndexOf('.') >= 0)
514 alias
= alias
.substring(0, alias
.lastIndexOf('.'));
516 if (name
.endsWith(PKCS7_DSA_SUFFIX
) || name
.endsWith(PKCS7_RSA_SUFFIX
))
519 debug("reading PKCS7 info from " + name
+ ", alias=" + alias
);
520 PKCS7SignedData sig
= null;
523 sig
= new PKCS7SignedData(super.getInputStream(ze
));
525 catch (CertificateException ce
)
527 IOException ioe
= new IOException("certificate parsing error");
531 catch (CRLException crle
)
533 IOException ioe
= new IOException("CRL parsing error");
537 if (name
.endsWith(PKCS7_DSA_SUFFIX
))
538 pkcs7Dsa
.put(alias
, sig
);
539 else if (name
.endsWith(PKCS7_RSA_SUFFIX
))
540 pkcs7Rsa
.put(alias
, sig
);
542 else if (name
.endsWith(SF_SUFFIX
))
545 debug("reading signature file for " + alias
+ ": " + name
);
546 Manifest sf
= new Manifest(super.getInputStream(ze
));
547 sigFiles
.put(alias
, sf
);
549 debug("result: " + sf
);
554 // Phase 2: verify the signatures on any signature files.
555 Set validCerts
= new HashSet();
556 Map entryCerts
= new HashMap();
557 for (Iterator it
= sigFiles
.entrySet().iterator(); it
.hasNext(); )
560 Map
.Entry e
= (Map
.Entry
) it
.next();
561 String alias
= (String
) e
.getKey();
563 PKCS7SignedData sig
= (PKCS7SignedData
) pkcs7Dsa
.get(alias
);
566 Certificate
[] certs
= sig
.getCertificates();
567 Set signerInfos
= sig
.getSignerInfos();
568 for (Iterator it2
= signerInfos
.iterator(); it2
.hasNext(); )
569 verify(certs
, (SignerInfo
) it2
.next(), alias
, validCerts
);
572 sig
= (PKCS7SignedData
) pkcs7Rsa
.get(alias
);
575 Certificate
[] certs
= sig
.getCertificates();
576 Set signerInfos
= sig
.getSignerInfos();
577 for (Iterator it2
= signerInfos
.iterator(); it2
.hasNext(); )
578 verify(certs
, (SignerInfo
) it2
.next(), alias
, validCerts
);
581 // It isn't a signature for anything. Punt it.
582 if (validCerts
.isEmpty())
588 entryCerts
.put(e
.getValue(), new HashSet(validCerts
));
592 // Phase 3: verify the signature file signatures against the manifest,
593 // mapping the entry name to the target certificates.
594 this.entryCerts
= new HashMap();
595 for (Iterator it
= entryCerts
.entrySet().iterator(); it
.hasNext(); )
597 Map
.Entry e
= (Map
.Entry
) it
.next();
598 Manifest sigfile
= (Manifest
) e
.getKey();
599 Map entries
= sigfile
.getEntries();
600 Set certificates
= (Set
) e
.getValue();
602 for (Iterator it2
= entries
.entrySet().iterator(); it2
.hasNext(); )
604 Map
.Entry e2
= (Map
.Entry
) it2
.next();
605 String entryname
= String
.valueOf(e2
.getKey());
606 Attributes attr
= (Attributes
) e2
.getValue();
607 if (verifyHashes(entryname
, attr
))
610 debug("entry " + entryname
+ " has certificates " + certificates
);
611 Set s
= (Set
) this.entryCerts
.get(entryname
);
613 s
.addAll(certificates
);
615 this.entryCerts
.put(entryname
, new HashSet(certificates
));
620 signaturesRead
= true;
624 * Tell if the given signer info is over the given alias's signature file,
625 * given one of the certificates specified.
627 private void verify(Certificate
[] certs
, SignerInfo signerInfo
,
628 String alias
, Set validCerts
)
630 Signature sig
= null;
633 OID alg
= signerInfo
.getDigestEncryptionAlgorithmId();
634 if (alg
.equals(DSA_ENCRYPTION_OID
))
636 if (!signerInfo
.getDigestAlgorithmId().equals(SHA1_OID
))
638 sig
= Signature
.getInstance("SHA1withDSA");
640 else if (alg
.equals(RSA_ENCRYPTION_OID
))
642 OID hash
= signerInfo
.getDigestAlgorithmId();
643 if (hash
.equals(MD2_OID
))
644 sig
= Signature
.getInstance("md2WithRsaEncryption");
645 else if (hash
.equals(MD4_OID
))
646 sig
= Signature
.getInstance("md4WithRsaEncryption");
647 else if (hash
.equals(MD5_OID
))
648 sig
= Signature
.getInstance("md5WithRsaEncryption");
649 else if (hash
.equals(SHA1_OID
))
650 sig
= Signature
.getInstance("sha1WithRsaEncryption");
657 debug("unsupported signature algorithm: " + alg
);
661 catch (NoSuchAlgorithmException nsae
)
666 nsae
.printStackTrace();
670 ZipEntry sigFileEntry
= super.getEntry(META_INF
+ alias
+ SF_SUFFIX
);
671 if (sigFileEntry
== null)
673 for (int i
= 0; i
< certs
.length
; i
++)
675 if (!(certs
[i
] instanceof X509Certificate
))
677 X509Certificate cert
= (X509Certificate
) certs
[i
];
678 if (!cert
.getIssuerX500Principal().equals(signerInfo
.getIssuer()) ||
679 !cert
.getSerialNumber().equals(signerInfo
.getSerialNumber()))
683 sig
.initVerify(cert
.getPublicKey());
684 InputStream in
= super.getInputStream(sigFileEntry
);
687 byte[] buf
= new byte[1024];
689 while ((len
= in
.read(buf
)) != -1)
690 sig
.update(buf
, 0, len
);
691 if (sig
.verify(signerInfo
.getEncryptedDigest()))
694 debug("signature for " + cert
.getSubjectDN() + " is good");
695 validCerts
.add(cert
);
698 catch (IOException ioe
)
702 catch (InvalidKeyException ike
)
706 catch (SignatureException se
)
714 * Verifies that the digest(s) in a signature file were, in fact, made
715 * over the manifest entry for ENTRY.
717 * @param entry The entry name.
718 * @param attr The attributes from the signature file to verify.
720 private boolean verifyHashes(String entry
, Attributes attr
)
724 // The bytes for ENTRY's manifest entry, which are signed in the
726 byte[] entryBytes
= null;
729 ZipEntry e
= super.getEntry(entry
);
733 debug("verifyHashes: no entry '" + entry
+ "'");
736 entryBytes
= readManifestEntry(e
);
738 catch (IOException ioe
)
743 ioe
.printStackTrace();
748 for (Iterator it
= attr
.entrySet().iterator(); it
.hasNext(); )
750 Map
.Entry e
= (Map
.Entry
) it
.next();
751 String key
= String
.valueOf(e
.getKey());
752 if (!key
.endsWith(DIGEST_KEY_SUFFIX
))
754 String alg
= key
.substring(0, key
.length() - DIGEST_KEY_SUFFIX
.length());
757 byte[] hash
= Base64InputStream
.decode((String
) e
.getValue());
758 MessageDigest md
= MessageDigest
.getInstance(alg
);
759 md
.update(entryBytes
);
760 byte[] hash2
= md
.digest();
762 debug("verifying SF entry " + entry
+ " alg: " + md
.getAlgorithm()
763 + " expect=" + new java
.math
.BigInteger(hash
).toString(16)
764 + " comp=" + new java
.math
.BigInteger(hash2
).toString(16));
765 if (!Arrays
.equals(hash
, hash2
))
769 catch (IOException ioe
)
774 ioe
.printStackTrace();
778 catch (NoSuchAlgorithmException nsae
)
783 nsae
.printStackTrace();
789 // We have to find at least one valid digest.
794 * Read the raw bytes that comprise a manifest entry. We can't use the
795 * Manifest object itself, because that loses information (such as line
796 * endings, and order of entries).
798 private byte[] readManifestEntry(ZipEntry entry
) throws IOException
800 InputStream in
= super.getInputStream(super.getEntry(MANIFEST_NAME
));
801 ByteArrayOutputStream out
= new ByteArrayOutputStream();
802 byte[] target
= ("Name: " + entry
.getName()).getBytes();
803 int t
= 0, c
, prev
= -1, state
= 0, l
= -1;
805 while ((c
= in
.read()) != -1)
809 // + (c == '\n' ? "\\n" : (c == '\r' ? "\\r" : String.valueOf((char) c)))
810 // + " state=" + state + " prev="
811 // + (prev == '\n' ? "\\n" : (prev == '\r' ? "\\r" : String.valueOf((char) prev)))
812 // + " t=" + t + (t < target.length ? (" target[t]=" + (char) target[t]) : "")
817 // Step 1: read until we find the "target" bytes: the start
818 // of the entry we need to read.
820 if (((byte) c
) != target
[t
])
825 if (t
== target
.length
)
833 // Step 2: assert that there is a newline character after
834 // the "target" bytes.
836 if (c
!= '\n' && c
!= '\r')
849 // Step 3: read this whole entry, until we reach an empty
855 // NL always terminates a line.
856 if (l
== 0 || (l
== 1 && prev
== '\r'))
857 return out
.toByteArray();
862 // Here we see a blank line terminated by a CR,
863 // followed by the next entry. Technically, `c' should
864 // always be 'N' at this point.
865 if (l
== 1 && prev
== '\r')
866 return out
.toByteArray();
874 throw new RuntimeException("this statement should be unreachable");
878 // The last entry, with a single CR terminating the line.
879 if (state
== 2 && prev
== '\r' && l
== 0)
880 return out
.toByteArray();
882 // We should not reach this point, we didn't find the entry (or, possibly,
883 // it is the last entry and is malformed).
884 throw new IOException("could not find " + entry
+ " in manifest");
888 * A utility class that verifies jar entries as they are read.
890 private static class EntryInputStream
extends FilterInputStream
892 private final JarFile jarfile
;
893 private final long length
;
895 private final ZipEntry entry
;
896 private final byte[][] hashes
;
897 private final MessageDigest
[] md
;
898 private boolean checked
;
900 EntryInputStream(final ZipEntry entry
,
901 final InputStream in
,
909 length
= entry
.getSize();
914 Manifest manifest
= jarfile
.getManifest();
915 if (manifest
!= null)
916 attr
= manifest
.getAttributes(entry
.getName());
920 debug("verifying entry " + entry
+ " attr=" + attr
);
923 hashes
= new byte[0][];
924 md
= new MessageDigest
[0];
928 List hashes
= new LinkedList();
929 List md
= new LinkedList();
930 for (Iterator it
= attr
.entrySet().iterator(); it
.hasNext(); )
932 Map
.Entry e
= (Map
.Entry
) it
.next();
933 String key
= String
.valueOf(e
.getKey());
936 if (!key
.endsWith(DIGEST_KEY_SUFFIX
))
938 hashes
.add(Base64InputStream
.decode((String
) e
.getValue()));
941 md
.add(MessageDigest
.getInstance
942 (key
.substring(0, key
.length() - DIGEST_KEY_SUFFIX
.length())));
944 catch (NoSuchAlgorithmException nsae
)
946 IOException ioe
= new IOException("no such message digest: " + key
);
952 debug("digests=" + md
);
953 this.hashes
= (byte[][]) hashes
.toArray(new byte[hashes
.size()][]);
954 this.md
= (MessageDigest
[]) md
.toArray(new MessageDigest
[md
.size()]);
958 public boolean markSupported()
963 public void mark(int readLimit
)
971 public int read() throws IOException
973 int b
= super.read();
979 for (int i
= 0; i
< md
.length
; i
++)
980 md
[i
].update((byte) b
);
982 if (length
> 0 && pos
>= length
)
987 public int read(byte[] buf
, int off
, int len
) throws IOException
989 int count
= super.read(buf
, off
, (int) Math
.min(len
, (length
!= 0
991 : Integer
.MAX_VALUE
)));
992 if (count
== -1 || (length
> 0 && pos
>= length
))
997 for (int i
= 0; i
< md
.length
; i
++)
998 md
[i
].update(buf
, off
, count
);
1000 if (length
!= 0 && pos
>= length
)
1005 public int read(byte[] buf
) throws IOException
1007 return read(buf
, 0, buf
.length
);
1010 public long skip(long bytes
) throws IOException
1012 byte[] b
= new byte[1024];
1014 while (amount
< bytes
)
1016 int l
= read(b
, 0, (int) Math
.min(b
.length
, bytes
- amount
));
1024 private void eof() throws IOException
1029 for (int i
= 0; i
< md
.length
; i
++)
1031 byte[] hash
= md
[i
].digest();
1033 debug("verifying " + md
[i
].getAlgorithm() + " expect="
1034 + new java
.math
.BigInteger(hashes
[i
]).toString(16)
1035 + " comp=" + new java
.math
.BigInteger(hash
).toString(16));
1036 if (!Arrays
.equals(hash
, hashes
[i
]))
1038 synchronized(jarfile
)
1041 debug(entry
+ " could NOT be verified");
1042 jarfile
.verified
.put(entry
.getName(), Boolean
.FALSE
);
1045 // XXX ??? what do we do here?
1046 // throw new ZipException("message digest mismatch");
1050 synchronized(jarfile
)
1053 debug(entry
+ " has been VERIFIED");
1054 jarfile
.verified
.put(entry
.getName(), Boolean
.TRUE
);