JarFile
class is used to read the contents of a jar file
@@ -178,7 +179,7 @@ class JarFile extends ZipFile {
byte[] b = getBytes(manEntry);
man = new Manifest(new ByteArrayInputStream(b));
if (!jvInitialized) {
- jv = new JarVerifier(b);
+ jv = new JarVerifier(b, man);
}
} else {
man = new Manifest(super.getInputStream(manEntry));
@@ -297,10 +298,7 @@ class JarFile extends ZipFile {
if (names != null) {
for (int i = 0; i < names.length; i++) {
String name = names[i].toUpperCase(Locale.ENGLISH);
- if (name.endsWith(".DSA") ||
- name.endsWith(".RSA") ||
- name.endsWith(".EC") ||
- name.endsWith(".SF")) {
+ if (SignatureFileVerifier.isBlockOrSF(name)) {
// Assume since we found a signature-related file
// that the jar is signed and that we therefore
// need a JarVerifier and Manifest
@@ -329,17 +327,17 @@ class JarFile extends ZipFile {
if (names != null) {
for (int i = 0; i < names.length; i++) {
JarEntry e = getJarEntry(names[i]);
- if (!e.isDirectory()) {
+ if (!e.isDirectory() &&
+ SignatureFileVerifier.isBlock(names[i])) {
if (mev == null) {
mev = new ManifestEntryVerifier
(getManifestFromReference());
}
- byte[] b = getBytes(e);
- if (b != null && b.length > 0) {
- jv.beginEntry(e, mev);
- jv.update(b.length, b, 0, b.length, mev);
- jv.update(-1, null, 0, 0, mev);
- }
+ String key = names[i].substring(
+ 0, names[i].lastIndexOf("."));
+ jv.verifyBlock(names[i],
+ getBytes(e),
+ super.getInputStream(getJarEntry(key + ".SF")));
}
}
}
diff --git a/jdk/src/share/classes/java/util/jar/JarInputStream.java b/jdk/src/share/classes/java/util/jar/JarInputStream.java
index 3c4553304b2..b84219ea87a 100644
--- a/jdk/src/share/classes/java/util/jar/JarInputStream.java
+++ b/jdk/src/share/classes/java/util/jar/JarInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -95,7 +95,7 @@ class JarInputStream extends ZipInputStream {
man.read(new ByteArrayInputStream(bytes));
closeEntry();
if (doVerify) {
- jv = new JarVerifier(bytes);
+ jv = new JarVerifier(bytes, man);
mev = new ManifestEntryVerifier(man);
}
return (JarEntry)super.getNextEntry();
diff --git a/jdk/src/share/classes/java/util/jar/JarVerifier.java b/jdk/src/share/classes/java/util/jar/JarVerifier.java
index 4f84ac28eff..8c69ff5c583 100644
--- a/jdk/src/share/classes/java/util/jar/JarVerifier.java
+++ b/jdk/src/share/classes/java/util/jar/JarVerifier.java
@@ -48,35 +48,18 @@ class JarVerifier {
/* a table mapping names to code signers, for jar entries that have
had their actual hashes verified */
- private Hashtable verifiedSigners;
+ private Map verifiedSigners;
/* a table mapping names to code signers, for jar entries that have
passed the .SF/.DSA/.EC -> MANIFEST check */
- private Hashtable sigFileSigners;
-
- /* a hash table to hold .SF bytes */
- private Hashtable sigFileData;
-
- /** "queue" of pending PKCS7 blocks that we couldn't parse
- * until we parsed the .SF file */
- private ArrayList pendingBlocks;
+ private Map sigFileSigners;
/* cache of CodeSigner objects */
private ArrayList signerCache;
- /* Are we parsing a block? */
- private boolean parsingBlockOrSF = false;
-
- /* Are we done parsing META-INF entries? */
- private boolean parsingMeta = true;
-
/* Are there are files to verify? */
private boolean anyToVerify = true;
- /* The output stream to use when keeping track of files we are interested
- in */
- private ByteArrayOutputStream baos;
-
/** The ManifestDigester object */
private volatile ManifestDigester manDig;
@@ -92,20 +75,20 @@ class JarVerifier {
/** collect -DIGEST-MANIFEST values for blacklist */
private List manifestDigests;
- public JarVerifier(byte rawBytes[]) {
+ /** The manifest object */
+ Manifest man = null;
+
+ public JarVerifier(byte rawBytes[], Manifest man) {
+ this.man = man;
manifestRawBytes = rawBytes;
- sigFileSigners = new Hashtable();
- verifiedSigners = new Hashtable();
- sigFileData = new Hashtable(11);
- pendingBlocks = new ArrayList();
- baos = new ByteArrayOutputStream();
+ sigFileSigners = new HashMap();
+ verifiedSigners = new HashMap();
manifestDigests = new ArrayList();
}
/**
- * This method scans to see which entry we're parsing and
- * keeps various state information depending on what type of
- * file is being parsed.
+ * This method scans to see which entry we're parsing and keeps
+ * various state information depending on the file being parsed.
*/
public void beginEntry(JarEntry je, ManifestEntryVerifier mev)
throws IOException
@@ -129,30 +112,6 @@ class JarVerifier {
* b. digest mismatch between the actual jar entry and the manifest
*/
- if (parsingMeta) {
- String uname = name.toUpperCase(Locale.ENGLISH);
- if ((uname.startsWith("META-INF/") ||
- uname.startsWith("/META-INF/"))) {
-
- if (je.isDirectory()) {
- mev.setEntry(null, je);
- return;
- }
-
- if (SignatureFileVerifier.isBlockOrSF(uname)) {
- /* We parse only DSA, RSA or EC PKCS7 blocks. */
- parsingBlockOrSF = true;
- baos.reset();
- mev.setEntry(null, je);
- }
- return;
- }
- }
-
- if (parsingMeta) {
- doneWithMeta();
- }
-
if (je.isDirectory()) {
mev.setEntry(null, je);
return;
@@ -188,11 +147,7 @@ class JarVerifier {
throws IOException
{
if (b != -1) {
- if (parsingBlockOrSF) {
- baos.write(b);
- } else {
- mev.update((byte)b);
- }
+ mev.update((byte)b);
} else {
processEntry(mev);
}
@@ -207,11 +162,7 @@ class JarVerifier {
throws IOException
{
if (n != -1) {
- if (parsingBlockOrSF) {
- baos.write(b, off, n);
- } else {
- mev.update(b, off, n);
- }
+ mev.update(b, off, n);
} else {
processEntry(mev);
}
@@ -223,101 +174,10 @@ class JarVerifier {
private void processEntry(ManifestEntryVerifier mev)
throws IOException
{
- if (!parsingBlockOrSF) {
- JarEntry je = mev.getEntry();
- if ((je != null) && (je.signers == null)) {
- je.signers = mev.verify(verifiedSigners, sigFileSigners);
- je.certs = mapSignersToCertArray(je.signers);
- }
- } else {
-
- try {
- parsingBlockOrSF = false;
-
- if (debug != null) {
- debug.println("processEntry: processing block");
- }
-
- String uname = mev.getEntry().getName()
- .toUpperCase(Locale.ENGLISH);
-
- if (uname.endsWith(".SF")) {
- String key = uname.substring(0, uname.length()-3);
- byte bytes[] = baos.toByteArray();
- // add to sigFileData in case future blocks need it
- sigFileData.put(key, bytes);
- // check pending blocks, we can now process
- // anyone waiting for this .SF file
- Iterator it = pendingBlocks.iterator();
- while (it.hasNext()) {
- SignatureFileVerifier sfv =
- (SignatureFileVerifier) it.next();
- if (sfv.needSignatureFile(key)) {
- if (debug != null) {
- debug.println(
- "processEntry: processing pending block");
- }
-
- sfv.setSignatureFile(bytes);
- sfv.process(sigFileSigners, manifestDigests);
- }
- }
- return;
- }
-
- // now we are parsing a signature block file
-
- String key = uname.substring(0, uname.lastIndexOf("."));
-
- if (signerCache == null)
- signerCache = new ArrayList();
-
- if (manDig == null) {
- synchronized(manifestRawBytes) {
- if (manDig == null) {
- manDig = new ManifestDigester(manifestRawBytes);
- manifestRawBytes = null;
- }
- }
- }
-
- SignatureFileVerifier sfv =
- new SignatureFileVerifier(signerCache,
- manDig, uname, baos.toByteArray());
-
- if (sfv.needSignatureFileBytes()) {
- // see if we have already parsed an external .SF file
- byte[] bytes = (byte[]) sigFileData.get(key);
-
- if (bytes == null) {
- // put this block on queue for later processing
- // since we don't have the .SF bytes yet
- // (uname, block);
- if (debug != null) {
- debug.println("adding pending block");
- }
- pendingBlocks.add(sfv);
- return;
- } else {
- sfv.setSignatureFile(bytes);
- }
- }
- sfv.process(sigFileSigners, manifestDigests);
-
- } catch (IOException ioe) {
- // e.g. sun.security.pkcs.ParsingException
- if (debug != null) debug.println("processEntry caught: "+ioe);
- // ignore and treat as unsigned
- } catch (SignatureException se) {
- if (debug != null) debug.println("processEntry caught: "+se);
- // ignore and treat as unsigned
- } catch (NoSuchAlgorithmException nsae) {
- if (debug != null) debug.println("processEntry caught: "+nsae);
- // ignore and treat as unsigned
- } catch (CertificateException ce) {
- if (debug != null) debug.println("processEntry caught: "+ce);
- // ignore and treat as unsigned
- }
+ JarEntry je = mev.getEntry();
+ if ((je != null) && (je.signers == null)) {
+ je.signers = mev.verify(verifiedSigners, sigFileSigners);
+ je.certs = mapSignersToCertArray(je.signers);
}
}
@@ -354,15 +214,15 @@ class JarVerifier {
* Force a read of the entry data to generate the
* verification hash.
*/
- try {
- InputStream s = jar.getInputStream(entry);
+ try (InputStream s = jar.getInputStream(entry)) {
byte[] buffer = new byte[1024];
int n = buffer.length;
while (n != -1) {
n = s.read(buffer, 0, buffer.length);
}
- s.close();
} catch (IOException e) {
+ // Ignore. When an exception is thrown, code signer
+ // will not be assigned.
}
}
return getCodeSigners(name);
@@ -408,11 +268,7 @@ class JarVerifier {
*/
void doneWithMeta()
{
- parsingMeta = false;
anyToVerify = !sigFileSigners.isEmpty();
- baos = null;
- sigFileData = null;
- pendingBlocks = null;
signerCache = null;
manDig = null;
// MANIFEST.MF is always treated as signed and verified,
@@ -423,6 +279,41 @@ class JarVerifier {
}
}
+ /**
+ * Verifies a PKCS7 SignedData block
+ * @param key name of block
+ * @param block the pkcs7 file
+ * @param ins the clear data
+ */
+ void verifyBlock(String key, byte[] block, InputStream ins) {
+ try {
+ if (signerCache == null)
+ signerCache = new ArrayList();
+
+ if (manDig == null) {
+ synchronized(manifestRawBytes) {
+ if (manDig == null) {
+ manDig = new ManifestDigester(manifestRawBytes);
+ manifestRawBytes = null;
+ }
+ }
+ }
+ SignatureFileVerifier sfv =
+ new SignatureFileVerifier(signerCache, man,
+ manDig, key, block);
+
+ if (sfv.needSignatureFile()) {
+ // see if we have already parsed an external .SF file
+ sfv.setSignatureFile(ins);
+ }
+ sfv.process(sigFileSigners, manifestDigests);
+ } catch (Exception e) {
+ if (debug != null) {
+ e.printStackTrace();
+ }
+ }
+ }
+
static class VerifierStream extends java.io.InputStream {
private InputStream is;
@@ -553,10 +444,7 @@ class JarVerifier {
* but this handles a CodeSource of any type, just in case.
*/
CodeSource[] sources = mapSignersToCodeSources(cs.getLocation(), getJarCodeSigners(), true);
- List sourceList = new ArrayList();
- for (int i = 0; i < sources.length; i++) {
- sourceList.add(sources[i]);
- }
+ List sourceList = Arrays.asList(sources);
int j = sourceList.indexOf(cs);
if (j != -1) {
CodeSigner[] match;
diff --git a/jdk/src/share/classes/sun/security/pkcs/PKCS7.java b/jdk/src/share/classes/sun/security/pkcs/PKCS7.java
index e80beb7372c..72a2e5f9f3e 100644
--- a/jdk/src/share/classes/sun/security/pkcs/PKCS7.java
+++ b/jdk/src/share/classes/sun/security/pkcs/PKCS7.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -38,6 +38,7 @@ import java.security.*;
import sun.security.util.*;
import sun.security.x509.AlgorithmId;
import sun.security.x509.CertificateIssuerName;
+import sun.security.x509.KeyUsageExtension;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;
import sun.security.x509.X509CRLImpl;
@@ -492,7 +493,7 @@ public class PKCS7 {
// CRLs (optional)
if (crls != null && crls.length != 0) {
// cast to X509CRLImpl[] since X509CRLImpl implements DerEncoder
- Set+ * This method must be called in a row, reading chunks from a single + * manifest file by order. After all chunks are read, caller must call + * {@code update(null)} to fully consume the manifest. + *
+ * The entry names and attributes read will be merged in with the current + * manifest entries. The {@link #read} method cannot be called inside a + * row of update calls. + *
+ * Along with the calls, caller can call {@link #getMainAttributes()},
+ * {@link #getAttributes(java.lang.String)} or {@link #getEntries()}
+ * to get already available contents. However, in order not to return
+ * partial result, when the main attributes in the new manifest is not
+ * consumed completely, {@link #getMainAttributes()} throws an
+ * {@code IllegalStateException}. When a certain named entry is not
+ * consumed completely, {@link #getAttributes(java.lang.String)}
+ * returns the old {@code Attributes} for the name (if it exists).
+ *
+ * @param data null for last call, otherwise, feeding chunks
+ * @param offset offset into data to begin read
+ * @param length length of data after offset to read
+ * @exception IOException if an I/O error has occurred
+ * @exception IllegalStateException if {@code update(null)} is called
+ * without any previous {@code update(non-null)} call
+ */
+ public void update(byte[] data, int offset, int length) throws IOException {
+
+ // The last call
+ if (data == null) {
+ if (state == 0) {
+ throw new IllegalStateException("No data to update");
+ }
+ // We accept manifest not ended with \n or \n\n
+ if (hasLastByte()) {
+ consumeCurrent();
+ }
+ // We accept empty lines at the end
+ if (!currentAttr.isEmpty()) {
+ consumeAttr();
+ }
+ state = 0; // back to non-update state
+ current = null;
+ currentAttr = null;
+ return;
+ }
+
+ // The first call
+ if (state == 0) {
+ current = new byte[1024];
+ currentAttr = super.getMainAttributes(); // the main attribute
+ state = 1;
+ }
+
+ int end = offset + length;
+
+ while (offset < end) {
+ switch (data[offset]) {
+ case '\r':
+ break; // always skip
+ case '\n':
+ if (hasLastByte() && lastByte() == '\n') { // new section
+ consumeCurrent();
+ consumeAttr();
+ if (state == 1) {
+ state = 2;
+ }
+ currentAttr = new Attributes(2);
+ } else {
+ if (hasLastByte()) {
+ // save \n into current but do not parse,
+ // there might be a continuation later
+ ensureCapacity();
+ current[currentPos++] = data[offset];
+ } else if (state == 1) {
+ // there can be multiple empty lines between
+ // sections, but cannot be at the beginning
+ throw new IOException("invalid manifest format");
+ }
+ }
+ break;
+ case ' ':
+ if (!hasLastByte()) {
+ throw new IOException("invalid manifest format");
+ } else if (lastByte() == '\n') {
+ currentPos--; // continuation, remove last \n
+ } else { // a very normal ' '
+ ensureCapacity();
+ current[currentPos++] = data[offset];
+ }
+ break;
+ default:
+ if (hasLastByte() && lastByte() == '\n') {
+ // The start of a new pair, not continuation
+ consumeCurrent(); // the last line read
+ }
+ ensureCapacity();
+ current[currentPos++] = data[offset];
+ break;
+ }
+ offset++;
+ }
+ }
+
+ /**
+ * Returns the main Attributes for the Manifest.
+ * @exception IllegalStateException the main attributes is being read
+ * @return the main Attributes for the Manifest
+ */
+ public Attributes getMainAttributes() {
+ if (state == 1) {
+ throw new IllegalStateException();
+ }
+ return super.getMainAttributes();
+ }
+
+ /**
+ * Reads the Manifest from the specified InputStream. The entry
+ * names and attributes read will be merged in with the current
+ * manifest entries.
+ *
+ * @param is the input stream
+ * @exception IOException if an I/O error has occurred
+ * @exception IllegalStateException if called between two {@link #update}
+ * calls
+ */
+ public void read(InputStream is) throws IOException {
+ if (state != 0) {
+ throw new IllegalStateException("Cannot call read between updates");
+ }
+ super.read(is);
+ }
+
+ /*
+ * ---------- Helper methods -----------------
+ */
+
+ private void ensureCapacity() {
+ if (currentPos >= current.length-1) {
+ current = Arrays.copyOf(current, current.length*2);
+ }
+ }
+
+ private boolean hasLastByte() {
+ return currentPos > 0;
+ }
+
+ private byte lastByte() {
+ return current[currentPos-1];
+ }
+
+ // Parse current as key:value and save into currentAttr.
+ // There MUST be something inside current.
+ private void consumeCurrent() throws IOException {
+ // current normally has a \n end, except for the last line
+ if (current[currentPos-1] == '\n') currentPos--;
+ for (int i=0; i