
org.eclipse.osgi.internal.signedcontent.SignedBundleHook Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.eclipse.osgi Show documentation
Show all versions of org.eclipse.osgi Show documentation
This is org.eclipse.osgi jar used by Scout SDK
/*******************************************************************************
* Copyright (c) 2006, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.signedcontent;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.*;
import java.util.zip.ZipFile;
import org.eclipse.osgi.framework.log.FrameworkLogEntry;
import org.eclipse.osgi.internal.framework.EquinoxBundle;
import org.eclipse.osgi.internal.framework.EquinoxContainer;
import org.eclipse.osgi.internal.hookregistry.*;
import org.eclipse.osgi.internal.service.security.KeyStoreTrustEngine;
import org.eclipse.osgi.internal.signedcontent.SignedStorageHook.StorageHookImpl;
import org.eclipse.osgi.service.security.TrustEngine;
import org.eclipse.osgi.signedcontent.*;
import org.eclipse.osgi.storage.BundleInfo.Generation;
import org.eclipse.osgi.storage.bundlefile.*;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* Implements signed bundle hook support for the framework
*/
public class SignedBundleHook implements ActivatorHookFactory, BundleFileWrapperFactoryHook, HookConfigurator, SignedContentFactory {
static final int VERIFY_CERTIFICATE = 0x01;
static final int VERIFY_TRUST = 0x02;
static final int VERIFY_RUNTIME = 0x04;
static final int VERIFY_ALL = VERIFY_CERTIFICATE | VERIFY_TRUST | VERIFY_RUNTIME;
private final static String SUPPORT_CERTIFICATE = "certificate"; //$NON-NLS-1$
private final static String SUPPORT_TRUST = "trust"; //$NON-NLS-1$
private final static String SUPPORT_RUNTIME = "runtime"; //$NON-NLS-1$
private final static String SUPPORT_ALL = "all"; //$NON-NLS-1$
private final static String SUPPORT_TRUE = "true"; //$NON-NLS-1$
//TODO: comes from configuration!;
private final static String CACERTS_PATH = System.getProperty("java.home") + File.separatorChar + "lib" + File.separatorChar + "security" + File.separatorChar + "cacerts"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-4$
private final static String CACERTS_TYPE = "JKS"; //$NON-NLS-1$
private final static String SIGNED_BUNDLE_SUPPORT = "osgi.support.signature.verify"; //$NON-NLS-1$
private final static String SIGNED_CONTENT_SUPPORT = "osgi.signedcontent.support"; //$NON-NLS-1$
private final static String OSGI_KEYSTORE = "osgi.framework.keystore"; //$NON-NLS-1$
private int supportSignedBundles;
TrustEngineListener trustEngineListener;
private String trustEngineNameProp;
private ServiceRegistration signedContentFactoryReg;
private ServiceRegistration systemTrustEngineReg;
private List> osgiTrustEngineReg;
private ServiceTracker trustEngineTracker;
private BundleContext context;
private EquinoxContainer container;
@Override
public BundleActivator createActivator() {
return new BundleActivator() {
@Override
public void start(BundleContext bc) throws Exception {
frameworkStart(bc);
}
@Override
public void stop(BundleContext bc) throws Exception {
frameworkStop(bc);
}
};
}
BundleContext getContext() {
return context;
}
void frameworkStart(BundleContext bc) {
this.context = bc;
if ((supportSignedBundles & VERIFY_TRUST) != 0)
// initialize the trust engine listener only if trust is being established with a trust engine
trustEngineListener = new TrustEngineListener(context, this);
// always register the trust engine
Dictionary trustEngineProps = new Hashtable(7);
trustEngineProps.put(Constants.SERVICE_RANKING, new Integer(Integer.MIN_VALUE));
trustEngineProps.put(SignedContentConstants.TRUST_ENGINE, SignedContentConstants.DEFAULT_TRUST_ENGINE);
KeyStoreTrustEngine systemTrustEngine = new KeyStoreTrustEngine(CACERTS_PATH, CACERTS_TYPE, null, "System", this); //$NON-NLS-1$
systemTrustEngineReg = context.registerService(TrustEngine.class.getName(), systemTrustEngine, trustEngineProps);
String osgiTrustPath = context.getProperty(OSGI_KEYSTORE);
if (osgiTrustPath != null) {
try {
URL url = new URL(osgiTrustPath);
if ("file".equals(url.getProtocol())) { //$NON-NLS-1$
trustEngineProps.put(SignedContentConstants.TRUST_ENGINE, OSGI_KEYSTORE);
String path = url.getPath();
osgiTrustEngineReg = new ArrayList>(1);
osgiTrustEngineReg.add(context.registerService(TrustEngine.class.getName(), new KeyStoreTrustEngine(path, CACERTS_TYPE, null, OSGI_KEYSTORE, this), trustEngineProps));
}
} catch (MalformedURLException e) {
log("Invalid setting for " + OSGI_KEYSTORE, FrameworkLogEntry.WARNING, e); //$NON-NLS-1$
}
} else {
String osgiTrustRepoPaths = context.getProperty(Constants.FRAMEWORK_TRUST_REPOSITORIES);
if (osgiTrustRepoPaths != null) {
trustEngineProps.put(SignedContentConstants.TRUST_ENGINE, Constants.FRAMEWORK_TRUST_REPOSITORIES);
StringTokenizer st = new StringTokenizer(osgiTrustRepoPaths, File.pathSeparator);
osgiTrustEngineReg = new ArrayList>(1);
while (st.hasMoreTokens()) {
String trustRepoPath = st.nextToken();
osgiTrustEngineReg.add(context.registerService(TrustEngine.class.getName(), new KeyStoreTrustEngine(trustRepoPath, CACERTS_TYPE, null, OSGI_KEYSTORE, this), trustEngineProps));
}
}
}
// always register the signed content factory
signedContentFactoryReg = context.registerService(SignedContentFactory.class.getName(), this, null);
}
void frameworkStop(BundleContext bc) {
if (signedContentFactoryReg != null) {
signedContentFactoryReg.unregister();
signedContentFactoryReg = null;
}
if (systemTrustEngineReg != null) {
systemTrustEngineReg.unregister();
systemTrustEngineReg = null;
}
if (osgiTrustEngineReg != null) {
for (Iterator> it = osgiTrustEngineReg.iterator(); it.hasNext();)
it.next().unregister();
osgiTrustEngineReg = null;
}
if (trustEngineTracker != null) {
trustEngineTracker.close();
trustEngineTracker = null;
}
}
public BundleFileWrapper wrapBundleFile(BundleFile bundleFile, Generation generation, boolean base) {
try {
if (bundleFile != null) {
StorageHookImpl hook = generation.getStorageHook(SignedStorageHook.class);
SignedBundleFile signedBaseFile;
if (base && hook != null) {
signedBaseFile = new SignedBundleFile(bundleFile, hook.signedContent, supportSignedBundles, this);
if (hook.signedContent == null) {
signedBaseFile.initializeSignedContent();
SignedContentImpl signedContent = signedBaseFile.getSignedContent();
hook.signedContent = signedContent != null && signedContent.isSigned() ? signedContent : null;
}
} else
signedBaseFile = new SignedBundleFile(bundleFile, null, supportSignedBundles, this);
signedBaseFile.initializeSignedContent();
SignedContentImpl signedContent = signedBaseFile.getSignedContent();
if (signedContent != null && signedContent.isSigned()) {
// only use the signed file if there are certs
signedContent.setContent(signedBaseFile);
return new BundleFileWrapper(signedBaseFile);
}
}
} catch (IOException e) {
log("Bad bundle file: " + bundleFile.getBaseFile(), FrameworkLogEntry.WARNING, e); //$NON-NLS-1$
} catch (GeneralSecurityException e) {
log("Bad bundle file: " + bundleFile.getBaseFile(), FrameworkLogEntry.WARNING, e); //$NON-NLS-1$
}
return null;
}
public void addHooks(HookRegistry hookRegistry) {
container = hookRegistry.getContainer();
hookRegistry.addActivatorHookFactory(this);
String[] support = ManifestElement.getArrayFromList(hookRegistry.getConfiguration().getConfiguration(SIGNED_CONTENT_SUPPORT, hookRegistry.getConfiguration().getConfiguration(SIGNED_BUNDLE_SUPPORT)), ","); //$NON-NLS-1$
for (int i = 0; i < support.length; i++) {
if (SUPPORT_CERTIFICATE.equals(support[i]))
supportSignedBundles |= VERIFY_CERTIFICATE;
else if (SUPPORT_TRUST.equals(support[i]))
supportSignedBundles |= VERIFY_CERTIFICATE | VERIFY_TRUST;
else if (SUPPORT_RUNTIME.equals(support[i]))
supportSignedBundles |= VERIFY_CERTIFICATE | VERIFY_RUNTIME;
else if (SUPPORT_TRUE.equals(support[i]) || SUPPORT_ALL.equals(support[i]))
supportSignedBundles |= VERIFY_ALL;
}
trustEngineNameProp = hookRegistry.getConfiguration().getConfiguration(SignedContentConstants.TRUST_ENGINE);
if ((supportSignedBundles & VERIFY_CERTIFICATE) != 0) {
hookRegistry.addStorageHookFactory(new SignedStorageHook());
hookRegistry.addBundleFileWrapperFactoryHook(this);
}
}
public SignedContent getSignedContent(File content) throws IOException, InvalidKeyException, SignatureException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException {
if (content == null)
throw new IllegalArgumentException("null content"); //$NON-NLS-1$
BundleFile contentBundleFile;
if (content.isDirectory()) {
contentBundleFile = new DirBundleFile(content, false);
} else {
// make sure we have a ZipFile first, this will throw an IOException if not valid
ZipFile temp = new ZipFile(content);
temp.close();
contentBundleFile = new ZipBundleFile(content, null, null, container.getConfiguration().getDebug());
}
SignedBundleFile result = new SignedBundleFile(contentBundleFile, null, VERIFY_ALL, this);
try {
result.initializeSignedContent();
} catch (InvalidKeyException e) {
throw (InvalidKeyException) new InvalidKeyException(NLS.bind(SignedContentMessages.Factory_SignedContent_Error, content)).initCause(e);
} catch (SignatureException e) {
throw (SignatureException) new SignatureException(NLS.bind(SignedContentMessages.Factory_SignedContent_Error, content)).initCause(e);
} catch (CertificateException e) {
throw (CertificateException) new CertificateException(NLS.bind(SignedContentMessages.Factory_SignedContent_Error, content)).initCause(e);
} catch (NoSuchAlgorithmException e) {
throw (NoSuchAlgorithmException) new NoSuchAlgorithmException(NLS.bind(SignedContentMessages.Factory_SignedContent_Error, content)).initCause(e);
} catch (NoSuchProviderException e) {
throw (NoSuchProviderException) new NoSuchProviderException(NLS.bind(SignedContentMessages.Factory_SignedContent_Error, content)).initCause(e);
}
return new SignedContentFile(result.getSignedContent());
}
public SignedContent getSignedContent(Bundle bundle) throws IOException, InvalidKeyException, SignatureException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException, IllegalArgumentException {
final Generation generation = (Generation) ((EquinoxBundle) bundle).getModule().getCurrentRevision().getRevisionInfo();
StorageHookImpl hook = generation.getStorageHook(SignedStorageHook.class);
SignedContent result = hook != null ? hook.signedContent : null;
if (result != null)
return result; // just reuse the signed content the storage hook
// must create a new signed content using the raw file
if (System.getSecurityManager() == null)
return getSignedContent(generation.getBundleFile().getBaseFile());
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction() {
public SignedContent run() throws Exception {
return getSignedContent(generation.getBundleFile().getBaseFile());
}
});
} catch (PrivilegedActionException e) {
if (e.getException() instanceof IOException)
throw (IOException) e.getException();
if (e.getException() instanceof InvalidKeyException)
throw (InvalidKeyException) e.getException();
if (e.getException() instanceof SignatureException)
throw (SignatureException) e.getException();
if (e.getException() instanceof CertificateException)
throw (CertificateException) e.getException();
if (e.getException() instanceof NoSuchAlgorithmException)
throw (NoSuchAlgorithmException) e.getException();
if (e.getException() instanceof NoSuchProviderException)
throw (NoSuchProviderException) e.getException();
throw new RuntimeException("Unknown error.", e.getException()); //$NON-NLS-1$
}
}
public void log(String msg, int severity, Throwable t) {
container.getLogServices().log(EquinoxContainer.NAME, severity, msg, t);
}
private TrustEngine[] getTrustEngines() {
// find all the trust engines available
if (context == null)
return new TrustEngine[0];
if (trustEngineTracker == null) {
// read the trust provider security property
Filter filter = null;
if (trustEngineNameProp != null)
try {
filter = context.createFilter("(&(" + Constants.OBJECTCLASS + "=" + TrustEngine.class.getName() + ")(" + SignedContentConstants.TRUST_ENGINE + "=" + trustEngineNameProp + "))"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$//$NON-NLS-5$
} catch (InvalidSyntaxException e) {
log("Invalid trust engine filter", FrameworkLogEntry.WARNING, e); //$NON-NLS-1$
}
if (filter != null) {
trustEngineTracker = new ServiceTracker(context, filter, new TrustEngineCustomizer());
} else
trustEngineTracker = new ServiceTracker(context, TrustEngine.class.getName(), new TrustEngineCustomizer());
trustEngineTracker.open();
}
Object[] services = trustEngineTracker.getServices();
if (services != null) {
TrustEngine[] engines = new TrustEngine[services.length];
System.arraycopy(services, 0, engines, 0, services.length);
return engines;
}
return new TrustEngine[0];
}
class TrustEngineCustomizer implements ServiceTrackerCustomizer {
@Override
public TrustEngine addingService(ServiceReference reference) {
TrustEngine engine = getContext().getService(reference);
if (engine != null) {
try {
Field trustEngineListenerField = TrustEngine.class.getDeclaredField("trustEngineListener"); //$NON-NLS-1$
trustEngineListenerField.setAccessible(true);
trustEngineListenerField.set(engine, SignedBundleHook.this.trustEngineListener);
} catch (Exception e) {
log("Unable to set the trust engine listener.", FrameworkLogEntry.ERROR, e); //$NON-NLS-1$
}
}
return engine;
}
@Override
public void modifiedService(ServiceReference reference, TrustEngine service) {
// nothing
}
@Override
public void removedService(ServiceReference reference, TrustEngine service) {
// nothing
}
}
void determineTrust(SignedContentImpl trustedContent, int supportFlags) {
TrustEngine[] engines = null;
SignerInfo[] signers = trustedContent.getSignerInfos();
for (int i = 0; i < signers.length; i++) {
// first check if we need to find an anchor
if (signers[i].getTrustAnchor() == null) {
// no anchor set ask the trust engines
if (engines == null)
engines = getTrustEngines();
// check trust of singer certs
Certificate[] signerCerts = signers[i].getCertificateChain();
((SignerInfoImpl) signers[i]).setTrustAnchor(findTrustAnchor(signerCerts, engines, supportFlags));
// if signer has a tsa check trust of tsa certs
SignerInfo tsaSignerInfo = trustedContent.getTSASignerInfo(signers[i]);
if (tsaSignerInfo != null) {
Certificate[] tsaCerts = tsaSignerInfo.getCertificateChain();
((SignerInfoImpl) tsaSignerInfo).setTrustAnchor(findTrustAnchor(tsaCerts, engines, supportFlags));
}
}
}
}
private Certificate findTrustAnchor(Certificate[] certs, TrustEngine[] engines, int supportFlags) {
if ((supportFlags & SignedBundleHook.VERIFY_TRUST) == 0)
// we are not searching the engines; in this case we just assume the root cert is trusted
return certs != null && certs.length > 0 ? certs[certs.length - 1] : null;
for (int i = 0; i < engines.length; i++) {
try {
Certificate anchor = engines[i].findTrustAnchor(certs);
if (anchor != null)
// found an anchor
return anchor;
} catch (IOException e) {
// log the exception and continue
log("TrustEngine failure: " + engines[i].getName(), FrameworkLogEntry.WARNING, e); //$NON-NLS-1$
}
}
return null;
}
}