package com.dgex.offspring.update;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
/**
* Verifies a signed jar file given an array of truted CA certs
*
* @author Andrew Harrison
* @version $Revision: 148 $
* @created Apr 11, 2007: 11:02:26 PM
* @date $Date: 2007-04-12 13:31:48 +0100 (Thu, 12 Apr 2007) $ modified by
* $Author: scmabh $
* @todo Put your notes here...
*/
public class JarVerifier {
public static void verify(JarFile jf, X509Certificate[] trustedCaCerts)
throws IOException, CertificateException {
Vector<JarEntry> entriesVec = new Vector<JarEntry>();
// Ensure there is a manifest file
Manifest man = jf.getManifest();
if (man == null)
throw new SecurityException("The JAR is not signed");
// Ensure all the entries' signatures verify correctly
byte[] buffer = new byte[8192];
Enumeration entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry je = (JarEntry) entries.nextElement();
entriesVec.addElement(je);
InputStream is = jf.getInputStream(je);
int n;
while ((n = is.read(buffer, 0, buffer.length)) != -1) {
// we just read. this will throw a SecurityException
// if a signature/digest check fails.
}
is.close();
}
jf.close();
// Get the list of signer certificates
Enumeration e = entriesVec.elements();
while (e.hasMoreElements()) {
JarEntry je = (JarEntry) e.nextElement();
if (je.isDirectory())
continue;
// Every file must be signed - except
// files in META-INF
Certificate[] certs = je.getCertificates();
if ((certs == null) || (certs.length == 0)) {
if (!je.getName().startsWith("META-INF"))
throw new SecurityException("The JCE framework " + "has unsigned "
+ "class files.");
}
else {
// Check whether the file
// is signed as expected.
// The framework may be signed by
// multiple signers. At least one of
// the signers must be a trusted signer.
// First, determine the roots of the certificate chains
X509Certificate[] chainRoots = getChainRoots(certs);
boolean signedAsExpected = false;
for (int i = 0; i < chainRoots.length; i++) {
if (isTrusted(chainRoots[i], trustedCaCerts)) {
signedAsExpected = true;
break;
}
}
if (!signedAsExpected) { throw new SecurityException(
"The JAR is not signed by a trusted signer"); }
}
}
}
public static boolean isTrusted(X509Certificate cert,
X509Certificate[] trustedCaCerts) {
// Return true iff either of the following is true:
// 1) the cert is in the trustedCaCerts.
// 2) the cert is issued by a trusted CA.
// Check whether the cert is in the trustedCaCerts
for (int i = 0; i < trustedCaCerts.length; i++) {
// If the cert has the same SubjectDN
// as a trusted CA, check whether
// the two certs are the same.
if (cert.getSubjectDN().equals(trustedCaCerts[i].getSubjectDN())) {
if (cert.equals(trustedCaCerts[i])) { return true; }
}
}
// Check whether the cert is issued by a trusted CA.
// Signature verification is expensive. So we check
// whether the cert is issued
// by one of the trusted CAs if the above loop failed.
for (int i = 0; i < trustedCaCerts.length; i++) {
// If the issuer of the cert has the same name as
// a trusted CA, check whether that trusted CA
// actually issued the cert.
if (cert.getIssuerDN().equals(trustedCaCerts[i].getSubjectDN())) {
try {
cert.verify(trustedCaCerts[i].getPublicKey());
return true;
}
catch (Exception e) {
// Do nothing.
}
}
}
return false;
}
public static X509Certificate[] getChainRoots(Certificate[] certs) {
Vector<X509Certificate> result = new Vector<X509Certificate>(3);
// choose a Vector size that seems reasonable
for (int i = 0; i < certs.length - 1; i++) {
if (!((X509Certificate) certs[i + 1]).getSubjectDN().equals(
((X509Certificate) certs[i]).getIssuerDN())) {
// We've reached the end of a chain
result.addElement((X509Certificate) certs[i]);
}
}
// The final entry in the certs array is always
// a "root" certificate
result.addElement((X509Certificate) certs[certs.length - 1]);
X509Certificate[] ret = new X509Certificate[result.size()];
result.copyInto(ret);
return ret;
}
}