blob: 4ce7a24e382466782ea173b91887adc426915963 [file] [log] [blame]
package org.eclipse.update.internal.security;
/*
* (c) Copyright IBM Corp. 2000, 2002.
* All Rights Reserved.
*/
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.*;
import java.util.jar.*;
import org.eclipse.core.runtime.*;
import org.eclipse.update.internal.core.Policy;
import org.eclipse.update.internal.core.UpdateManagerPlugin;
/**
* The JarVerifier will check the integrity of the JAR.
* If the Jar is signed and the integrity is validated,
* it will check if one of the certificate of each file
* is in one of the keystore.
*
*/
public class JarVerifier {
private JarVerificationResult result;
/**
* List of initialized keystores
*/
List /* of KeyStore */
listOfKeystores;
/**
* Number of files in the JarFile
*/
private int entries;
/**
* ProgressMonitor during integrity validation
*/
private IProgressMonitor monitor;
/**
* JAR File Name: used in the readJarFile.
*/
private String jarFileName;
//RESULT VALUES
/**
* Default Constructor
*/
public JarVerifier() {
}
/**
*
*/
public JarVerifier(IProgressMonitor monitor) {
this.monitor = monitor;
}
/**
* Returns the list of the keystores.
*
*/
private List getKeyStores() throws CoreException {
if (listOfKeystores == null) {
listOfKeystores = new ArrayList(0);
KeyStores listOfKeystoreHandles = new KeyStores();
InputStream in = null;
KeyStore keystore = null;
KeystoreHandle handle = null;
while (listOfKeystoreHandles.hasNext()) {
try {
handle = listOfKeystoreHandles.next();
in = handle.getLocation().openStream();
try {
keystore = KeyStore.getInstance(handle.getType());
keystore.load(in, null); // no password
} catch (NoSuchAlgorithmException e) {
throw newCoreException(Policy.bind("JarVerifier.UnableToFindEncryption",handle.getLocation().toExternalForm()), e); //$NON-NLS-1$
} catch (CertificateException e) {
throw newCoreException(Policy.bind("JarVerifier.UnableToLoadCertificate",handle.getLocation().toExternalForm()), e); //$NON-NLS-1$
} catch (KeyStoreException e) {
throw newCoreException(Policy.bind("JarVerifier.UnableToFindProviderForKeystore",handle.getType()), e); //$NON-NLS-1$
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
} // nothing
}
} // try loading a keyStore
// keystore was loaded
listOfKeystores.add(keystore);
} catch (IOException e) {
// nothing... if the keystore doesn't exist, continue
}
} // while all key stores
}
return listOfKeystores;
}
/**
* initialize instance variables
*/
private void initializeVariables(File jarFile) throws IOException {
result = new JarVerificationResult();
result.setVerificationCode(JarVerification.UNKNOWN_ERROR);
result.setResultCode(JarVerification.ASK_USER);
result.setResultException(null);
JarFile jar = new JarFile(jarFile);
entries = jar.size();
try {
jar.close();
} catch (IOException ex) {
// unchecked
}
jarFileName = jarFile.getName();
}
/**
* Returns true if one of the certificate exists in the keystore
*/
private boolean existsInKeystore(Certificate cert) throws CoreException {
try {
List keyStores = getKeyStores();
if (!keyStores.isEmpty()) {
Iterator listOfKeystores = keyStores.iterator();
while (listOfKeystores.hasNext()) {
KeyStore keystore = (KeyStore) listOfKeystores.next();
if (keystore.getCertificateAlias(cert) != null) {
return true;
}
}
}
} catch (KeyStoreException e) {
throw newCoreException(Policy.bind("JarVerifier.KeyStoreNotLoaded"), e); //$NON-NLS-1$
}
return false;
}
/**
* Throws exception or set the resultcode to UNKNOWN_ERROR
*/
private List readJarFile(final JarInputStream jis)
throws IOException, InterruptedException, InvocationTargetException {
final List list = new ArrayList(0);
byte[] buffer = new byte[4096];
JarEntry ent;
if (monitor != null)
monitor.beginTask(Policy.bind("JarVerifier.Verify", jarFileName), entries);//$NON-NLS-1$ //$NON-NLS-2$
try {
while ((ent = jis.getNextJarEntry()) != null) {
list.add(ent);
if (monitor != null)
monitor.worked(1);
while ((jis.read(buffer, 0, buffer.length)) != -1) {
// Security error thrown if tempered
}
}
} catch (IOException e) {
result.setVerificationCode(JarVerification.UNKNOWN_ERROR);
result.setResultException(e);
} finally {
if (monitor != null)
monitor.done();
}
return list;
}
/**
*
* @param newMonitor org.eclipse.core.runtime.IProgressMonitor
*/
public void setMonitor(IProgressMonitor newMonitor) {
monitor = newMonitor;
}
/**
* Verifies integrity and the validity of a valid
* URL representing a JAR file
* the possible results are:
*
* result == NOT_SIGNED if the jar file is not signed.
* result == INTEGRITY_VERIFIED if the Jar file has not been
* modified since it has been
* signed
* result == CORRUPTED if the Jar file has been changed
* since it has been signed.
* result == SOURCE_VERIFIED if all the files in the Jar
* have a certificate that is
* present in the keystore
* result == UNKNOWN_ERROR an occured during process, do
* not install.
* result == VERIFICATION.CANCELLED if process was cancelled, do
* not install.
* @return int
*/
public JarVerificationResult verify(File jarFile) {
try {
// new verification, clean instance variables
initializeVariables(jarFile);
// verify integrity
verifyIntegrity(jarFile);
// do not close input stream
// as verifyIntegrity already did it
// verify source certificate
if (result.getVerificationCode() == JarVerification.JAR_INTEGRITY_VERIFIED)
verifyAuthentication();
} catch (Exception e) {
result.setVerificationCode(JarVerification.UNKNOWN_ERROR);
result.setResultException(e);
}
return result;
}
/**
* Verifies that each file has at least one certificate
* valid in the keystore
*
* At least one certificate from each Certificate Array
* of the Jar file must be found in the known Certificates
*/
private void verifyAuthentication() throws CoreException {
CertificatePair[] entries = result.getRootCertificates();
boolean certificateFound = false;
// If all the cartificate of an entry are
// not found in the list of known certifcate
// the certificate is not trusted by any keystore.
for (int i = 0; i < entries.length; i++) {
certificateFound = existsInKeystore(entries[i].getRoot());
if (certificateFound) {
result.setVerificationCode(JarVerification.JAR_SOURCE_VERIFIED);
result.setFoundCertificate(entries[i]);
return;
}
}
}
/**
* Verifies the integrity of the JAR
*/
private void verifyIntegrity(File jarFile) {
JarInputStream jis = null;
Collection certificateEntries; // of Certificate[]
try {
// If the JAR is signed and not valid
// a security exception will be thrown
// while reading it
jis = new JarInputStream(new FileInputStream(jarFile), true);
List filesInJar = readJarFile(jis);
// you have to read all the files once
// before getting the certificates
if (jis.getManifest() != null) {
Iterator iter = filesInJar.iterator();
boolean certificateFound = false;
while (iter.hasNext()) {
Certificate[] certs = ((JarEntry) iter.next()).getCertificates();
if ((certs != null) && (certs.length != 0)) {
certificateFound = true;
result.addCertificates(certs);
};
}
if (certificateFound)
result.setVerificationCode(JarVerification.JAR_INTEGRITY_VERIFIED);
else
result.setVerificationCode(JarVerification.JAR_NOT_SIGNED);
}
} catch (SecurityException e) {
// Jar file is signed
// but content has changed since signed
result.setVerificationCode(JarVerification.JAR_CORRUPTED);
} catch (InterruptedException e) {
result.setVerificationCode(JarVerification.VERIFICATION_CANCELLED);
} catch (Exception e) {
result.setVerificationCode(JarVerification.UNKNOWN_ERROR);
result.setResultException(e);
} finally {
if (jis != null) {
try {
jis.close();
} catch (IOException e) {
} // nothing
}
}
}
/**
*
*/
private CoreException newCoreException(String s, Throwable e)
throws CoreException {
String id =
UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier();
return new CoreException(new Status(IStatus.ERROR, id, 0, s, e));
}
}