Return-Path: Delivered-To: apmail-incubator-harmony-commits-archive@www.apache.org Received: (qmail 55149 invoked from network); 31 May 2006 15:00:05 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 31 May 2006 15:00:05 -0000 Received: (qmail 62416 invoked by uid 500); 31 May 2006 15:00:05 -0000 Delivered-To: apmail-incubator-harmony-commits-archive@incubator.apache.org Received: (qmail 62386 invoked by uid 500); 31 May 2006 15:00:05 -0000 Mailing-List: contact harmony-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: harmony-dev@incubator.apache.org Delivered-To: mailing list harmony-commits@incubator.apache.org Received: (qmail 62373 invoked by uid 99); 31 May 2006 15:00:05 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 31 May 2006 08:00:05 -0700 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [140.211.166.113] (HELO eris.apache.org) (140.211.166.113) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 31 May 2006 08:00:03 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id C59541A9842; Wed, 31 May 2006 07:59:43 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r410554 - /incubator/harmony/enhanced/classlib/trunk/modules/archive/src/main/java/java/util/jar/JarVerifier.java Date: Wed, 31 May 2006 14:59:43 -0000 To: harmony-commits@incubator.apache.org From: tellison@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20060531145943.C59541A9842@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N 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 digest - */ - MessageDigest digest; - - /** - * Comment for hash - */ - byte[] hash; - - /** - * Comment for certificates - */ - 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 not in the - * META-INF 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 META-INF 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 META-INF - * directory. - * @param buf - * the file bytes for the file called name. - * @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 true if the associated jar is signed and an - * internal check verifies the validity of the signature(s). - * false if the associated jar file has no entries at - * all in its META-INF directory. This situation is - * indicative of an invalid jar file. - *

- * Will also return true if the jar file is not signed. - *

- * @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 java.util.jar.Manifest 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 - * zipEntry. - * @param zipEntry - * an entry in the jar file - * @throws SecurityException - * if the digest value stored in the manifest does not - * agree with the decrypted digest as recovered from the - * .SF 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 boolean indication of whether or not the - * associated jar file is signed. - * - * @return true if the jar is signed, false - * 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 - * name. - * - * @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 META-INF directory. - * - * @see #addMetaEntry(String, byte[]) - */ - void removeMetaEntries() { - metaEntries = null; - } - - /** - * Returns a Vector 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 Map of all of the certificate chains - * discovered so far while attempting to verify the jar that - * contains the signature file signatureFileName. - * 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 Certificate 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 digest + */ + MessageDigest digest; + + /** + * Comment for hash + */ + byte[] hash; + + /** + * Comment for certificates + */ + 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 not in the + * META-INF 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 META-INF 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 META-INF + * directory. + * @param buf + * the file bytes for the file called name. + * @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 true if the associated jar is signed and an + * internal check verifies the validity of the signature(s). + * false if the associated jar file has no entries at + * all in its META-INF directory. This situation is + * indicative of an invalid jar file. + *

+ * Will also return true if the jar file is not signed. + *

+ * @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 java.util.jar.Manifest 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 + * zipEntry. + * @param zipEntry + * an entry in the jar file + * @throws SecurityException + * if the digest value stored in the manifest does not + * agree with the decrypted digest as recovered from the + * .SF 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 boolean indication of whether or not the + * associated jar file is signed. + * + * @return true if the jar is signed, false + * 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 + * name. + * + * @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 META-INF directory. + * + * @see #addMetaEntry(String, byte[]) + */ + void removeMetaEntries() { + metaEntries = null; + } + + /** + * Returns a Vector 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 Map of all of the certificate chains + * discovered so far while attempting to verify the jar that + * contains the signature file signatureFileName. + * 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 Certificate 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; + } }