//
// This file is part of the prose package.
//
// The contents of this file are subject to the Mozilla Public License
// Version 1.1 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
// for the specific language governing rights and limitations under the
// License.
//
// The Original Code is prose.
//
// The Initial Developer of the Original Code is Andrei Popovici. Portions
// created by Andrei Popovici are Copyright (C) 2002 Andrei Popovici.
// All Rights Reserved.
//
// Contributor(s):
// $Id: SecureLocalAspectManager.java,v 1.4 2008/11/18 11:44:47 anicoara Exp $
// =====================================================================
//
package ch.ethz.prose;
// used packages
import java.io.FileInputStream;
import java.io.IOException;
import java.security.AccessController;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.Enumeration;
import java.util.Hashtable;
import ch.ethz.inf.util.Logger;
/**
* Class SecureLocalAspectManager extends LocalAspectManager. It recognizes
* SignedExtensions in <code>insertExtension</code> and performs the insertion
* under the permissions granted to the SecureLocalAspectManager class, in case
* that the signature is valid and the signer is trusted to insert extensions.<p>
*
* @version $Revision: 1.4 $
* @author Marcel Mueller
*
* <P>
* Used Properties:
* <UL>
* <LI><CODE>ch.ethz.prose.keystore.location</CODE>
* <b>Values:</b> path to keystore to use<br>
* <b>Usage:</b></LI>
* <LI><CODE>ch.ethz.prose.keystore.password</CODE>
* <b>Values:</b> password to keystore<br>
* <b>Usage:</b></LI>
* </UL>
*/
public class SecureLocalAspectManager extends LocalAspectManager {
private KeyStore keyStore;
private Hashtable certTable;
/**
* Constructs an extension manager that inserts SignedExtensions under privileged rights
*/
public SecureLocalAspectManager(boolean isConnectedToVM, ch.ethz.jvmai.JVMAspectInterface ai) {
super(isConnectedToVM, ai);
}
/**
* Decides whether a key is valid and trusted to insert an extension
* under the privileged permissions of this class.<p>
*
* The actual implementation is very restricted: accepts if a trusted
* certificate exists in the keystore defined by the system properties
* <code>ch.ethz.prose.keystore.location</code> and
* <code>ch.ethz.prose.keystore.password</code>. (ATTENTION: THIS
* IS A HUGE SECURITY HOLE!!!). Feel free to build a
* subclass with a better implementation.<p>
*
* It was thought to attach certificates to signed extensions to help
* the target system to make its decision. As in JDK 1.2.2 the class
* java.security.cert.Certificate is not Serializable, this feature is
* unluckily not yet implemented.
*
* @param key the key that signed the extension
* @return <code>true</code> if a trusted certificate exists in the keystore
*/
protected boolean validateKey(PublicKey key) {
try {
return isTrusted(key);
} catch (Exception e) {
Logger.message("SecureLocalAspectManager.validateKey: exception occurred ("+e+")");
return false;
}
}
/**
* Insert the extension <code>ext</code> into the extension
* manager. If <code>ext</code> is of type <code>SignedAspect</code>,
* the signature is verified and if the key is known to be trusted to
* insert extensions, the insertion is performed exclusively under the rights
* granted to this class.<p>
*
* If the extension fails the verification or the key is not trusted, it is attempted
* to install the extension in the usual way (without privileged permissions).
*
* @param ext the extension to be inserted
* @exception AspectManagerException if extension cannot be installed
*/
public synchronized void insert(Aspect ext) throws AspectManagerException {
if (ext==null) {
throw new AspectManagerException("Attempt to insert \"null\" as Aspect!");
}
if (ext instanceof SignedAspect) {
final SignedAspect se = (SignedAspect) ext;
Boolean b = (Boolean) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
// verify if extension was signed correctly with containing key
se.verifyExtension();
} catch (Exception e) {
Logger.message("SecureLocalAspectManager.insertExtension: extension could not be verified");
return Boolean.FALSE;
}
// validate the key (do we trust this public key to insert extensions?)
if (validateKey(se.getPublicKey())) {
// do super: we vouch for the extension to be a friendly one: it is inserted with our privileges
try {
superInsertExtension(se);
} catch (Exception e) {
Logger.message("SecureLocalAspectManager.insertExtension: priviliged insertion did not succeed ("+e+")");
}
return Boolean.TRUE;
} else {
Logger.message("SecureLocalAspectManager.insertExtension: signer of extension is not trusted");
return Boolean.FALSE;
}
}
});
if (b.booleanValue()) {
return;
}
}
super.insert(ext);
}
// work-around, I could not find direct way to call super method out of an inner class
private synchronized void superInsertExtension(Aspect ext) throws AspectManagerException {
super.insert(ext);
}
// get keystore instance
private KeyStore getKeyStore() throws KeyStoreException, NoSuchAlgorithmException, IOException, CertificateException {
if (keyStore == null) {
String ksl = System.getProperty("ch.ethz.prose.keystore.location");
String ksp = System.getProperty("ch.ethz.prose.keystore.password");
keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(ksl), ksp.toCharArray());
}
return keyStore;
}
// is there a trusted certificate in our keystore?
private boolean isTrusted(PublicKey key) throws KeyStoreException, NoSuchAlgorithmException, IOException, CertificateException {
if (certTable == null) {
certTable = new Hashtable();
KeyStore ks = getKeyStore();
Enumeration enumeration = ks.aliases();
while (enumeration.hasMoreElements()) {
String alias = (String) enumeration.nextElement();
Certificate[] certs = ks.getCertificateChain(alias);
if (certs != null) {
for (int i=0; i<certs.length; i++) {
certTable.put(certs[i].getPublicKey(), certs[i]);
}
}
}
}
return certTable.containsKey(key);
}
}