📄 jarfile.java
字号:
continue; } } } /** * 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. */ private boolean verifyHashes(String entry, Attributes attr) { 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) { if (DEBUG) { debug(ioe); ioe.printStackTrace(); } return false; } for (Iterator it = attr.entrySet().iterator(); it.hasNext(); ) { Map.Entry e = (Map.Entry) it.next(); String key = String.valueOf(e.getKey()); if (!key.endsWith(DIGEST_KEY_SUFFIX)) continue; String alg = key.substring(0, key.length() - DIGEST_KEY_SUFFIX.length()); try { byte[] hash = Base64InputStream.decode((String) e.getValue()); MessageDigest md = MessageDigest.getInstance(alg); md.update(entryBytes); byte[] hash2 = md.digest(); if (DEBUG) debug("verifying SF entry " + entry + " alg: " + md.getAlgorithm() + " expect=" + new java.math.BigInteger(hash).toString(16) + " comp=" + new java.math.BigInteger(hash2).toString(16)); if (!Arrays.equals(hash, hash2)) return false; verified++; } catch (IOException ioe) { if (DEBUG) { debug(ioe); ioe.printStackTrace(); } return false; } catch (NoSuchAlgorithmException nsae) { if (DEBUG) { debug(nsae); nsae.printStackTrace(); } return false; } } // We have to find at least one valid digest. return verified > 0; } /** * 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 { private final JarFile jarfile; private final long length; private long pos; private final ZipEntry entry; private final byte[][] hashes; private final MessageDigest[] md; private boolean checked; EntryInputStream(final ZipEntry entry, final InputStream in, final JarFile jar) throws IOException { super(in); this.entry = entry; this.jarfile = jar; length = entry.getSize(); pos = 0; checked = false; Attributes attr; Manifest manifest = jarfile.getManifest(); if (manifest != null) attr = manifest.getAttributes(entry.getName()); else attr = null; if (DEBUG) debug("verifying entry " + entry + " attr=" + attr); if (attr == null) { hashes = new byte[0][]; md = new MessageDigest[0]; } else { List hashes = new LinkedList(); List md = new LinkedList(); for (Iterator it = attr.entrySet().iterator(); it.hasNext(); ) { Map.Entry e = (Map.Entry) it.next(); String key = String.valueOf(e.getKey()); if (key == null) continue; if (!key.endsWith(DIGEST_KEY_SUFFIX)) continue; hashes.add(Base64InputStream.decode((String) e.getValue())); try { md.add(MessageDigest.getInstance (key.substring(0, key.length() - DIGEST_KEY_SUFFIX.length()))); } catch (NoSuchAlgorithmException nsae) { IOException ioe = new IOException("no such message digest: " + key); ioe.initCause(nsae); throw ioe; } } if (DEBUG) debug("digests=" + md); this.hashes = (byte[][]) hashes.toArray(new byte[hashes.size()][]); this.md = (MessageDigest[]) md.toArray(new MessageDigest[md.size()]); } } public boolean markSupported() { return false; } public void mark(int readLimit) { } public void reset() { } public int read() throws IOException { int b = super.read(); if (b == -1) { eof(); return -1; } for (int i = 0; i < md.length; i++) md[i].update((byte) b); pos++; if (length > 0 && pos >= length) eof(); return b; } public int read(byte[] buf, int off, int len) throws IOException { int count = super.read(buf, off, (int) Math.min(len, (length != 0 ? length - pos : Integer.MAX_VALUE))); if (count == -1 || (length > 0 && pos >= length)) { eof(); return -1; } for (int i = 0; i < md.length; i++) md[i].update(buf, off, count); pos += count; if (length != 0 && pos >= length) eof(); return count; } public int read(byte[] buf) throws IOException { return read(buf, 0, buf.length); } public long skip(long bytes) throws IOException { byte[] b = new byte[1024]; long amount = 0; while (amount < bytes) { int l = read(b, 0, (int) Math.min(b.length, bytes - amount)); if (l == -1) break; amount += l; } return amount; } private void eof() throws IOException { if (checked) return; checked = true; for (int i = 0; i < md.length; i++) { byte[] hash = md[i].digest(); if (DEBUG) debug("verifying " + md[i].getAlgorithm() + " expect=" + new java.math.BigInteger(hashes[i]).toString(16) + " comp=" + new java.math.BigInteger(hash).toString(16)); if (!Arrays.equals(hash, hashes[i])) { synchronized(jarfile) { if (DEBUG) debug(entry + " could NOT be verified"); jarfile.verified.put(entry.getName(), Boolean.FALSE); } return; // XXX ??? what do we do here? // throw new ZipException("message digest mismatch"); } } synchronized(jarfile) { if (DEBUG) debug(entry + " has been VERIFIED"); jarfile.verified.put(entry.getName(), Boolean.TRUE); } } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -