0N/A/*
3570N/A * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage java.util.jar;
0N/A
0N/Aimport java.io.*;
3570N/Aimport java.net.URL;
0N/Aimport java.util.*;
0N/Aimport java.security.*;
0N/Aimport java.security.cert.CertificateException;
3570N/Aimport java.util.zip.ZipEntry;
0N/A
0N/Aimport sun.security.util.ManifestDigester;
0N/Aimport sun.security.util.ManifestEntryVerifier;
0N/Aimport sun.security.util.SignatureFileVerifier;
0N/Aimport sun.security.util.Debug;
0N/A
0N/A/**
0N/A *
0N/A * @author Roland Schemers
0N/A */
0N/Aclass JarVerifier {
0N/A
0N/A /* Are we debugging ? */
0N/A static final Debug debug = Debug.getInstance("jar");
0N/A
0N/A /* a table mapping names to code signers, for jar entries that have
0N/A had their actual hashes verified */
4046N/A private Hashtable verifiedSigners;
0N/A
0N/A /* a table mapping names to code signers, for jar entries that have
1786N/A passed the .SF/.DSA/.EC -> MANIFEST check */
4046N/A private Hashtable sigFileSigners;
4046N/A
4046N/A /* a hash table to hold .SF bytes */
4046N/A private Hashtable sigFileData;
4046N/A
4046N/A /** "queue" of pending PKCS7 blocks that we couldn't parse
4046N/A * until we parsed the .SF file */
4046N/A private ArrayList pendingBlocks;
0N/A
0N/A /* cache of CodeSigner objects */
0N/A private ArrayList signerCache;
0N/A
4046N/A /* Are we parsing a block? */
4046N/A private boolean parsingBlockOrSF = false;
4046N/A
4046N/A /* Are we done parsing META-INF entries? */
4046N/A private boolean parsingMeta = true;
4046N/A
0N/A /* Are there are files to verify? */
0N/A private boolean anyToVerify = true;
0N/A
4046N/A /* The output stream to use when keeping track of files we are interested
4046N/A in */
4046N/A private ByteArrayOutputStream baos;
4046N/A
0N/A /** The ManifestDigester object */
2397N/A private volatile ManifestDigester manDig;
0N/A
0N/A /** the bytes for the manDig object */
0N/A byte manifestRawBytes[] = null;
0N/A
3570N/A /** controls eager signature validation */
3570N/A boolean eagerValidation;
3570N/A
3570N/A /** makes code source singleton instances unique to us */
3570N/A private Object csdomain = new Object();
3570N/A
3570N/A /** collect -DIGEST-MANIFEST values for blacklist */
3570N/A private List manifestDigests;
3570N/A
4046N/A public JarVerifier(byte rawBytes[]) {
0N/A manifestRawBytes = rawBytes;
4046N/A sigFileSigners = new Hashtable();
4046N/A verifiedSigners = new Hashtable();
4046N/A sigFileData = new Hashtable(11);
4046N/A pendingBlocks = new ArrayList();
4046N/A baos = new ByteArrayOutputStream();
3570N/A manifestDigests = new ArrayList();
0N/A }
0N/A
0N/A /**
4046N/A * This method scans to see which entry we're parsing and
4046N/A * keeps various state information depending on what type of
4046N/A * file is being parsed.
0N/A */
0N/A public void beginEntry(JarEntry je, ManifestEntryVerifier mev)
0N/A throws IOException
0N/A {
0N/A if (je == null)
0N/A return;
0N/A
0N/A if (debug != null) {
0N/A debug.println("beginEntry "+je.getName());
0N/A }
0N/A
0N/A String name = je.getName();
0N/A
0N/A /*
0N/A * Assumptions:
0N/A * 1. The manifest should be the first entry in the META-INF directory.
1786N/A * 2. The .SF/.DSA/.EC files follow the manifest, before any normal entries
0N/A * 3. Any of the following will throw a SecurityException:
0N/A * a. digest mismatch between a manifest section and
0N/A * the SF section.
0N/A * b. digest mismatch between the actual jar entry and the manifest
0N/A */
0N/A
4046N/A if (parsingMeta) {
4046N/A String uname = name.toUpperCase(Locale.ENGLISH);
4046N/A if ((uname.startsWith("META-INF/") ||
4046N/A uname.startsWith("/META-INF/"))) {
4046N/A
4046N/A if (je.isDirectory()) {
4046N/A mev.setEntry(null, je);
4046N/A return;
4046N/A }
4046N/A
4046N/A if (SignatureFileVerifier.isBlockOrSF(uname)) {
4046N/A /* We parse only DSA, RSA or EC PKCS7 blocks. */
4046N/A parsingBlockOrSF = true;
4046N/A baos.reset();
4046N/A mev.setEntry(null, je);
4046N/A }
4046N/A return;
4046N/A }
4046N/A }
4046N/A
4046N/A if (parsingMeta) {
4046N/A doneWithMeta();
4046N/A }
4046N/A
0N/A if (je.isDirectory()) {
0N/A mev.setEntry(null, je);
0N/A return;
0N/A }
0N/A
0N/A // be liberal in what you accept. If the name starts with ./, remove
0N/A // it as we internally canonicalize it with out the ./.
0N/A if (name.startsWith("./"))
0N/A name = name.substring(2);
0N/A
0N/A // be liberal in what you accept. If the name starts with /, remove
0N/A // it as we internally canonicalize it with out the /.
0N/A if (name.startsWith("/"))
0N/A name = name.substring(1);
0N/A
0N/A // only set the jev object for entries that have a signature
0N/A if (sigFileSigners.get(name) != null) {
0N/A mev.setEntry(name, je);
0N/A return;
0N/A }
0N/A
0N/A // don't compute the digest for this entry
0N/A mev.setEntry(null, je);
0N/A
0N/A return;
0N/A }
0N/A
0N/A /**
0N/A * update a single byte.
0N/A */
0N/A
0N/A public void update(int b, ManifestEntryVerifier mev)
0N/A throws IOException
0N/A {
0N/A if (b != -1) {
4046N/A if (parsingBlockOrSF) {
4046N/A baos.write(b);
4046N/A } else {
4046N/A mev.update((byte)b);
4046N/A }
0N/A } else {
0N/A processEntry(mev);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * update an array of bytes.
0N/A */
0N/A
0N/A public void update(int n, byte[] b, int off, int len,
0N/A ManifestEntryVerifier mev)
0N/A throws IOException
0N/A {
0N/A if (n != -1) {
4046N/A if (parsingBlockOrSF) {
4046N/A baos.write(b, off, n);
4046N/A } else {
4046N/A mev.update(b, off, n);
4046N/A }
0N/A } else {
0N/A processEntry(mev);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * called when we reach the end of entry in one of the read() methods.
0N/A */
0N/A private void processEntry(ManifestEntryVerifier mev)
0N/A throws IOException
0N/A {
4046N/A if (!parsingBlockOrSF) {
4046N/A JarEntry je = mev.getEntry();
4046N/A if ((je != null) && (je.signers == null)) {
4046N/A je.signers = mev.verify(verifiedSigners, sigFileSigners);
4046N/A je.certs = mapSignersToCertArray(je.signers);
4046N/A }
4046N/A } else {
4046N/A
4046N/A try {
4046N/A parsingBlockOrSF = false;
4046N/A
4046N/A if (debug != null) {
4046N/A debug.println("processEntry: processing block");
4046N/A }
4046N/A
4046N/A String uname = mev.getEntry().getName()
4046N/A .toUpperCase(Locale.ENGLISH);
4046N/A
4046N/A if (uname.endsWith(".SF")) {
4046N/A String key = uname.substring(0, uname.length()-3);
4046N/A byte bytes[] = baos.toByteArray();
4046N/A // add to sigFileData in case future blocks need it
4046N/A sigFileData.put(key, bytes);
4046N/A // check pending blocks, we can now process
4046N/A // anyone waiting for this .SF file
4046N/A Iterator it = pendingBlocks.iterator();
4046N/A while (it.hasNext()) {
4046N/A SignatureFileVerifier sfv =
4046N/A (SignatureFileVerifier) it.next();
4046N/A if (sfv.needSignatureFile(key)) {
4046N/A if (debug != null) {
4046N/A debug.println(
4046N/A "processEntry: processing pending block");
4046N/A }
4046N/A
4046N/A sfv.setSignatureFile(bytes);
4046N/A sfv.process(sigFileSigners, manifestDigests);
4046N/A }
4046N/A }
4046N/A return;
4046N/A }
4046N/A
4046N/A // now we are parsing a signature block file
4046N/A
4046N/A String key = uname.substring(0, uname.lastIndexOf("."));
4046N/A
4046N/A if (signerCache == null)
4046N/A signerCache = new ArrayList();
4046N/A
4046N/A if (manDig == null) {
4046N/A synchronized(manifestRawBytes) {
4046N/A if (manDig == null) {
4046N/A manDig = new ManifestDigester(manifestRawBytes);
4046N/A manifestRawBytes = null;
4046N/A }
4046N/A }
4046N/A }
4046N/A
4046N/A SignatureFileVerifier sfv =
4046N/A new SignatureFileVerifier(signerCache,
4046N/A manDig, uname, baos.toByteArray());
4046N/A
4046N/A if (sfv.needSignatureFileBytes()) {
4046N/A // see if we have already parsed an external .SF file
4046N/A byte[] bytes = (byte[]) sigFileData.get(key);
4046N/A
4046N/A if (bytes == null) {
4046N/A // put this block on queue for later processing
4046N/A // since we don't have the .SF bytes yet
4046N/A // (uname, block);
4046N/A if (debug != null) {
4046N/A debug.println("adding pending block");
4046N/A }
4046N/A pendingBlocks.add(sfv);
4046N/A return;
4046N/A } else {
4046N/A sfv.setSignatureFile(bytes);
4046N/A }
4046N/A }
4046N/A sfv.process(sigFileSigners, manifestDigests);
4046N/A
4046N/A } catch (IOException ioe) {
4046N/A // e.g. sun.security.pkcs.ParsingException
4046N/A if (debug != null) debug.println("processEntry caught: "+ioe);
4046N/A // ignore and treat as unsigned
4046N/A } catch (SignatureException se) {
4046N/A if (debug != null) debug.println("processEntry caught: "+se);
4046N/A // ignore and treat as unsigned
4046N/A } catch (NoSuchAlgorithmException nsae) {
4046N/A if (debug != null) debug.println("processEntry caught: "+nsae);
4046N/A // ignore and treat as unsigned
4046N/A } catch (CertificateException ce) {
4046N/A if (debug != null) debug.println("processEntry caught: "+ce);
4046N/A // ignore and treat as unsigned
4046N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Return an array of java.security.cert.Certificate objects for
0N/A * the given file in the jar.
3570N/A * @deprecated
0N/A */
0N/A public java.security.cert.Certificate[] getCerts(String name)
0N/A {
0N/A return mapSignersToCertArray(getCodeSigners(name));
0N/A }
0N/A
3570N/A public java.security.cert.Certificate[] getCerts(JarFile jar, JarEntry entry)
3570N/A {
3570N/A return mapSignersToCertArray(getCodeSigners(jar, entry));
3570N/A }
3570N/A
0N/A /**
0N/A * return an array of CodeSigner objects for
0N/A * the given file in the jar. this array is not cloned.
0N/A *
0N/A */
0N/A public CodeSigner[] getCodeSigners(String name)
0N/A {
0N/A return (CodeSigner[])verifiedSigners.get(name);
0N/A }
0N/A
3570N/A public CodeSigner[] getCodeSigners(JarFile jar, JarEntry entry)
3570N/A {
3570N/A String name = entry.getName();
3570N/A if (eagerValidation && sigFileSigners.get(name) != null) {
3570N/A /*
3570N/A * Force a read of the entry data to generate the
3570N/A * verification hash.
3570N/A */
4046N/A try {
4046N/A InputStream s = jar.getInputStream(entry);
3570N/A byte[] buffer = new byte[1024];
3570N/A int n = buffer.length;
3570N/A while (n != -1) {
3570N/A n = s.read(buffer, 0, buffer.length);
3570N/A }
4046N/A s.close();
3570N/A } catch (IOException e) {
3570N/A }
3570N/A }
3570N/A return getCodeSigners(name);
3570N/A }
3570N/A
0N/A /*
0N/A * Convert an array of signers into an array of concatenated certificate
0N/A * arrays.
0N/A */
0N/A private static java.security.cert.Certificate[] mapSignersToCertArray(
0N/A CodeSigner[] signers) {
0N/A
0N/A if (signers != null) {
0N/A ArrayList certChains = new ArrayList();
0N/A for (int i = 0; i < signers.length; i++) {
0N/A certChains.addAll(
0N/A signers[i].getSignerCertPath().getCertificates());
0N/A }
0N/A
0N/A // Convert into a Certificate[]
0N/A return (java.security.cert.Certificate[])
0N/A certChains.toArray(
0N/A new java.security.cert.Certificate[certChains.size()]);
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * returns true if there no files to verify.
0N/A * should only be called after all the META-INF entries
0N/A * have been processed.
0N/A */
0N/A boolean nothingToVerify()
0N/A {
0N/A return (anyToVerify == false);
0N/A }
0N/A
0N/A /**
0N/A * called to let us know we have processed all the
0N/A * META-INF entries, and if we re-read one of them, don't
0N/A * re-process it. Also gets rid of any data structures
0N/A * we needed when parsing META-INF entries.
0N/A */
0N/A void doneWithMeta()
0N/A {
4046N/A parsingMeta = false;
0N/A anyToVerify = !sigFileSigners.isEmpty();
4046N/A baos = null;
4046N/A sigFileData = null;
4046N/A pendingBlocks = null;
0N/A signerCache = null;
0N/A manDig = null;
3875N/A // MANIFEST.MF is always treated as signed and verified,
3875N/A // move its signers from sigFileSigners to verifiedSigners.
3875N/A if (sigFileSigners.containsKey(JarFile.MANIFEST_NAME)) {
3875N/A verifiedSigners.put(JarFile.MANIFEST_NAME,
3875N/A sigFileSigners.remove(JarFile.MANIFEST_NAME));
3875N/A }
0N/A }
0N/A
0N/A static class VerifierStream extends java.io.InputStream {
0N/A
0N/A private InputStream is;
0N/A private JarVerifier jv;
0N/A private ManifestEntryVerifier mev;
0N/A private long numLeft;
0N/A
0N/A VerifierStream(Manifest man,
0N/A JarEntry je,
0N/A InputStream is,
0N/A JarVerifier jv) throws IOException
0N/A {
0N/A this.is = is;
0N/A this.jv = jv;
0N/A this.mev = new ManifestEntryVerifier(man);
0N/A this.jv.beginEntry(je, mev);
0N/A this.numLeft = je.getSize();
0N/A if (this.numLeft == 0)
0N/A this.jv.update(-1, this.mev);
0N/A }
0N/A
0N/A public int read() throws IOException
0N/A {
0N/A if (numLeft > 0) {
0N/A int b = is.read();
0N/A jv.update(b, mev);
0N/A numLeft--;
0N/A if (numLeft == 0)
0N/A jv.update(-1, mev);
0N/A return b;
0N/A } else {
0N/A return -1;
0N/A }
0N/A }
0N/A
0N/A public int read(byte b[], int off, int len) throws IOException {
0N/A if ((numLeft > 0) && (numLeft < len)) {
0N/A len = (int)numLeft;
0N/A }
0N/A
0N/A if (numLeft > 0) {
0N/A int n = is.read(b, off, len);
0N/A jv.update(n, b, off, len, mev);
0N/A numLeft -= n;
0N/A if (numLeft == 0)
0N/A jv.update(-1, b, off, len, mev);
0N/A return n;
0N/A } else {
0N/A return -1;
0N/A }
0N/A }
0N/A
0N/A public void close()
0N/A throws IOException
0N/A {
0N/A if (is != null)
0N/A is.close();
0N/A is = null;
0N/A mev = null;
0N/A jv = null;
0N/A }
0N/A
0N/A public int available() throws IOException {
0N/A return is.available();
0N/A }
0N/A
0N/A }
3570N/A
3570N/A // Extended JavaUtilJarAccess CodeSource API Support
3570N/A
3570N/A private Map urlToCodeSourceMap = new HashMap();
3570N/A private Map signerToCodeSource = new HashMap();
3570N/A private URL lastURL;
3570N/A private Map lastURLMap;
3570N/A
3570N/A /*
3570N/A * Create a unique mapping from codeSigner cache entries to CodeSource.
3570N/A * In theory, multiple URLs origins could map to a single locally cached
3570N/A * and shared JAR file although in practice there will be a single URL in use.
3570N/A */
3570N/A private synchronized CodeSource mapSignersToCodeSource(URL url, CodeSigner[] signers) {
3570N/A Map map;
3570N/A if (url == lastURL) {
3570N/A map = lastURLMap;
3570N/A } else {
3570N/A map = (Map) urlToCodeSourceMap.get(url);
3570N/A if (map == null) {
3570N/A map = new HashMap();
3570N/A urlToCodeSourceMap.put(url, map);
3570N/A }
3570N/A lastURLMap = map;
3570N/A lastURL = url;
3570N/A }
3570N/A CodeSource cs = (CodeSource) map.get(signers);
3570N/A if (cs == null) {
3570N/A cs = new VerifierCodeSource(csdomain, url, signers);
3570N/A signerToCodeSource.put(signers, cs);
3570N/A }
3570N/A return cs;
3570N/A }
3570N/A
3570N/A private CodeSource[] mapSignersToCodeSources(URL url, List signers, boolean unsigned) {
3570N/A List sources = new ArrayList();
3570N/A
3570N/A for (int i = 0; i < signers.size(); i++) {
3570N/A sources.add(mapSignersToCodeSource(url, (CodeSigner[]) signers.get(i)));
3570N/A }
3570N/A if (unsigned) {
3570N/A sources.add(mapSignersToCodeSource(url, null));
3570N/A }
3570N/A return (CodeSource[]) sources.toArray(new CodeSource[sources.size()]);
3570N/A }
3570N/A private CodeSigner[] emptySigner = new CodeSigner[0];
3570N/A
3570N/A /*
3570N/A * Match CodeSource to a CodeSigner[] in the signer cache.
3570N/A */
3570N/A private CodeSigner[] findMatchingSigners(CodeSource cs) {
3570N/A if (cs instanceof VerifierCodeSource) {
3570N/A VerifierCodeSource vcs = (VerifierCodeSource) cs;
3570N/A if (vcs.isSameDomain(csdomain)) {
3570N/A return ((VerifierCodeSource) cs).getPrivateSigners();
3570N/A }
3570N/A }
3570N/A
3570N/A /*
3570N/A * In practice signers should always be optimized above
3570N/A * but this handles a CodeSource of any type, just in case.
3570N/A */
3570N/A CodeSource[] sources = mapSignersToCodeSources(cs.getLocation(), getJarCodeSigners(), true);
4046N/A List sourceList = new ArrayList();
4046N/A for (int i = 0; i < sources.length; i++) {
4046N/A sourceList.add(sources[i]);
4046N/A }
3570N/A int j = sourceList.indexOf(cs);
3570N/A if (j != -1) {
3570N/A CodeSigner[] match;
3570N/A match = ((VerifierCodeSource) sourceList.get(j)).getPrivateSigners();
3570N/A if (match == null) {
3570N/A match = emptySigner;
3570N/A }
3570N/A return match;
3570N/A }
3570N/A return null;
3570N/A }
3570N/A
3570N/A /*
3570N/A * Instances of this class hold uncopied references to internal
3570N/A * signing data that can be compared by object reference identity.
3570N/A */
3570N/A private static class VerifierCodeSource extends CodeSource {
3570N/A
3570N/A URL vlocation;
3570N/A CodeSigner[] vsigners;
3570N/A java.security.cert.Certificate[] vcerts;
3570N/A Object csdomain;
3570N/A
3570N/A VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) {
3570N/A super(location, signers);
3570N/A this.csdomain = csdomain;
3570N/A vlocation = location;
3570N/A vsigners = signers; // from signerCache
3570N/A }
3570N/A
3570N/A VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs) {
3570N/A super(location, certs);
3570N/A this.csdomain = csdomain;
3570N/A vlocation = location;
3570N/A vcerts = certs; // from signerCache
3570N/A }
3570N/A
3570N/A /*
3570N/A * All VerifierCodeSource instances are constructed based on
3570N/A * singleton signerCache or signerCacheCert entries for each unique signer.
3570N/A * No CodeSigner<->Certificate[] conversion is required.
3570N/A * We use these assumptions to optimize equality comparisons.
3570N/A */
3570N/A public boolean equals(Object obj) {
3570N/A if (obj == this) {
3570N/A return true;
3570N/A }
3570N/A if (obj instanceof VerifierCodeSource) {
3570N/A VerifierCodeSource that = (VerifierCodeSource) obj;
3570N/A
3570N/A /*
3570N/A * Only compare against other per-signer singletons constructed
3570N/A * on behalf of the same JarFile instance. Otherwise, compare
3570N/A * things the slower way.
3570N/A */
3570N/A if (isSameDomain(that.csdomain)) {
3570N/A if (that.vsigners != this.vsigners
3570N/A || that.vcerts != this.vcerts) {
3570N/A return false;
3570N/A }
3570N/A if (that.vlocation != null) {
3570N/A return that.vlocation.equals(this.vlocation);
3570N/A } else if (this.vlocation != null) {
3570N/A return this.vlocation.equals(that.vlocation);
3570N/A } else { // both null
3570N/A return true;
3570N/A }
3570N/A }
3570N/A }
3570N/A return super.equals(obj);
3570N/A }
3570N/A
3570N/A boolean isSameDomain(Object csdomain) {
3570N/A return this.csdomain == csdomain;
3570N/A }
3570N/A
3570N/A private CodeSigner[] getPrivateSigners() {
3570N/A return vsigners;
3570N/A }
3570N/A
3570N/A private java.security.cert.Certificate[] getPrivateCertificates() {
3570N/A return vcerts;
3570N/A }
3570N/A }
3570N/A private Map signerMap;
3570N/A
3570N/A private synchronized Map signerMap() {
3570N/A if (signerMap == null) {
3570N/A /*
3570N/A * Snapshot signer state so it doesn't change on us. We care
3570N/A * only about the asserted signatures. Verification of
3570N/A * signature validity happens via the JarEntry apis.
3570N/A */
3570N/A signerMap = new HashMap(verifiedSigners.size() + sigFileSigners.size());
3570N/A signerMap.putAll(verifiedSigners);
3570N/A signerMap.putAll(sigFileSigners);
3570N/A }
3570N/A return signerMap;
3570N/A }
3570N/A
3570N/A public synchronized Enumeration<String> entryNames(JarFile jar, final CodeSource[] cs) {
3570N/A final Map map = signerMap();
3570N/A final Iterator itor = map.entrySet().iterator();
3570N/A boolean matchUnsigned = false;
3570N/A
3570N/A /*
3570N/A * Grab a single copy of the CodeSigner arrays. Check
3570N/A * to see if we can optimize CodeSigner equality test.
3570N/A */
3570N/A List req = new ArrayList(cs.length);
3570N/A for (int i = 0; i < cs.length; i++) {
3570N/A CodeSigner[] match = findMatchingSigners(cs[i]);
3570N/A if (match != null) {
3570N/A if (match.length > 0) {
3570N/A req.add(match);
3570N/A } else {
3570N/A matchUnsigned = true;
3570N/A }
3570N/A }
3570N/A }
3570N/A
3570N/A final List signersReq = req;
3570N/A final Enumeration enum2 = (matchUnsigned) ? unsignedEntryNames(jar) : emptyEnumeration;
3570N/A
3570N/A return new Enumeration<String>() {
3570N/A
3570N/A String name;
3570N/A
3570N/A public boolean hasMoreElements() {
3570N/A if (name != null) {
3570N/A return true;
3570N/A }
3570N/A
3570N/A while (itor.hasNext()) {
3570N/A Map.Entry e = (Map.Entry) itor.next();
3570N/A if (signersReq.contains((CodeSigner[]) e.getValue())) {
3570N/A name = (String) e.getKey();
3570N/A return true;
3570N/A }
3570N/A }
3570N/A while (enum2.hasMoreElements()) {
3570N/A name = (String) enum2.nextElement();
3570N/A return true;
3570N/A }
3570N/A return false;
3570N/A }
3570N/A
3570N/A public String nextElement() {
3570N/A if (hasMoreElements()) {
3570N/A String value = name;
3570N/A name = null;
3570N/A return value;
3570N/A }
3570N/A throw new NoSuchElementException();
3570N/A }
3570N/A };
3570N/A }
3570N/A
3570N/A /*
3570N/A * Like entries() but screens out internal JAR mechanism entries
3570N/A * and includes signed entries with no ZIP data.
3570N/A */
3570N/A public Enumeration<JarEntry> entries2(final JarFile jar, Enumeration e) {
3570N/A final Map map = new HashMap();
3570N/A map.putAll(signerMap());
3570N/A final Enumeration enum_ = e;
3570N/A return new Enumeration<JarEntry>() {
3570N/A
3570N/A Enumeration signers = null;
3570N/A JarEntry entry;
3570N/A
3570N/A public boolean hasMoreElements() {
3570N/A if (entry != null) {
3570N/A return true;
3570N/A }
3570N/A while (enum_.hasMoreElements()) {
3570N/A ZipEntry ze = (ZipEntry) enum_.nextElement();
3570N/A if (JarVerifier.isSigningRelated(ze.getName())) {
3570N/A continue;
3570N/A }
3570N/A entry = jar.newEntry(ze);
3570N/A return true;
3570N/A }
3570N/A if (signers == null) {
3570N/A signers = Collections.enumeration(map.keySet());
3570N/A }
3570N/A while (signers.hasMoreElements()) {
3570N/A String name = (String) signers.nextElement();
3570N/A entry = jar.newEntry(new ZipEntry(name));
3570N/A return true;
3570N/A }
3570N/A
3570N/A // Any map entries left?
3570N/A return false;
3570N/A }
3570N/A
3570N/A public JarEntry nextElement() {
3570N/A if (hasMoreElements()) {
3570N/A JarEntry je = entry;
3570N/A map.remove(je.getName());
3570N/A entry = null;
3570N/A return je;
3570N/A }
3570N/A throw new NoSuchElementException();
3570N/A }
3570N/A };
3570N/A }
3570N/A private Enumeration emptyEnumeration = new Enumeration<String>() {
3570N/A
3570N/A public boolean hasMoreElements() {
3570N/A return false;
3570N/A }
3570N/A
3570N/A public String nextElement() {
3570N/A throw new NoSuchElementException();
3570N/A }
3570N/A };
3570N/A
3570N/A // true if file is part of the signature mechanism itself
3570N/A static boolean isSigningRelated(String name) {
3570N/A name = name.toUpperCase(Locale.ENGLISH);
3570N/A if (!name.startsWith("META-INF/")) {
3570N/A return false;
3570N/A }
3570N/A name = name.substring(9);
3570N/A if (name.indexOf('/') != -1) {
3570N/A return false;
3570N/A }
3570N/A if (name.endsWith(".DSA")
3570N/A || name.endsWith(".RSA")
3570N/A || name.endsWith(".SF")
3570N/A || name.endsWith(".EC")
3570N/A || name.startsWith("SIG-")
3570N/A || name.equals("MANIFEST.MF")) {
3570N/A return true;
3570N/A }
3570N/A return false;
3570N/A }
3570N/A
3570N/A private Enumeration<String> unsignedEntryNames(JarFile jar) {
3570N/A final Map map = signerMap();
3570N/A final Enumeration entries = jar.entries();
3570N/A return new Enumeration<String>() {
3570N/A
3570N/A String name;
3570N/A
3570N/A /*
3570N/A * Grab entries from ZIP directory but screen out
3570N/A * metadata.
3570N/A */
3570N/A public boolean hasMoreElements() {
3570N/A if (name != null) {
3570N/A return true;
3570N/A }
3570N/A while (entries.hasMoreElements()) {
3570N/A String value;
3570N/A ZipEntry e = (ZipEntry) entries.nextElement();
3570N/A value = e.getName();
3570N/A if (e.isDirectory() || isSigningRelated(value)) {
3570N/A continue;
3570N/A }
3570N/A if (map.get(value) == null) {
3570N/A name = value;
3570N/A return true;
3570N/A }
3570N/A }
3570N/A return false;
3570N/A }
3570N/A
3570N/A public String nextElement() {
3570N/A if (hasMoreElements()) {
3570N/A String value = name;
3570N/A name = null;
3570N/A return value;
3570N/A }
3570N/A throw new NoSuchElementException();
3570N/A }
3570N/A };
3570N/A }
3570N/A private List jarCodeSigners;
3570N/A
3570N/A private synchronized List getJarCodeSigners() {
3570N/A CodeSigner[] signers;
3570N/A if (jarCodeSigners == null) {
3570N/A HashSet set = new HashSet();
3570N/A set.addAll(signerMap().values());
3570N/A jarCodeSigners = new ArrayList();
3570N/A jarCodeSigners.addAll(set);
3570N/A }
3570N/A return jarCodeSigners;
3570N/A }
3570N/A
3570N/A public synchronized CodeSource[] getCodeSources(JarFile jar, URL url) {
3570N/A boolean hasUnsigned = unsignedEntryNames(jar).hasMoreElements();
3570N/A
3570N/A return mapSignersToCodeSources(url, getJarCodeSigners(), hasUnsigned);
3570N/A }
3570N/A
3570N/A public CodeSource getCodeSource(URL url, String name) {
3570N/A CodeSigner[] signers;
3570N/A
3570N/A signers = (CodeSigner[]) signerMap().get(name);
3570N/A return mapSignersToCodeSource(url, signers);
3570N/A }
3570N/A
3570N/A public CodeSource getCodeSource(URL url, JarFile jar, JarEntry je) {
3570N/A CodeSigner[] signers;
3570N/A
3570N/A return mapSignersToCodeSource(url, getCodeSigners(jar, je));
3570N/A }
3570N/A
3570N/A public void setEagerValidation(boolean eager) {
3570N/A eagerValidation = eager;
3570N/A }
3570N/A
3570N/A public synchronized List getManifestDigests() {
3570N/A return Collections.unmodifiableList(manifestDigests);
3570N/A }
3570N/A
3570N/A static CodeSource getUnsignedCS(URL url) {
3570N/A return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null);
3570N/A }
0N/A}