/*
*
* 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();
}
}
|