/* * @(#)JarFile.java 1.13 06/10/11 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package java.util.jar; import java.io.*; import java.util.*; import java.util.zip.*; import java.security.cert.Certificate; import sun.security.util.ManifestEntryVerifier; /** * The <code>JarFile</code> class is used to read the contents of a JAR file * from any file that can be opened with <code>java.io.RandomAccessFile</code>. * NOTE: <B>java.io.RandomAccessFile</B> is found in J2ME CDC profiles * such as J2ME Foundation Profile. * It extends the class <code>java.util.zip.ZipFile</code> with support * for reading an optional <code>Manifest</code> entry. The * <code>Manifest</code> can be used to specify meta-information about the * JAR file and its entries. * * @author David Connelly * @version 1.38, 02/02/00 * @see Manifest * @see java.util.zip.ZipFile * @see java.util.jar.JarEntry * @since 1.2 */ public class JarFile extends ZipFile { private Manifest man; private JarEntry manEntry; private boolean manLoaded; private JarVerifier jv; private boolean jvInitialized; private boolean verify; /** * The JAR manifest file name. */ public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; /** * Creates a new <code>JarFile</code> to read from the specified * file <code>name</code>. The <code>JarFile</code> will be verified if * it is signed. * <p> * ***NOTE: In J2ME CDC, there is no support for certificates by design. * If CDC by itself or a J2ME profile built on CDC does not add back the * java.security.Signature class (plus all its dependencies), * the <code>JarFile</code> does not need to be verified if signed. * <p> * @param name the name of the JAR file to be opened for reading * @exception IOException if an I/O error has occurred * @exception SecurityException if access to the file is denied * by the SecurityManager */ public JarFile(String name) throws IOException { this(new File(name), true, ZipFile.OPEN_READ); } /** * Creates a new <code>JarFile</code> to read from the specified * file <code>name</code>. * <p> * ***NOTE: In J2ME CDC, there is no support for certificates by design. * If CDC by itself or a J2ME profile built on CDC does not add back the * java.security.Signature class (plus all its dependencies), * the <code>JarFile</code> does not need to be verified if signed. * <p> * @param name the name of the JAR file to be opened for reading * @param verify whether or not to verify the JarFile if * it is signed. * @exception IOException if an I/O error has occurred * @exception SecurityException if access to the file is denied * by the SecurityManager */ public JarFile(String name, boolean verify) throws IOException { this(new File(name), verify, ZipFile.OPEN_READ); } /** * Creates a new <code>JarFile</code> to read from the specified * <code>File</code> object. The <code>JarFile</code> will be verified if * it is signed. * <p> * ***NOTE: In J2ME CDC, there is no support for certificates by design. * If CDC by itself or a J2ME profile built on CDC does not add back the * java.security.Signature class (plus all its dependencies), * the <code>JarFile</code> does not need to be verified if signed. * <p> * @param file the JAR file to be opened for reading * @exception IOException if an I/O error has occurred * @exception SecurityException if access to the file is denied * by the SecurityManager */ public JarFile(File file) throws IOException { this(file, true, ZipFile.OPEN_READ); } /** * Creates a new <code>JarFile</code> to read from the specified * <code>File</code> object. * <p> * ***NOTE: In J2ME CDC, there is no support for certificates by design. * If CDC by itself or a J2ME profile built on CDC does not add back the * java.security.Signature class (plus all its dependencies), * the <code>JarFile</code> does not need to be verified if signed. * <p> * @param file the JAR file to be opened for reading * @param verify whether or not to verify the JarFile if * it is signed. * @exception IOException if an I/O error has occurred * @exception SecurityException if access to the file is denied * by the SecurityManager. */ public JarFile(File file, boolean verify) throws IOException { this(file, verify, ZipFile.OPEN_READ); } /** * Creates a new <code>JarFile</code> to read from the specified * <code>File</code> object in the specified mode. The mode argument * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>. * * <p> * ***NOTE: In J2ME CDC, there is no support for certificates by design. * If CDC by itself or a J2ME profile built on CDC does not add back the * java.security.Signature class (plus all its dependencies), * the <code>JarFile</code> does not need to be verified if signed. * <p> * @param file the JAR file to be opened for reading * @param verify whether or not to verify the JarFile if * it is signed. * @param mode the mode in which the file is to be opened * @exception IOException if an I/O error has occurred * @exception IllegalArgumentException * If the <tt>mode</tt> argument is invalid * @exception SecurityException if access to the file is denied * by the SecurityManager */ public JarFile(File file, boolean verify, int mode) throws IOException { super(file, mode); this.verify = verify; } /** * Returns the JAR file manifest, or <code>null</code> if none. * * @return the JAR file manifest, or <code>null</code> if none */ public Manifest getManifest() throws IOException { if (!manLoaded) { // First look up manifest entry using standard name manEntry = getJarEntry(MANIFEST_NAME); if (manEntry == null) { // If not found, then iterate through all the "META-INF/" // entries to find a match. String[] names = getMetaInfEntryNames(); if (names != null) { for (int i = 0; i < names.length; i++) { if (MANIFEST_NAME.equals(names[i].toUpperCase())) { manEntry = getJarEntry(names[i]); break; } } } } // If found then load the manifest if (manEntry != null) { if (verify) { byte[] b = getBytes(manEntry); man = new Manifest(new ByteArrayInputStream(b)); jv = new JarVerifier(man, b); } else { man = new Manifest(super.getInputStream(manEntry)); } } manLoaded = true; } return man; } private native String[] getMetaInfEntryNames(); /** * Returns the <code>JarEntry</code> for the given entry name or * <code>null</code> if not found. * * @param name the JAR file entry name * @return the <code>JarEntry</code> for the given entry name or * <code>null</code> if not found. * @see java.util.jar.JarEntry */ public JarEntry getJarEntry(String name) { return (JarEntry)getEntry(name); } /** * Returns the <code>ZipEntry</code> for the given entry name or * <code>null</code> if not found. * * @param name the JAR file entry name * @return the <code>ZipEntry</code> for the given entry name or * <code>null</code> if not found * @see java.util.zip.ZipEntry */ public ZipEntry getEntry(String name) { ZipEntry ze = super.getEntry(name); if (ze != null) { return new JarFileEntry(ze); } return null; } /** * Returns an enumeration of the ZIP file entries. */ public Enumeration entries() { final Enumeration enum_ = super.entries(); return new Enumeration() { public boolean hasMoreElements() { return enum_.hasMoreElements(); } public Object nextElement() { ZipEntry ze = (ZipEntry)enum_.nextElement(); return new JarFileEntry(ze); } }; } private class JarFileEntry extends JarEntry { JarFileEntry(ZipEntry ze) { super(ze); } public Attributes getAttributes() throws IOException { Manifest man = JarFile.this.getManifest(); if (man != null) { return man.getAttributes(getName()); } else { return null; } } public java.security.cert.Certificate[] getCertificates() { if (certs == null && jv != null) { Certificate[] cs = jv.getCerts(getName()); if (cs != null) { certs = (Certificate[])cs.clone(); } } return certs; } } /* * Initializes the verifier object by reading all the manifest * entries and passing them to the verifier. */ private void initializeVerifier() { ManifestEntryVerifier mev = null; // Verify "META-INF/" entries... try { String[] names = getMetaInfEntryNames(); if (names != null) { for (int i = 0; i < names.length; i++) { JarEntry e = getJarEntry(names[i]); if (!e.isDirectory()) { if (mev == null) { mev = new ManifestEntryVerifier(man); } 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); } } } } } catch (IOException ex) { // if we had an error parsing any blocks, just // treat the jar file as being unsigned jv = null; } // if after initializing the verifier we have nothing // signed, we null it out. if (jv != null) { jv.doneWithMeta(); if (JarVerifier.debug != null) { JarVerifier.debug.println("done with meta!"); } if (jv.nothingToVerify()) { if (JarVerifier.debug != null) { JarVerifier.debug.println("nothing to verify!"); } jv = null; } } } /* * Reads all the bytes for a given entry. Used to process the * the META-INF files. */ private byte[] getBytes(ZipEntry ze) throws IOException { byte[] b = new byte[(int)ze.getSize()]; DataInputStream is = new DataInputStream(super.getInputStream(ze)); is.readFully(b, 0, b.length); is.close(); return b; } /** * Returns an input stream for reading the contents of the specified * ZIP file entry. * @param ze the zip file entry * @return an input stream for reading the contents of the specified * ZIP file entry * @exception ZipException if a ZIP format error has occurred * @exception IOException if an I/O error has occurred * <p> * NOTE: No SecurityException is thrown from this method since there * is no code signing in J2ME CDC, therefore no security exception * can occur. This exception may be added back in profiles supersetting * the J2ME CDC. */ public synchronized InputStream getInputStream(ZipEntry ze) throws IOException { if (!manLoaded) { getManifest(); } if (jv == null) { return super.getInputStream(ze); } if (!jvInitialized) { initializeVerifier(); jvInitialized = true; // could be set to null after a call to // initializeVerifier if we have nothing to // verify if (jv == null) return super.getInputStream(ze); } // wrap a verifier stream around the real stream return new JarVerifier.VerifierStream(man, (JarEntry)ze, super.getInputStream(ze), jv); } }