« Restricting Methods of Access | Main | Installation of Fedora Core 5 »

Java PluginClassLoader

I recently researched a means for verifying the integrity and authenticity of Java archive programmatically within a Java Applet. In my case, I had an Applet that was digitally-signed and trusted by the client. The Applet downloaded and executed Java bytecode packaged in JAR files. Before loading the downloaded code, I wanted the Java Applet to verify the digital signatures of the archives using the Java Plugin's certificate-management features.

I learned that there is a ClassLoader implementation called PluginClassLoader, and is provided in Java Runtime Environments version 1.3 or better. The PluginClassLoader is an extension of the URLClassLoader and can be used only in the context of the Java Plugin. Once a JAR file has been registered with a PluginClassLoader instance, the classloader will verify that the JAR's classes loaded by it are signed by certificates trusted by the user and/or in the browser keystore.

Here is some code that, when run as an Applet, will verify the integrity of an arbitrary JAR file in its path:

String jarFileName = ...
if (file.exists() && 
	file.getName().endsWith(".jar") && 
	classLoader instanceof PluginClassLoader) 
{
	try {
		// add the JAR to the ClassLoader's context
		((PluginClassLoader)classLoader).addLocalJar(file.toURL());
		
		// attempt to load a Java class from the JAR
		JarFile jarFile = new JarFile(file, false);
		Enumeration fileEntriesEnum = jarFile.entries();
		while (fileEntriesEnum.hasMoreElements()) 
		{
			JarEntry entry = (JarEntry) fileEntriesEnum.nextElement();
			if (entry.getName().endsWith(".class"))
			{
				String className = entry.getName().replace('/', '.').replace('\\', '.').replaceFirst(".class", "");
				System.out.println("Verifying with class: " + className);
				
				// Use the PluginClassLoader to load an arbitrary class from 
                                // the archive, triggering verification
				Class.forName(className, false, classLoader);
				verified = true;
				break;
			}
		}
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}