You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by te...@apache.org on 2006/05/31 16:59:43 UTC

svn commit: r410554 - /incubator/harmony/enhanced/classlib/trunk/modules/archive/src/main/java/java/util/jar/JarVerifier.java

Author: tellison
Date: Wed May 31 07:59:43 2006
New Revision: 410554

URL: http://svn.apache.org/viewvc?rev=410554&view=rev
Log:
Source tidy-up and reformatting.

Modified:
    incubator/harmony/enhanced/classlib/trunk/modules/archive/src/main/java/java/util/jar/JarVerifier.java

Modified: incubator/harmony/enhanced/classlib/trunk/modules/archive/src/main/java/java/util/jar/JarVerifier.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/archive/src/main/java/java/util/jar/JarVerifier.java?rev=410554&r1=410553&r2=410554&view=diff
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/archive/src/main/java/java/util/jar/JarVerifier.java (original)
+++ incubator/harmony/enhanced/classlib/trunk/modules/archive/src/main/java/java/util/jar/JarVerifier.java Wed May 31 07:59:43 2006
@@ -15,7 +15,6 @@
 
 package java.util.jar;
 
-
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -32,7 +31,6 @@
 import java.util.Vector;
 import java.util.zip.ZipEntry;
 
-
 import org.apache.harmony.security.utils.JarUtils;
 
 import org.apache.harmony.luni.util.Base64;
@@ -56,437 +54,451 @@
  */
 class JarVerifier {
 
-	private String jarName;
+    private String jarName;
 
-	private Manifest man;
+    private Manifest man;
 
-	private HashMap metaEntries = new HashMap(5);
+    private HashMap metaEntries = new HashMap(5);
 
-	private Hashtable signatures = new Hashtable(5);
+    private Hashtable signatures = new Hashtable(5);
 
-	private Hashtable certificates = new Hashtable(5);
-
-	private Hashtable verifiedEntries = new Hashtable();
-
-	byte[] mainAttributesChunk;
-
-	/**
-	 * TODO Type description
-	 */
-	static class VerifierEntry extends OutputStream {
-		/**
-		 * Comment for <code>digest</code>
-		 */
-		MessageDigest digest;
-
-		/**
-		 * Comment for <code>hash</code>
-		 */
-		byte[] hash;
-
-		/**
-		 * Comment for <code>certificates</code>
-		 */
-		java.security.cert.Certificate[] certificates;
-
-		/**
-		 * @param digest
-		 * @param hash
-		 * @param certificates
-		 */
-		VerifierEntry(MessageDigest digest, byte[] hash,
-				java.security.cert.Certificate[] certificates) {
-			this.digest = digest;
-			this.hash = hash;
-			this.certificates = certificates;
-		}
-
-		/*
-		 * (non-Javadoc)
-		 * 
-		 * @see java.io.OutputStream#write(int)
-		 */
-		public void write(int value) {
-			digest.update((byte) value);
-		}
-
-		/*
-		 * (non-Javadoc)
-		 * 
-		 * @see java.io.OutputStream#write(byte[], int, int)
-		 */
-		public void write(byte[] buf, int off, int nbytes) {
-			digest.update(buf, off, nbytes);
-		}
-	}
-
-	/**
-	 * Constructs and answers with a new instance of JarVerifier.
-	 * 
-	 * @param name
-	 *            the name of the jar file being verified.
-	 */
-	JarVerifier(String name) {
-		jarName = name;
-	}
-
-	/**
-	 * Called for each new jar entry read in from the input stream. This method
-	 * constructs and returns a new {@link VerifierEntry} which contains the
-	 * certificates used to sign the entry and its hash value as specified in
-	 * the jar manifest.
-	 * 
-	 * @param name
-	 *            the name of an entry in a jar file which is <b>not</b> in the
-	 *            <code>META-INF</code> directory.
-	 * @return a new instance of {@link VerifierEntry} which can be used by
-	 *         callers as an {@link OutputStream}.
-	 */
-	VerifierEntry initEntry(String name) {
-		// If no manifest is present by the time an entry is found,
-		// verification cannot occur. If no signature files have
-		// been found, do not verify.
-		if (man == null || signatures.size() == 0)
-			return null;
-
-		Attributes attributes = man.getAttributes(name);
-		// entry has no digest
-		if (attributes == null)
-			return null;
-
-		Vector certs = new Vector();
-		Iterator it = signatures.entrySet().iterator();
-		while (it.hasNext()) {
-			Map.Entry entry = (Map.Entry) it.next();
-			HashMap hm = (HashMap) entry.getValue();
-			if (hm.get(name) != null) {
-				// Found an entry for entry name in .SF file
-				String signatureFile = (String) entry.getKey();
-
-				Vector newCerts = getSignerCertificates(signatureFile,
-						certificates);
-				Iterator iter = newCerts.iterator();
-				while (iter.hasNext()) {
-					certs.add(iter.next());
-				}
-			}
-		}
-
-		// entry is not signed
-		if (certs.size() == 0)
-			return null;
-		Certificate[] certificatesArray = new Certificate[certs.size()];
-		certs.toArray(certificatesArray);
-
-		String algorithms = attributes.getValue("Digest-Algorithms");
-		if (algorithms == null)
-			algorithms = "SHA SHA1";
-		StringTokenizer tokens = new StringTokenizer(algorithms);
-		while (tokens.hasMoreTokens()) {
-			String algorithm = tokens.nextToken();
-			String hash = attributes.getValue(algorithm + "-Digest");
-			if (hash == null)
-				continue;
-			byte[] hashBytes;
-			try {
-				hashBytes = hash.getBytes("ISO8859_1");
-			} catch (UnsupportedEncodingException e) {
-				throw new RuntimeException(e.toString());
-			}
-
-			try {
-				return new VerifierEntry(MessageDigest.getInstance(algorithm),
-						hashBytes, certificatesArray);
-			} catch (NoSuchAlgorithmException e) {
-			}
-		}
-		return null;
-	}
-
-	/**
-	 * Add a new meta entry to the internal collection of data held on each jar
-	 * entry in the <code>META-INF</code> directory including the manifest
-	 * file itself. Files associated with the signing of a jar would also be
-	 * added to this collection.
-	 * 
-	 * @param name
-	 *            the name of the file located in the <code>META-INF</code>
-	 *            directory.
-	 * @param buf
-	 *            the file bytes for the file called <code>name</code>.
-	 * @see #removeMetaEntries()
-	 */
-	void addMetaEntry(String name, byte[] buf) {
-		metaEntries.put(name.toUpperCase(), buf);
-	}
-
-	/**
-	 * If the associated jar file is signed, check on the validity of all of the
-	 * known signatures.
-	 * 
-	 * @return <code>true</code> if the associated jar is signed and an
-	 *         internal check verifies the validity of the signature(s).
-	 *         <code>false</code> if the associated jar file has no entries at
-	 *         all in its <code>META-INF</code> directory. This situation is
-	 *         indicative of an invalid jar file.
-	 *         <p>
-	 *         Will also return true if the jar file is <i>not</i> signed.
-	 *         </p>
-	 * @throws SecurityException
-	 *             if the jar file is signed and it is determined that a
-	 *             signature block file contains an invalid signature for the
-	 *             corresponding signature file.
-	 */
-	synchronized boolean readCertificates() {
-		if (metaEntries == null)
-			return false;
-		Iterator it = metaEntries.keySet().iterator();
-		while (it.hasNext()) {
-			String key = (String) it.next();
-			if (key.endsWith(".DSA") || key.endsWith(".RSA")) {
-				verifyCertificate(key);
-				// Check for recursive class load
-				if (metaEntries == null)
-					return false;
-				it.remove();
-			}
-		}
-		return true;
-	}
-
-	/**
-	 * @param certFile
-	 */
-	private void verifyCertificate(String certFile) {
-		// Found Digital Sig, .SF should already have been read
-		String signatureFile = certFile.substring(0, certFile.lastIndexOf('.'))
-				+ ".SF";
-		byte[] sfBytes = (byte[]) metaEntries.get(signatureFile);
-		if (sfBytes == null)
-			return;
-
-		byte[] sBlockBytes = (byte[]) metaEntries.get(certFile);
-		try {
-			Certificate[] signerCertChain = JarUtils.verifySignature(
-					new ByteArrayInputStream(sfBytes),
-					new ByteArrayInputStream(sBlockBytes));
-			if (signerCertChain != null) {
-				this.certificates.put(signatureFile, signerCertChain);
-			}
-		} catch (IOException e) {
-			return;
-		} catch (GeneralSecurityException e) {
-			/* [MSG "K00eb", "{0} failed verification of {1}"] */
-			throw new SecurityException(Msg.getString("K00eb", jarName,
-					signatureFile));
-		}
-
-		// Verify manifest hash in .sf file
-		Attributes attributes = new Attributes();
-		HashMap hm = new HashMap();
-		try {
-			new InitManifest(new ByteArrayInputStream(sfBytes), attributes, hm,
-					null, "Signature-Version");
-		} catch (IOException e) {
-			return;
-		}
-
-		boolean createdBySigntool = false;
-		String createdByValue = attributes.getValue("Created-By");
-		if (createdByValue != null) {
-			createdBySigntool = createdByValue.indexOf("signtool") != -1;
-		}
-
-		// Use .SF to verify the mainAttributes of the manifest
-		// If there is no -Digest-Manifest-Main-Attributes entry in .SF
-		// file, such as those created before java 1.5, then we ignore
-		// such verification.
-		// FIXME: The meaning of createdBySigntool
-		if (this.mainAttributesChunk != null && !createdBySigntool) {
-			String digestAttribute = "-Digest-Manifest-Main-Attributes";
-			if (!verify(attributes, digestAttribute, this.mainAttributesChunk,
-					false, true)) {
-				/* [MSG "K00eb", "{0} failed verification of {1}"] */
-				throw new SecurityException(Msg.getString("K00eb", jarName,
-						signatureFile));
-			}
-		}
-
-		byte[] manifest = (byte[]) metaEntries.get(JarFile.MANIFEST_NAME);
-		if (manifest == null)
-			return;
-		// Use .SF to verify the whole manifest
-		String digestAttribute = createdBySigntool ? "-Digest"
-				: "-Digest-Manifest";
-		if (!verify(attributes, digestAttribute, manifest, false,false)) {
-			Iterator it = hm.entrySet().iterator();
-			while (it.hasNext()) {
-				Map.Entry entry = (Map.Entry) it.next();
-				byte[] chunk = man.getChunk((String) entry.getKey());
-				if (chunk == null)
-					return;
-				if (!verify((Attributes) entry.getValue(), "-Digest", chunk,
-						createdBySigntool,false))
-					/* [MSG "K00ec", "{0} has invalid digest for {1} in {2}"] */
-					throw new SecurityException(Msg.getString("K00ec",
-							new Object[] { signatureFile, entry.getKey(),
-									jarName }));
-			}
-		}
-		metaEntries.put(signatureFile, null);
-		signatures.put(signatureFile, hm);
-	}
-
-	/**
-	 * Associate this verifier with the specified {@link Manifest} object.
-	 * 
-	 * @param mf
-	 *            a <code>java.util.jar.Manifest</code> object.
-	 */
-	void setManifest(Manifest mf) {
-		man = mf;
-	}
-
-	/**
-	 * Verifies that the digests stored in the manifest match the decrypted
-	 * digests from the .SF file. This indicates the validity of the signing,
-	 * not the integrity of the file, as it's digest must be calculated and
-	 * verified when its contents are read.
-	 * 
-	 * @param entry
-	 *            the {@link VerifierEntry} associated with the specified
-	 *            <code>zipEntry</code>.
-	 * @param zipEntry
-	 *            an entry in the jar file
-	 * @throws SecurityException
-	 *             if the digest value stored in the manifest does <i>not</i>
-	 *             agree with the decrypted digest as recovered from the
-	 *             <code>.SF</code> file.
-	 * @see #initEntry(String)
-	 */
-	void verifySignatures(VerifierEntry entry, ZipEntry zipEntry) {
-		byte[] digest = entry.digest.digest();
-		if (!MessageDigest.isEqual(digest, Base64.decode(entry.hash)))
-			/* [MSG "K00ec", "{0} has invalid digest for {1} in {2}"] */
-			throw new SecurityException(Msg.getString("K00ec", new Object[] {
-					JarFile.MANIFEST_NAME, zipEntry.getName(), jarName }));
-		verifiedEntries.put(zipEntry.getName(), entry.certificates);
-		if (zipEntry instanceof JarEntry)
-			((JarEntry) zipEntry).certificates = (Certificate[]) entry.certificates
-					.clone();
-	}
-
-	/**
-	 * Returns a <code>boolean</code> indication of whether or not the
-	 * associated jar file is signed.
-	 * 
-	 * @return <code>true</code> if the jar is signed, <code>false</code>
-	 *         otherwise.
-	 */
-	boolean isSignedJar() {
-		return certificates.size() > 0;
-	}
-
-	/*
-	 * @param attributes @param entry @param data @param ignoreSecondEndline
-	 * @param ignorable @return
-	 */
-	private boolean verify(Attributes attributes, String entry, byte[] data,
-			boolean ignoreSecondEndline, boolean ignorable) {
-		String algorithms = attributes.getValue("Digest-Algorithms");
-		if (algorithms == null)
-			algorithms = "SHA SHA1";
-		StringTokenizer tokens = new StringTokenizer(algorithms);
-		while (tokens.hasMoreTokens()) {
-			String algorithm = tokens.nextToken();
-			String hash = attributes.getValue(algorithm + entry);
-			if (hash == null)
-				continue;
-
-			MessageDigest md;
-			try {
-				md = MessageDigest.getInstance(algorithm);
-			} catch (NoSuchAlgorithmException e) {
-				continue;
-			}
-			if (ignoreSecondEndline && data[data.length - 1] == '\n'
-					&& data[data.length - 2] == '\n') {
-				md.update(data, 0, data.length - 1);
-			} else {
-				md.update(data, 0, data.length);
-			}
-			byte[] b = md.digest();
-			byte[] hashBytes;
-			try {
-				hashBytes = hash.getBytes("ISO8859_1");
-			} catch (UnsupportedEncodingException e) {
-				throw new RuntimeException(e.toString());
-			}
-			return MessageDigest.isEqual(b, Base64.decode(hashBytes));
-		}
-		if (ignorable) {
-			return true;
-		} else {
-		return false;
-	}
-	}
-
-	/**
-	 * Returns all of the {@link java.security.cert.Certificate} instances that
-	 * were used to verify the signature on the jar entry called
-	 * <code>name</code>.
-	 * 
-	 * @param name
-	 *            the name of a jar entry.
-	 * @return an array of {@link java.security.cert.Certificate}.
-	 */
-	Certificate[] getCertificates(String name) {
-		Certificate[] verifiedCerts = (Certificate[]) verifiedEntries.get(name);
-		if (verifiedCerts == null) {
-			return null;
-		}
-		return (Certificate[]) verifiedCerts.clone();
-	}
-
-	/**
-	 * Remove all entries from the internal collection of data held about each
-	 * jar entry in the <code>META-INF</code> directory.
-	 * 
-	 * @see #addMetaEntry(String, byte[])
-	 */
-	void removeMetaEntries() {
-		metaEntries = null;
-	}
-
-	/**
-	 * Returns a <code>Vector</code> of all of the
-	 * {@link java.security.cert.Certificate}s that are associated with the
-	 * signing of the named signature file.
-	 * 
-	 * @param signatureFileName
-	 *            the name of a signature file
-	 * @param certificates
-	 *            a <code>Map</code> of all of the certificate chains
-	 *            discovered so far while attempting to verify the jar that
-	 *            contains the signature file <code>signatureFileName</code>.
-	 *            This object will have been previously set in the course of one
-	 *            or more calls to
-	 *            {@link #verifyJarSignatureFile(String, String, String, Map, Map)}
-	 *            where it was passed in as the last argument.
-	 * @return all of the <code>Certificate</code> entries for the signer of
-	 *         the jar whose actions led to the creation of the named signature
-	 *         file.
-	 */
-	public static Vector getSignerCertificates(String signatureFileName,
-			Map certificates) {
-		Vector result = new Vector();
-		Certificate[] certChain = (Certificate[]) certificates
-				.get(signatureFileName);
-		if (certChain != null) {
-			for (int i = 0; i < certChain.length; i++) {
-				result.add(certChain[i]);
-			}// end for all certificates
-		}// end if at least one cert found
-		return result;
-	}
+    private Hashtable certificates = new Hashtable(5);
+
+    private Hashtable verifiedEntries = new Hashtable();
+
+    byte[] mainAttributesChunk;
+
+    /**
+     * TODO Type description
+     */
+    static class VerifierEntry extends OutputStream {
+        /**
+         * Comment for <code>digest</code>
+         */
+        MessageDigest digest;
+
+        /**
+         * Comment for <code>hash</code>
+         */
+        byte[] hash;
+
+        /**
+         * Comment for <code>certificates</code>
+         */
+        java.security.cert.Certificate[] certificates;
+
+        /**
+         * @param digest
+         * @param hash
+         * @param certificates
+         */
+        VerifierEntry(MessageDigest digest, byte[] hash,
+                java.security.cert.Certificate[] certificates) {
+            this.digest = digest;
+            this.hash = hash;
+            this.certificates = certificates;
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see java.io.OutputStream#write(int)
+         */
+        public void write(int value) {
+            digest.update((byte) value);
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see java.io.OutputStream#write(byte[], int, int)
+         */
+        public void write(byte[] buf, int off, int nbytes) {
+            digest.update(buf, off, nbytes);
+        }
+    }
+
+    /**
+     * Constructs and answers with a new instance of JarVerifier.
+     * 
+     * @param name
+     *            the name of the jar file being verified.
+     */
+    JarVerifier(String name) {
+        jarName = name;
+    }
+
+    /**
+     * Called for each new jar entry read in from the input stream. This method
+     * constructs and returns a new {@link VerifierEntry} which contains the
+     * certificates used to sign the entry and its hash value as specified in
+     * the jar manifest.
+     * 
+     * @param name
+     *            the name of an entry in a jar file which is <b>not</b> in the
+     *            <code>META-INF</code> directory.
+     * @return a new instance of {@link VerifierEntry} which can be used by
+     *         callers as an {@link OutputStream}.
+     */
+    VerifierEntry initEntry(String name) {
+        // If no manifest is present by the time an entry is found,
+        // verification cannot occur. If no signature files have
+        // been found, do not verify.
+        if (man == null || signatures.size() == 0) {
+            return null;
+        }
+
+        Attributes attributes = man.getAttributes(name);
+        // entry has no digest
+        if (attributes == null) {
+            return null;
+        }
+
+        Vector certs = new Vector();
+        Iterator it = signatures.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry entry = (Map.Entry) it.next();
+            HashMap hm = (HashMap) entry.getValue();
+            if (hm.get(name) != null) {
+                // Found an entry for entry name in .SF file
+                String signatureFile = (String) entry.getKey();
+
+                Vector newCerts = getSignerCertificates(signatureFile,
+                        certificates);
+                Iterator iter = newCerts.iterator();
+                while (iter.hasNext()) {
+                    certs.add(iter.next());
+                }
+            }
+        }
+
+        // entry is not signed
+        if (certs.size() == 0) {
+            return null;
+        }
+        Certificate[] certificatesArray = new Certificate[certs.size()];
+        certs.toArray(certificatesArray);
+
+        String algorithms = attributes.getValue("Digest-Algorithms");
+        if (algorithms == null) {
+            algorithms = "SHA SHA1";
+        }
+        StringTokenizer tokens = new StringTokenizer(algorithms);
+        while (tokens.hasMoreTokens()) {
+            String algorithm = tokens.nextToken();
+            String hash = attributes.getValue(algorithm + "-Digest");
+            if (hash == null) {
+                continue;
+            }
+            byte[] hashBytes;
+            try {
+                hashBytes = hash.getBytes("ISO8859_1");
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException(e.toString());
+            }
+
+            try {
+                return new VerifierEntry(MessageDigest.getInstance(algorithm),
+                        hashBytes, certificatesArray);
+            } catch (NoSuchAlgorithmException e) {
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Add a new meta entry to the internal collection of data held on each jar
+     * entry in the <code>META-INF</code> directory including the manifest
+     * file itself. Files associated with the signing of a jar would also be
+     * added to this collection.
+     * 
+     * @param name
+     *            the name of the file located in the <code>META-INF</code>
+     *            directory.
+     * @param buf
+     *            the file bytes for the file called <code>name</code>.
+     * @see #removeMetaEntries()
+     */
+    void addMetaEntry(String name, byte[] buf) {
+        metaEntries.put(name.toUpperCase(), buf);
+    }
+
+    /**
+     * If the associated jar file is signed, check on the validity of all of the
+     * known signatures.
+     * 
+     * @return <code>true</code> if the associated jar is signed and an
+     *         internal check verifies the validity of the signature(s).
+     *         <code>false</code> if the associated jar file has no entries at
+     *         all in its <code>META-INF</code> directory. This situation is
+     *         indicative of an invalid jar file.
+     *         <p>
+     *         Will also return true if the jar file is <i>not</i> signed.
+     *         </p>
+     * @throws SecurityException
+     *             if the jar file is signed and it is determined that a
+     *             signature block file contains an invalid signature for the
+     *             corresponding signature file.
+     */
+    synchronized boolean readCertificates() {
+        if (metaEntries == null) {
+            return false;
+        }
+        Iterator it = metaEntries.keySet().iterator();
+        while (it.hasNext()) {
+            String key = (String) it.next();
+            if (key.endsWith(".DSA") || key.endsWith(".RSA")) {
+                verifyCertificate(key);
+                // Check for recursive class load
+                if (metaEntries == null) {
+                    return false;
+                }
+                it.remove();
+            }
+        }
+        return true;
+    }
+
+    /**
+     * @param certFile
+     */
+    private void verifyCertificate(String certFile) {
+        // Found Digital Sig, .SF should already have been read
+        String signatureFile = certFile.substring(0, certFile.lastIndexOf('.'))
+                + ".SF";
+        byte[] sfBytes = (byte[]) metaEntries.get(signatureFile);
+        if (sfBytes == null) {
+            return;
+        }
+
+        byte[] sBlockBytes = (byte[]) metaEntries.get(certFile);
+        try {
+            Certificate[] signerCertChain = JarUtils.verifySignature(
+                    new ByteArrayInputStream(sfBytes),
+                    new ByteArrayInputStream(sBlockBytes));
+            if (signerCertChain != null) {
+                this.certificates.put(signatureFile, signerCertChain);
+            }
+        } catch (IOException e) {
+            return;
+        } catch (GeneralSecurityException e) {
+            /* [MSG "K00eb", "{0} failed verification of {1}"] */
+            throw new SecurityException(Msg.getString("K00eb", jarName,
+                    signatureFile));
+        }
+
+        // Verify manifest hash in .sf file
+        Attributes attributes = new Attributes();
+        HashMap hm = new HashMap();
+        try {
+            new InitManifest(new ByteArrayInputStream(sfBytes), attributes, hm,
+                    null, "Signature-Version");
+        } catch (IOException e) {
+            return;
+        }
+
+        boolean createdBySigntool = false;
+        String createdByValue = attributes.getValue("Created-By");
+        if (createdByValue != null) {
+            createdBySigntool = createdByValue.indexOf("signtool") != -1;
+        }
+
+        // Use .SF to verify the mainAttributes of the manifest
+        // If there is no -Digest-Manifest-Main-Attributes entry in .SF
+        // file, such as those created before java 1.5, then we ignore
+        // such verification.
+        // FIXME: The meaning of createdBySigntool
+        if (this.mainAttributesChunk != null && !createdBySigntool) {
+            String digestAttribute = "-Digest-Manifest-Main-Attributes";
+            if (!verify(attributes, digestAttribute, this.mainAttributesChunk,
+                    false, true)) {
+                /* [MSG "K00eb", "{0} failed verification of {1}"] */
+                throw new SecurityException(Msg.getString("K00eb", jarName,
+                        signatureFile));
+            }
+        }
+
+        byte[] manifest = (byte[]) metaEntries.get(JarFile.MANIFEST_NAME);
+        if (manifest == null) {
+            return;
+        }
+        // Use .SF to verify the whole manifest
+        String digestAttribute = createdBySigntool ? "-Digest"
+                : "-Digest-Manifest";
+        if (!verify(attributes, digestAttribute, manifest, false, false)) {
+            Iterator it = hm.entrySet().iterator();
+            while (it.hasNext()) {
+                Map.Entry entry = (Map.Entry) it.next();
+                byte[] chunk = man.getChunk((String) entry.getKey());
+                if (chunk == null) {
+                    return;
+                }
+                if (!verify((Attributes) entry.getValue(), "-Digest", chunk,
+                        createdBySigntool, false)) {
+                    /* [MSG "K00ec", "{0} has invalid digest for {1} in {2}"] */
+                    throw new SecurityException(Msg.getString("K00ec",
+                            new Object[] { signatureFile, entry.getKey(),
+                                    jarName }));
+                }
+            }
+        }
+        metaEntries.put(signatureFile, null);
+        signatures.put(signatureFile, hm);
+    }
+
+    /**
+     * Associate this verifier with the specified {@link Manifest} object.
+     * 
+     * @param mf
+     *            a <code>java.util.jar.Manifest</code> object.
+     */
+    void setManifest(Manifest mf) {
+        man = mf;
+    }
+
+    /**
+     * Verifies that the digests stored in the manifest match the decrypted
+     * digests from the .SF file. This indicates the validity of the signing,
+     * not the integrity of the file, as it's digest must be calculated and
+     * verified when its contents are read.
+     * 
+     * @param entry
+     *            the {@link VerifierEntry} associated with the specified
+     *            <code>zipEntry</code>.
+     * @param zipEntry
+     *            an entry in the jar file
+     * @throws SecurityException
+     *             if the digest value stored in the manifest does <i>not</i>
+     *             agree with the decrypted digest as recovered from the
+     *             <code>.SF</code> file.
+     * @see #initEntry(String)
+     */
+    void verifySignatures(VerifierEntry entry, ZipEntry zipEntry) {
+        byte[] digest = entry.digest.digest();
+        if (!MessageDigest.isEqual(digest, Base64.decode(entry.hash))) {
+            /* [MSG "K00ec", "{0} has invalid digest for {1} in {2}"] */
+            throw new SecurityException(Msg.getString("K00ec", new Object[] {
+                    JarFile.MANIFEST_NAME, zipEntry.getName(), jarName }));
+        }
+        verifiedEntries.put(zipEntry.getName(), entry.certificates);
+        if (zipEntry instanceof JarEntry) {
+            ((JarEntry) zipEntry).certificates = entry.certificates.clone();
+        }
+    }
+
+    /**
+     * Returns a <code>boolean</code> indication of whether or not the
+     * associated jar file is signed.
+     * 
+     * @return <code>true</code> if the jar is signed, <code>false</code>
+     *         otherwise.
+     */
+    boolean isSignedJar() {
+        return certificates.size() > 0;
+    }
+
+    /*
+     * @param attributes @param entry @param data @param ignoreSecondEndline
+     * @param ignorable @return
+     */
+    private boolean verify(Attributes attributes, String entry, byte[] data,
+            boolean ignoreSecondEndline, boolean ignorable) {
+        String algorithms = attributes.getValue("Digest-Algorithms");
+        if (algorithms == null) {
+            algorithms = "SHA SHA1";
+        }
+        StringTokenizer tokens = new StringTokenizer(algorithms);
+        while (tokens.hasMoreTokens()) {
+            String algorithm = tokens.nextToken();
+            String hash = attributes.getValue(algorithm + entry);
+            if (hash == null) {
+                continue;
+            }
+
+            MessageDigest md;
+            try {
+                md = MessageDigest.getInstance(algorithm);
+            } catch (NoSuchAlgorithmException e) {
+                continue;
+            }
+            if (ignoreSecondEndline && data[data.length - 1] == '\n'
+                    && data[data.length - 2] == '\n') {
+                md.update(data, 0, data.length - 1);
+            } else {
+                md.update(data, 0, data.length);
+            }
+            byte[] b = md.digest();
+            byte[] hashBytes;
+            try {
+                hashBytes = hash.getBytes("ISO8859_1");
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException(e.toString());
+            }
+            return MessageDigest.isEqual(b, Base64.decode(hashBytes));
+        }
+        if (ignorable) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns all of the {@link java.security.cert.Certificate} instances that
+     * were used to verify the signature on the jar entry called
+     * <code>name</code>.
+     * 
+     * @param name
+     *            the name of a jar entry.
+     * @return an array of {@link java.security.cert.Certificate}.
+     */
+    Certificate[] getCertificates(String name) {
+        Certificate[] verifiedCerts = (Certificate[]) verifiedEntries.get(name);
+        if (verifiedCerts == null) {
+            return null;
+        }
+        return verifiedCerts.clone();
+    }
+
+    /**
+     * Remove all entries from the internal collection of data held about each
+     * jar entry in the <code>META-INF</code> directory.
+     * 
+     * @see #addMetaEntry(String, byte[])
+     */
+    void removeMetaEntries() {
+        metaEntries = null;
+    }
+
+    /**
+     * Returns a <code>Vector</code> of all of the
+     * {@link java.security.cert.Certificate}s that are associated with the
+     * signing of the named signature file.
+     * 
+     * @param signatureFileName
+     *            the name of a signature file
+     * @param certificates
+     *            a <code>Map</code> of all of the certificate chains
+     *            discovered so far while attempting to verify the jar that
+     *            contains the signature file <code>signatureFileName</code>.
+     *            This object will have been previously set in the course of one
+     *            or more calls to
+     *            {@link #verifyJarSignatureFile(String, String, String, Map, Map)}
+     *            where it was passed in as the last argument.
+     * @return all of the <code>Certificate</code> entries for the signer of
+     *         the jar whose actions led to the creation of the named signature
+     *         file.
+     */
+    public static Vector getSignerCertificates(String signatureFileName,
+            Map certificates) {
+        Vector result = new Vector();
+        Certificate[] certChain = (Certificate[]) certificates
+                .get(signatureFileName);
+        if (certChain != null) {
+            for (Certificate element : certChain) {
+                result.add(element);
+            }
+        }
+        return result;
+    }
 }