diff options
Diffstat (limited to 'java/util/jar/JarFile.java')
-rw-r--r-- | java/util/jar/JarFile.java | 202 |
1 files changed, 57 insertions, 145 deletions
diff --git a/java/util/jar/JarFile.java b/java/util/jar/JarFile.java index 003d9cb5e..680773659 100644 --- a/java/util/jar/JarFile.java +++ b/java/util/jar/JarFile.java @@ -68,6 +68,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; @@ -149,6 +151,12 @@ public class JarFile extends ZipFile */ HashMap entryCerts; + /** + * A {@link Map} of message digest algorithm names to their implementation. + * Used to reduce object (algorithm implementation) instantiation. + */ + private HashMap digestAlgorithms = new HashMap(); + static boolean DEBUG = false; static void debug(Object msg) { @@ -374,19 +382,8 @@ public class JarFile extends ZipFile } jarfile.signaturesRead = true; // fudge it. } - - // Include the certificates only if we have asserted that the - // signatures are valid. This means the certificates will not be - // available if the entry hasn't been read yet. - if (jarfile.entryCerts != null - && jarfile.verified.get(zip.getName()) == Boolean.TRUE) - { - Set certs = (Set) jarfile.entryCerts.get(jar.getName()); - if (certs != null) - jar.certs = (Certificate[]) - certs.toArray(new Certificate[certs.size()]); - } } + jar.jarfile = jarfile; return jar; } } @@ -431,18 +428,7 @@ public class JarFile extends ZipFile } signaturesRead = true; } - // See the comments in the JarEnumeration for why we do this - // check. - if (DEBUG) - debug("entryCerts=" + entryCerts + " verified " + name - + " ? " + verified.get(name)); - if (entryCerts != null && verified.get(name) == Boolean.TRUE) - { - Set certs = (Set) entryCerts.get(name); - if (certs != null) - jarEntry.certs = (Certificate[]) - certs.toArray(new Certificate[certs.size()]); - } + jarEntry.jarfile = this; return jarEntry; } return null; @@ -599,6 +585,31 @@ public class JarFile extends ZipFile validCerts.clear(); } + // Read the manifest into a HashMap (String fileName, String entry) + // The fileName might be split into multiple lines in the manifest. + // Such additional lines will start with a space. + InputStream in = super.getInputStream(super.getEntry(MANIFEST_NAME)); + ByteArrayOutputStream baStream = new ByteArrayOutputStream(); + byte[] ba = new byte[1024]; + while (true) + { + int len = in.read(ba); + if (len < 0) + break; + baStream.write(ba, 0, len); + } + in.close(); + + HashMap hmManifestEntries = new HashMap(); + Pattern p = Pattern.compile("Name: (.+?\r?\n(?: .+?\r?\n)*)" + + ".+?-Digest: .+?\r?\n\r?\n"); + Matcher m = p.matcher(baStream.toString()); + while (m.find()) + { + String fileName = m.group(1).replaceAll("\r?\n ?", ""); + hmManifestEntries.put(fileName, m.group()); + } + // Phase 3: verify the signature file signatures against the manifest, // mapping the entry name to the target certificates. this.entryCerts = new HashMap(); @@ -614,7 +625,7 @@ public class JarFile extends ZipFile Map.Entry e2 = (Map.Entry) it2.next(); String entryname = String.valueOf(e2.getKey()); Attributes attr = (Attributes) e2.getValue(); - if (verifyHashes(entryname, attr)) + if (verifyHashes(entryname, attr, hmManifestEntries)) { if (DEBUG) debug("entry " + entryname + " has certificates " + certificates); @@ -721,39 +732,29 @@ public class JarFile extends ZipFile } /** - * Verifies that the digest(s) in a signature file were, in fact, made - * over the manifest entry for ENTRY. - * + * Verifies that the digest(s) in a signature file were, in fact, made over + * the manifest entry for ENTRY. + * * @param entry The entry name. * @param attr The attributes from the signature file to verify. + * @param hmManifestEntries Mappings of Jar file entry names to their manifest + * entry text; i.e. the base-64 encoding of their */ - private boolean verifyHashes(String entry, Attributes attr) + private boolean verifyHashes(String entry, Attributes attr, + HashMap hmManifestEntries) { int verified = 0; - // The bytes for ENTRY's manifest entry, which are signed in the - // signature file. - byte[] entryBytes = null; - try - { - ZipEntry e = super.getEntry(entry); - if (e == null) - { - if (DEBUG) - debug("verifyHashes: no entry '" + entry + "'"); - return false; - } - entryBytes = readManifestEntry(e); - } - catch (IOException ioe) + String stringEntry = (String) hmManifestEntries.get(entry); + if (stringEntry == null) { if (DEBUG) - { - debug(ioe); - ioe.printStackTrace(); - } + debug("could not find " + entry + " in manifest"); return false; } + // The bytes for ENTRY's manifest entry, which are signed in the + // signature file. + byte[] entryBytes = stringEntry.getBytes(); for (Iterator it = attr.entrySet().iterator(); it.hasNext(); ) { @@ -765,9 +766,14 @@ public class JarFile extends ZipFile try { byte[] hash = Base64InputStream.decode((String) e.getValue()); - MessageDigest md = MessageDigest.getInstance(alg, provider); - md.update(entryBytes); - byte[] hash2 = md.digest(); + MessageDigest md = (MessageDigest) digestAlgorithms.get(alg); + if (md == null) + { + md = MessageDigest.getInstance(alg, provider); + digestAlgorithms.put(alg, md); + } + md.reset(); + byte[] hash2 = md.digest(entryBytes); if (DEBUG) debug("verifying SF entry " + entry + " alg: " + md.getAlgorithm() + " expect=" + new java.math.BigInteger(hash).toString(16) @@ -801,100 +807,6 @@ public class JarFile extends ZipFile } /** - * Read the raw bytes that comprise a manifest entry. We can't use the - * Manifest object itself, because that loses information (such as line - * endings, and order of entries). - */ - private byte[] readManifestEntry(ZipEntry entry) throws IOException - { - InputStream in = super.getInputStream(super.getEntry(MANIFEST_NAME)); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] target = ("Name: " + entry.getName()).getBytes(); - int t = 0, c, prev = -1, state = 0, l = -1; - - while ((c = in.read()) != -1) - { -// if (DEBUG) -// debug("read " -// + (c == '\n' ? "\\n" : (c == '\r' ? "\\r" : String.valueOf((char) c))) -// + " state=" + state + " prev=" -// + (prev == '\n' ? "\\n" : (prev == '\r' ? "\\r" : String.valueOf((char) prev))) -// + " t=" + t + (t < target.length ? (" target[t]=" + (char) target[t]) : "") -// + " l=" + l); - switch (state) - { - - // Step 1: read until we find the "target" bytes: the start - // of the entry we need to read. - case 0: - if (((byte) c) != target[t]) - t = 0; - else - { - t++; - if (t == target.length) - { - out.write(target); - state = 1; - } - } - break; - - // Step 2: assert that there is a newline character after - // the "target" bytes. - case 1: - if (c != '\n' && c != '\r') - { - out.reset(); - t = 0; - state = 0; - } - else - { - out.write(c); - state = 2; - } - break; - - // Step 3: read this whole entry, until we reach an empty - // line. - case 2: - if (c == '\n') - { - out.write(c); - // NL always terminates a line. - if (l == 0 || (l == 1 && prev == '\r')) - return out.toByteArray(); - l = 0; - } - else - { - // Here we see a blank line terminated by a CR, - // followed by the next entry. Technically, `c' should - // always be 'N' at this point. - if (l == 1 && prev == '\r') - return out.toByteArray(); - out.write(c); - l++; - } - prev = c; - break; - - default: - throw new RuntimeException("this statement should be unreachable"); - } - } - - // The last entry, with a single CR terminating the line. - if (state == 2 && prev == '\r' && l == 0) - return out.toByteArray(); - - // We should not reach this point, we didn't find the entry (or, possibly, - // it is the last entry and is malformed). - throw new IOException("could not find " + entry + " in manifest"); - } - - /** * A utility class that verifies jar entries as they are read. */ private static class EntryInputStream extends FilterInputStream |