FileDocCategorySizeDatePackage
ZipLoader.javaAPI DocExample8321Sun Oct 25 18:13:36 GMT 1998None

ZipLoader.java

/*
 *
 * Copyright (c) 1998 Scott Oaks. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and
 * without fee is hereby granted.
 *
 * This sample source code is provided for example only,
 * on an unsupported, as-is basis. 
 *
 * AUTHOR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. AUTHOR SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
 * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
 * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
 * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
 * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
 * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
 * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  AUTHOR
 * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
 * HIGH RISK ACTIVITIES.
 */


import java.security.*;
import java.security.cert.*;
import java.net.*;
import java.io.*;
import java.util.zip.*;
import java.util.Enumeration;
import java.util.Hashtable;

public class ZipLoader extends SecureClassLoader {
	private URL urlBase;	
	public boolean printLoadMessages = true;
	Hashtable classArrays;
	CodeSource secureCS, defaultCS;
	java.security.cert.Certificate certificate;
	byte signature[];
	String classnames[];
	// In 1.2 beta 4, the Identity class is deprecated, and we no longer need
	// it to call defineClass anymore
	// XYZIdentity ids[];

	private ThreadGroup threadGroup;
	private int groupNum = 0;

	// class XYZIdentity extends Identity {
	//	XYZIdentity(String name, java.security.cert.Certificate c) throws KeyManagementException {
	//		super(name);
	//		addCertificate(c);
	//	}
	// }

	public ZipLoader(String base, ClassLoader parent) {
		super(parent);
		try {
			if (!(base.endsWith("/")))
				base = base + "/";
			urlBase = new URL(base);
			classArrays = new Hashtable();
			defaultCS = new CodeSource(urlBase, null);
		} catch (Exception e) {
			throw new IllegalArgumentException(base);
		}
	}

	private byte[] getClassBytes(InputStream is) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		BufferedInputStream bis = new BufferedInputStream(is);
		boolean eof = false;
		while (!eof) {
			try {
				int i = bis.read();
				if (i == -1)
					eof = true;
				else baos.write(i);
			} catch (IOException e) {
				return null;
			}
		}
		return baos.toByteArray();
	}

	// In 1.2 beta 4, the findLocalClass has been renamed to findClass
	protected Class findClass(String name) {
		String urlName = name.replace('.', '/');
		byte buf[];
		Class cl;

		buf = (byte[]) classArrays.get(urlName);
		if (buf != null) {
			// In 1.2 beta 4, the signature of the defineClass method has
			// changed
			if (isSecure(urlName))
				cl = defineClass(name, buf, 0, buf.length, secureCS);
			else cl = defineClass(name, buf, 0, buf.length, defaultCS);
			return cl;
		}

		try {
			URL url = new URL(urlBase, urlName + ".class");
			if (printLoadMessages)
				System.out.println("Loading " + url);
			InputStream is = url.openConnection().getInputStream();
			buf = getClassBytes(is);
			cl = defineClass(name, buf, 0, buf.length, defaultCS);
			return cl;
		} catch (Exception e) {
			System.out.println("Can't load " + name + ": " + e);
			return null;
		}
	}

	public void readZipFile(String name) {
		URL zipUrl = null;
		ZipInputStream zis;
		ZipEntry ze;

		try {
			zipUrl = new URL(urlBase, name);
		} catch (MalformedURLException mue) {
			System.out.println("Unknown zip file " + name);
			return;
		}
		if (printLoadMessages)
			System.out.println("Loading zip file " + zipUrl);

		try {
			zis = new ZipInputStream(zipUrl.openConnection().getInputStream());
		} catch (IOException ioe) {
			System.out.println("Can't open zip file " + zipUrl);
			return;
		}

		try {
			while ((ze = zis.getNextEntry()) != null) {
				String zipName = ze.getName();
				if (zipName.endsWith(".class"))
					loadClassBytes(zis, zipName);
				else if (zipName.equals("SIGNATURE"))
					processSignature(zis);
				else if (zipName.equals("CERTIFICATE"))
					processCertificate(zis);
				else if (zipName.equals("CLASSNAMES"))
					processManifest(zis);
				// else ignore it; it could be an image or audio file
				zis.closeEntry();
			}
			checkSignature();
		} catch (IOException ioe) {
			System.out.println("Badly formatted zip file");
		}
	}

	private void checkSignature() {
		if (classnames == null || signature == null || certificate == null) {
			secureCS = defaultCS;
		}
		else {
			try {
				// In 1.2 beta 4, we must pass an argument to the getInstance
				// method
				KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
				ks.load(new FileInputStream(System.getProperty("user.home") + File.separator + ".keystore"), null);
				String alias = ks.getCertificateAlias(certificate);
				if (alias == null) {
					if (printLoadMessages)
						System.out.println("Didn't find signer in database");
					secureCS = defaultCS;
					return;
				}
				Signature sig = Signature.getInstance("DSA");
				sig.initVerify(certificate.getPublicKey());
				for (int i = 0; i < classnames.length; i++) {
					sig.update((byte [])
						classArrays.get(classnames[i].substring(0,
												classnames[i].length() - 6)));
				}
				if (!sig.verify(signature)) {
					System.out.println("Signature is invalid");
					secureCS = defaultCS;
					return;
				}
				// In 1.2 beta 4, we use certificates instead of identities
				// ids = new XYZIdentity[1];
				// ids[0] = new XYZIdentity(alias, certificate);
				// secureCS = getCodeSource(urlBase, ids);
				secureCS = new CodeSource(urlBase, ks.getCertificateChain(alias));
			} catch (Exception e) {
				System.out.println("Treating as unsigned");
				e.printStackTrace();
				secureCS = defaultCS;
				return;
			}
		}
	}

	private void processSignature(ZipInputStream zis) {
		try {
			ObjectInputStream ois = new ObjectInputStream(zis);
			signature = (byte []) ois.readObject();
		} catch (Exception e) {
			System.out.println("Can't process signature");
		}
	}

	private void processCertificate(ZipInputStream zis) {
		try {
			ObjectInputStream ois = new ObjectInputStream(zis);
			// In 1.2 beta 4, we must use a CertificateFactory instead of
			// the X509 class directly
			// certificate = X509Certificate.getInstance(((byte []) ois.readObject()));
			CertificateFactory cf = CertificateFactory.getInstance("X509");
			byte buf[] = (byte[]) ois.readObject();
			ByteArrayInputStream bais = new ByteArrayInputStream(buf);
			certificate = (X509Certificate) cf.generateCertificate(bais);
		} catch (Exception e) {
			System.out.println("Can't process certificate");
		}
	}

	private boolean isSecure(String name) {
		String tmpName = name + ".class";
		if (classnames == null)
			return false;
		for (int i = 0; i < classnames.length; i++) {
			if (tmpName.equals(classnames[i]))
				return true;
		}
		return false;
	}

	private void processManifest(ZipInputStream zis) {
		try {
			ObjectInputStream ois = new ObjectInputStream(zis);
			classnames = (String []) ois.readObject();
		} catch (Exception e) {
			System.out.println("Can't process manifest");
		}
	}

	private void loadClassBytes(ZipInputStream zis, String zipName) {
		if (printLoadMessages)
			System.out.println("\t" + zipName);
		BufferedInputStream zipBuf = new BufferedInputStream(zis);
		ByteArrayOutputStream zipOut = new ByteArrayOutputStream();
		int b;
		try {
			while ((b = zipBuf.read()) != -1)
				zipOut.write(b);
			classArrays.put(zipName.substring(0, zipName.length() - 6),
							zipOut.toByteArray());
		} catch (IOException ioe) {
			System.out.println("Error reading entry " + zipName);
		}
	}

	ThreadGroup getThreadGroup() {
		if (threadGroup == null)
			threadGroup = new ThreadGroup("JavaRuner ThreadGroup-" +
 groupNum++);
		return threadGroup;
	}

	String getHost() {
		return urlBase.getHost();
	}
}