Switch to protection domains and implement support for digitally signed bundles.
I'll comment on the concerned JIRA issues later (FELIX-21)(FELIX-22).



git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@434386 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index f30af56..77fffc9 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -19,10 +19,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.List;
+import java.util.*;
 
 import org.osgi.framework.*;
 
@@ -65,70 +62,70 @@
     public URL getEntry(String name)
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            try 
+            try
             {
-                ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+                ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                     AdminPermission.RESOURCE));
-            } 
+            }
             catch (Exception e)
             {
                 return null; // No permission
             }
         }
-        
+
         return m_felix.getBundleEntry(this, name);
     }
 
     public Enumeration getEntryPaths(String path)
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            try 
+            try
             {
-                ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+                ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                     AdminPermission.RESOURCE));
-            } 
+            }
             catch (Exception e)
             {
                 return null; // No permission
             }
         }
-        
+
         return m_felix.getBundleEntryPaths(this, path);
     }
 
     public Enumeration findEntries(String path, String filePattern, boolean recurse)
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            try 
+            try
             {
-                ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+                ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                     AdminPermission.RESOURCE));
-            } 
+            }
             catch (Exception e)
             {
                 return null; // No permission
             }
         }
-        
+
         return m_felix.findBundleEntries(this, path, filePattern, recurse);
     }
 
     public Dictionary getHeaders()
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                 AdminPermission.METADATA));
         }
         return m_felix.getBundleHeaders(this);
@@ -142,10 +139,10 @@
     public String getLocation()
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                 AdminPermission.METADATA));
         }
         return m_felix.getBundleLocation(this);
@@ -170,51 +167,51 @@
     public ServiceReference[] getRegisteredServices()
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
             ServiceReference[] refs = m_felix.getBundleRegisteredServices(this);
-            
+
             if (refs == null)
             {
                 return refs;
             }
-            
+
             List result = new ArrayList();
-            
+
             for (int i = 0;i < refs.length;i++)
             {
                 String[] objectClass = (String[]) refs[i].getProperty(
                     Constants.OBJECTCLASS);
-                
+
                 if (objectClass == null)
                 {
                     continue;
                 }
-                
+
                 for (int j = 0;j < objectClass.length;j++)
                 {
                     try
                     {
                         ((SecurityManager) sm).checkPermission(new ServicePermission(
                             objectClass[j], ServicePermission.GET));
-                        
+
                         result.add(refs[i]);
-                        
+
                         break;
-                    } 
+                    }
                     catch (Exception e)
                     {
-                        
+
                     }
                 }
             }
-            
+
             if (result.isEmpty())
             {
                 return null;
             }
-            
+
             return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
         }
         else
@@ -226,54 +223,54 @@
     public ServiceReference[] getServicesInUse()
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
             ServiceReference[] refs = m_felix.getBundleServicesInUse(this);
-            
+
             if (refs == null)
             {
                 return refs;
             }
-            
+
             List result = new ArrayList();
-            
+
             for (int i = 0;i < refs.length;i++)
             {
                 String[] objectClass = (String[]) refs[i].getProperty(
                     Constants.OBJECTCLASS);
-                
+
                 if (objectClass == null)
                 {
                     continue;
                 }
-                
+
                 for (int j = 0;j < objectClass.length;j++)
                 {
                     try
                     {
                         ((SecurityManager) sm).checkPermission(new ServicePermission(
                             objectClass[j], ServicePermission.GET));
-                        
+
                         result.add(refs[i]);
-                        
+
                         break;
-                    } 
+                    }
                     catch (Exception e)
                     {
-                        
+
                     }
                 }
             }
-            
+
             if (result.isEmpty())
             {
                 return null;
             }
-            
+
             return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
         }
-        
+
         return m_felix.getBundleServicesInUse(this);
     }
 
@@ -296,33 +293,33 @@
     public Class loadClass(String name) throws ClassNotFoundException
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            try 
+            try
             {
-                ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+                ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                     AdminPermission.CLASS));
-            } 
+            }
             catch (Exception e)
             {
                 throw new ClassNotFoundException("No permission.", e);
             }
         }
-        
+
         return m_felix.loadBundleClass(this, name);
     }
 
     public void start() throws BundleException
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                 AdminPermission.EXECUTE));
         }
- 
+
         m_felix.startBundle(this, true);
     }
 
@@ -334,39 +331,39 @@
     public void update(InputStream is) throws BundleException
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                 AdminPermission.LIFECYCLE));
         }
-        
+
         m_felix.updateBundle(this, is);
     }
 
     public void stop() throws BundleException
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                 AdminPermission.EXECUTE));
         }
-        
+
         m_felix.stopBundle(this, true);
     }
 
     public void uninstall() throws BundleException
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                 AdminPermission.LIFECYCLE));
         }
-        
+
         m_felix.uninstallBundle(this);
     }
 
@@ -384,13 +381,13 @@
         // TODO: Implement Bundle.getHeaders(String locale)
         // Should be done after [#FELIX-27] resolution
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                 AdminPermission.METADATA));
         }
-     
+
         return null;
     }
 
@@ -398,20 +395,20 @@
     {
         // TODO: Implement Bundle.getResources()
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            try 
+            try
             {
-                ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+                ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                     AdminPermission.RESOURCE));
-            } 
+            }
             catch (Exception e)
             {
                 return null; // No permission
             }
         }
-        
+
         return null;
     }
 
@@ -423,4 +420,13 @@
         }
         return false;
     }
+
+    /*
+     * This is a hack to get access to the subject-dns of the current revision
+     * from inside the AdminPermission.
+     */
+    String[] getSubjectDNs()
+    {
+        return m_info.getArchive().getDNChains();
+    }
 }
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 2a56a40..0ed5465 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -18,6 +18,7 @@
 
 import java.io.*;
 import java.net.*;
+import java.security.*;
 import java.util.*;
 
 import org.apache.felix.framework.cache.*;
@@ -94,6 +95,11 @@
     // Reusable bundle URL stream handler.
     private URLStreamHandler m_bundleStreamHandler = null;
 
+    // The secure action used to do privileged calls
+    private SecureAction m_secureAction = new SecureAction();
+
+    private Collection m_trustedCaCerts = null;
+
     /**
      * <p>
      * This method starts the framework instance; instances of the framework
@@ -182,22 +188,30 @@
      * able to take advantage of the features it provides; refer to its
      * class documentation for more information.
      * </p>
-     * 
+     *
      * @param configMutable An object for obtaining configuration properties,
      *        may be <tt>null</tt>.
      * @param frameworkProps An object for obtaining framework properties,
      *        may be <tt>null</tt>.
      * @param activatorList A list of System Bundle activators.
     **/
+    public synchronized void start(MutablePropertyResolver configMutable,
+        List activatorList)
+    {
+        start(configMutable, activatorList, (Collection) null);
+    }
+
     public synchronized void start(
         MutablePropertyResolver configMutable,
-        List activatorList)
+        List activatorList, Collection trustedCaCerts)
     {
         if (m_frameworkStatus != INITIAL_STATUS)
         {
             throw new IllegalStateException("Invalid framework status: " + m_frameworkStatus);
         }
 
+        m_trustedCaCerts = trustedCaCerts;
+
         // The framework is now in its startup sequence.
         m_frameworkStatus = STARTING_STATUS;
 
@@ -227,7 +241,7 @@
 
         try
         {
-            m_cache = new BundleCache(m_config, m_logger);
+            m_cache = new BundleCache(m_config, m_logger, m_trustedCaCerts);
         }
         catch (Exception ex)
         {
@@ -241,23 +255,7 @@
                 ? false : embedded.equals("true");
             if (!isEmbedded)
             {
-                if (System.getSecurityManager() != null)
-                {
-                    java.security.AccessController.doPrivileged(
-                        new java.security.PrivilegedAction()
-                        {
-                            public Object run()
-                            {
-                                System.exit(-1);
-                                
-                                return null;
-                            }
-                        });
-                }
-                else
-                {
-                    System.exit(-1);
-                }
+                m_secureAction.exit(-1);
             }
             else
             {
@@ -339,7 +337,8 @@
             // Create a simple bundle info for the system bundle.
             BundleInfo info = new BundleInfo(
                 m_logger, new SystemBundleArchive(), null);
-            systembundle = new SystemBundle(this, info, activatorList);
+            systembundle = new SystemBundle(this, info, activatorList,
+                m_secureAction);
             // Create a module for the system bundle.
             IModuleDefinition md = new ModuleDefinition(
                 systembundle.getExports(), null, null, null);
@@ -350,6 +349,9 @@
             m_factory.setContentLoader(
                 systembundle.getInfo().getCurrentModule(),
                 systembundle.getContentLoader());
+            m_factory.setSecurityContext(
+                systembundle.getInfo().getCurrentModule(),
+                systembundle.getClass().getProtectionDomain());
 
             m_installedBundleMap.put(
                 systembundle.getInfo().getLocation(), systembundle);
@@ -380,7 +382,7 @@
             m_logger.log(Logger.LOG_ERROR, "Unable to start system bundle.", ex);
             throw new RuntimeException("Unable to start system bundle.");
         }
-        
+
         // Reload and cached bundles.
         BundleArchive[] archives = null;
 
@@ -672,10 +674,10 @@
             // Determine if we are lowering or raising the
             // active start level.
             boolean lowering = (requestedLevel < m_activeStartLevel);
-    
+
             // Record new start level.
             m_activeStartLevel = requestedLevel;
-    
+
             // Get a snapshot of all installed bundles.
             bundles = getBundles();
 
@@ -883,7 +885,7 @@
     {
         // Acquire bundle lock.
         acquireBundleLock((BundleImpl) bundle);
-        
+
         Throwable rethrow = null;
 
         try
@@ -897,7 +899,7 @@
             {
                 BundleImpl impl = (BundleImpl) bundle;
                 impl.getInfo().setStartLevel(startLevel);
-    
+
                 try
                 {
                     // Start the bundle if necessary.
@@ -1070,11 +1072,9 @@
             try
             {
                 return (obj instanceof java.security.Permission)
-                    ? java.security.Policy.getPolicy().getPermissions(
-                        new java.security.CodeSource(
-                            new java.net.URL(bundle.getInfo().getLocation()),
-                            (java.security.cert.Certificate[]) null))
-                                .implies((java.security.Permission) obj)
+                    ? ((ProtectionDomain)
+                    bundle.getInfo().getCurrentModule().getSecurityContext())
+                    .implies((java.security.Permission) obj)
                     : false;
             }
             catch (Exception ex)
@@ -1083,7 +1083,7 @@
                     Logger.LOG_WARNING,
                     "Exception while evaluating the permission.",
                     ex);
-                return false; 
+                return false;
             }
         }
 
@@ -1124,7 +1124,7 @@
         // we only acquire the lock for the bundle being started, because
         // when resolve is called on this bundle, it will eventually
         // call resolve on the module loader search policy, which does
-        // its own locking on the module factory instance. Since the 
+        // its own locking on the module factory instance. Since the
         // resolve algorithm is locking the module factory instance, it
         // is not possible for other bundles to be installed or removed,
         // so we don't have to worry about these possibilities.
@@ -1205,16 +1205,8 @@
             // Activate the bundle if it has an activator.
             if (bundle.getInfo().getActivator() != null)
             {
-                if (System.getSecurityManager() != null)
-                {
-                    java.security.AccessController.doPrivileged(
-                        new PrivilegedActivatorCall(PrivilegedActivatorCall.START,
-                        info.getActivator(), info.getContext()));
-                }
-                else
-                {
-                    info.getActivator().start(info.getContext());
-                }
+                m_secureAction.startActivator(info.getActivator(),
+                    info.getContext());
             }
 
             // TODO: CONCURRENCY - Reconsider firing event outside of the
@@ -1251,7 +1243,7 @@
             {
                 throw (SecurityException) th;
             }
-            else if ((System.getSecurityManager() != null) && 
+            else if ((System.getSecurityManager() != null) &&
                 (th instanceof java.security.PrivilegedActionException))
             {
                 th = ((java.security.PrivilegedActionException) th).getException();
@@ -1269,33 +1261,39 @@
         // to import the necessary packages.
         if (System.getSecurityManager() != null)
         {
-            URL url = null;
-            try
+
+            ProtectionDomain pd = (ProtectionDomain)
+                bundle.getInfo().getCurrentModule().getSecurityContext();
+
+            R4Import[] imports =
+                bundle.getInfo().getCurrentModule().getDefinition().getImports();
+
+            for (int i = 0;i < imports.length; i++)
             {
-                url = new URL(bundle.getInfo().getLocation());
-            }
-            catch (MalformedURLException ex)
-            {
-                throw new BundleException("Cannot resolve, bad URL "
-                    + bundle.getInfo().getLocation());
-            }
-            
-            try
-            {
-                java.security.AccessController.doPrivileged(
-                    new CheckImportsPrivileged(url, bundle));
-            }
-            catch (java.security.PrivilegedActionException ex)
-            {
-                Exception thrown = 
-                    ((java.security.PrivilegedActionException) ex).getException();
-                if (thrown instanceof SecurityException)
+                PackagePermission perm = new PackagePermission(imports[i].getName(),
+                    PackagePermission.IMPORT);
+
+                if (!pd.implies(perm))
                 {
-                    throw (SecurityException) thrown;
+                   throw new java.security.AccessControlException(
+                       "PackagePermission.IMPORT denied for import: " +
+                       imports[i].getName(), perm);
                 }
-                else
+            }
+            // Check export permission for all exports of the current module.
+            R4Export[] implicitImports =
+                bundle.getInfo().getCurrentModule().getDefinition().getExports();
+
+            for (int i = 0;i < implicitImports.length; i++)
+            {
+                PackagePermission perm = new PackagePermission(
+                    implicitImports[i].getName(), PackagePermission.EXPORT);
+
+                if (!pd.implies(perm))
                 {
-                    throw new BundleException("Problem resolving: " + ex);
+                    throw new java.security.AccessControlException(
+                        "PackagePermission.EXPORT denied for implicit export: " +
+                        implicitImports[i].getName(), perm);
                 }
             }
         }
@@ -1343,7 +1341,7 @@
     {
         // We guarantee to close the input stream, so put it in a
         // finally clause.
-    
+
         try
         {
             // Variable to indicate whether bundle is active or not.
@@ -1385,21 +1383,21 @@
                         info.getBundleId(),
                         archive.getRevisionCount() - 1,
                         info.getCurrentHeader());
-                    
+
                     Object sm = System.getSecurityManager();
-                    
+
                     if (sm != null)
                     {
                         ((SecurityManager) sm).checkPermission(
                             new AdminPermission(bundle, AdminPermission.LIFECYCLE));
                     }
-                    
+
                     // Add module to bundle info.
                     info.addModule(module);
-                } 
+                }
                 catch (Exception ex)
                 {
-                    try 
+                    try
                     {
                         archive.undoRevise();
                     }
@@ -1407,7 +1405,7 @@
                     {
                         m_logger.log(Logger.LOG_ERROR, "Unable to rollback.", busted);
                     }
-                    
+
                     throw ex;
                 }
             }
@@ -1417,22 +1415,22 @@
                 rethrow = ex;
             }
 
-            // Set new state, mark as needing a refresh, and fire updated event 
+            // Set new state, mark as needing a refresh, and fire updated event
             // if successful.
             if (rethrow == null)
             {
                 info.setLastModified(System.currentTimeMillis());
                 info.setState(Bundle.INSTALLED);
                 fireBundleEvent(BundleEvent.UNRESOLVED, bundle);
-                
+
                 // Mark previous the bundle's old module for removal since
                 // it can no longer be used to resolve other modules per the spec.
                 ((ModuleImpl) info.getModules()[info.getModules().length - 2])
                     .setRemovalPending(true);
-        
+
                 fireBundleEvent(BundleEvent.UPDATED, bundle);
             }
-    
+
             // Restart bundle, but do not change the persistent state.
             // This will not start the bundle if it was not previously
             // active.
@@ -1441,12 +1439,12 @@
             // If update failed, rethrow exception.
             if (rethrow != null)
             {
-                if ((System.getSecurityManager() != null) && 
+                if ((System.getSecurityManager() != null) &&
                     (rethrow instanceof SecurityException))
                 {
                     throw (SecurityException) rethrow;
                 }
-                
+
                 throw new BundleException("Update failed.", rethrow);
             }
         }
@@ -1484,7 +1482,7 @@
         throws BundleException
     {
         Throwable rethrow = null;
-    
+
         // Set the bundle's persistent state to inactive if necessary.
         if (record)
         {
@@ -1492,7 +1490,7 @@
         }
 
         BundleInfo info = bundle.getInfo();
-        
+
         switch (info.getState())
         {
             case Bundle.UNINSTALLED:
@@ -1514,18 +1512,10 @@
         {
             if (bundle.getInfo().getActivator() != null)
             {
-                if (System.getSecurityManager() != null)
-                {
-                    java.security.AccessController.doPrivileged(
-                        new PrivilegedActivatorCall(PrivilegedActivatorCall.STOP,
-                        info.getActivator(), info.getContext()));
-                }
-                else
-                {
-                    info.getActivator().stop(info.getContext());
-                }
+                m_secureAction.stopActivator(info.getActivator(),
+                    info.getContext());
             }
-        
+
             // Try to save the activator in the cache.
             // NOTE: This is non-standard OSGi behavior and only
             // occurs if strictness is disabled.
@@ -1557,17 +1547,17 @@
 
         // Unregister any services offered by this bundle.
         m_registry.unregisterServices(bundle);
-        
+
         // Release any services being used by this bundle.
         m_registry.ungetServices(bundle);
-        
+
         // The spec says that we must remove all event
         // listeners for a bundle when it is stopped.
         m_dispatcher.removeListeners(bundle);
-        
+
         info.setState(Bundle.RESOLVED);
         fireBundleEvent(BundleEvent.STOPPED, bundle);
-        
+
         // Throw activator error if there was one.
         if (rethrow != null)
         {
@@ -1581,12 +1571,12 @@
             {
                 throw (SecurityException) rethrow;
             }
-            else if ((System.getSecurityManager() != null) && 
+            else if ((System.getSecurityManager() != null) &&
                 (rethrow instanceof java.security.PrivilegedActionException))
             {
                 rethrow = ((java.security.PrivilegedActionException) rethrow).getException();
             }
-    
+
             // Rethrow all other exceptions as a BundleException.
             throw new BundleException("Activator stop error.", rethrow);
         }
@@ -1772,9 +1762,9 @@
             {
                 BundleArchive archive = m_cache.getArchive(id);
                 bundle = new BundleImpl(this, createBundleInfo(archive));
-                
+
                 Object sm = System.getSecurityManager();
-                
+
                 if (sm != null)
                 {
                     ((SecurityManager) sm).checkPermission(
@@ -1798,13 +1788,13 @@
                             "Could not remove from cache.", ex1);
                     }
                 }
-                
-                if ((System.getSecurityManager() != null) && 
+
+                if ((System.getSecurityManager() != null) &&
                     (ex instanceof SecurityException))
                 {
                     throw (SecurityException) ex;
                 }
-                
+
                 throw new BundleException("Could not create bundle object.", ex);
             }
 
@@ -1840,10 +1830,10 @@
                 // Not much else we can do.
             }
         }
-    
+
         // Fire bundle event.
         fireBundleEvent(BundleEvent.INSTALLED, bundle);
-    
+
         // Return new bundle.
         return bundle;
     }
@@ -1982,7 +1972,7 @@
     /**
      * Implementation for BundleContext.removeServiceListener().
      * Removes service listeners from the listener list.
-     * 
+     *
      * @param bundle The context bundle of the listener
      * @param l The service listener to remove from the listener list.
     **/
@@ -2205,7 +2195,7 @@
      * the class was loaded from a bundle from this framework instance. If the
      * class was not loaded from a bundle or was loaded by a bundle in another
      * framework instance, then <tt>null</tt> is returned.
-     * 
+     *
      * @param clazz the class for which to find its associated bundle.
      * @return the bundle associated with the specified class or <tt>null</tt>
      *         if the class was not loaded by a bundle or its associated
@@ -2476,7 +2466,7 @@
                 // At this point the map contains every bundle that has been
                 // updated and/or removed as well as all bundles that import
                 // packages from these bundles.
-                
+
                 // Create refresh helpers for each bundle.
                 RefreshHelper[] helpers = new RefreshHelper[bundles.length];
                 for (int i = 0; i < bundles.length; i++)
@@ -2626,16 +2616,38 @@
                 revision,
                 m_config.get(Constants.FRAMEWORK_OS_NAME),
                 m_config.get(Constants.FRAMEWORK_PROCESSOR)));
-                
+
         // Create the module using the module definition.
         IModule module = m_factory.createModule(
             Long.toString(targetId) + "." + Integer.toString(revision), md);
 
+        ProtectionDomain pd = null;
+
+        if (System.getSecurityManager() != null)
+        {
+            String location = m_cache.getArchive(targetId).getLocation();
+
+            if (location.startsWith("reference:"))
+            {
+                location = location.substring("reference:".length());
+            }
+
+            CodeSource codesource = new CodeSource(
+                new URL(location),
+                m_cache.getArchive(targetId).getCertificates());
+
+            pd = new ProtectionDomain(codesource,
+                m_secureAction.getPolicy().getPermissions(codesource));
+        }
+
+        m_factory.setSecurityContext(module, pd);
+
         // Create the content loader from the module archive.
         IContentLoader contentLoader = new ContentLoaderImpl(
                 m_logger,
                 m_cache.getArchive(targetId).getRevision(revision).getContent(),
-                m_cache.getArchive(targetId).getRevision(revision).getContentPath());
+                m_cache.getArchive(targetId).getRevision(revision).getContentPath(),
+                pd);
         // Set the content loader's search policy.
         contentLoader.setSearchPolicy(
                 new R4SearchPolicy(m_policyCore, module));
@@ -2658,9 +2670,9 @@
         // CONCURRENCY NOTE:
         // This method is called indirectly from startBundle() (via _startBundle()),
         // which has the exclusion lock, so there is no need to do any locking here.
-    
+
         BundleActivator activator = null;
-    
+
         String strict = m_config.get(FelixConstants.STRICT_OSGI_PROP);
         boolean isStrict = (strict == null) ? true : strict.equals("true");
         if (!isStrict)
@@ -2676,7 +2688,7 @@
                 activator = null;
             }
         }
-    
+
         // If there was no cached activator, then get the activator
         // class from the bundle manifest.
         if (activator == null)
@@ -2702,7 +2714,7 @@
                 activator = (BundleActivator) clazz.newInstance();
             }
         }
-    
+
         return activator;
     }
 
@@ -2714,7 +2726,7 @@
         try
         {
             BundleInfo info = bundle.getInfo();
-    
+
             // In case of a refresh, then we want to physically
             // remove the bundle's modules from the module manager.
             // This is necessary for two reasons: 1) because
@@ -2844,8 +2856,8 @@
         catch (IOException ex)
         {
             ex.printStackTrace();
-        } 
- 
+        }
+
         // Maven uses a '-' to separate the version qualifier,
         // while OSGi uses a '.', so we need to convert to a '.'
         StringBuffer sb =
@@ -2858,7 +2870,7 @@
         }
         return sb.toString();
     }
-    
+
     private void processAutoProperties()
     {
         // The auto-install property specifies a space-delimited list of
@@ -3228,7 +3240,7 @@
             {
                 return;
             }
-            
+
             int idx = -1;
             for (int i = 0; i < m_uninstalledBundles.length; i++)
             {
@@ -3238,7 +3250,7 @@
                     break;
                 }
             }
-    
+
             if (idx >= 0)
             {
                 // If this is the only bundle, then point to empty list.
@@ -3280,11 +3292,11 @@
                     throw new BundleException("Unable to install, thread interrupted.");
                 }
             }
-            
+
             m_installRequestMap.put(location, location);
         }
     }
-    
+
     protected void releaseInstallLock(String location)
     {
         synchronized (m_installRequestLock_Priority1)
@@ -3312,7 +3324,7 @@
             bundle.getInfo().lock();
         }
     }
-    
+
     protected boolean acquireBundleLockOrFail(BundleImpl bundle)
     {
         synchronized (m_bundleLock)
@@ -3379,7 +3391,7 @@
                         bundles = (BundleImpl[]) list.toArray(new BundleImpl[list.size()]);
                     }
                 }
-                
+
                 // Check if all unresolved bundles can be locked.
                 boolean lockable = true;
                 if (bundles != null)
@@ -3388,7 +3400,7 @@
                     {
                         lockable = bundles[i].getInfo().isLockable();
                     }
-        
+
                     // If we can lock all bundles, then lock them.
                     if (lockable)
                     {
@@ -3487,10 +3499,10 @@
                         // Add all importing bundles to map.
                         populateImportGraph(target, map);
                     }
-                    
+
                     bundles = (BundleImpl[]) map.values().toArray(new BundleImpl[map.size()]);
                 }
-                
+
                 // Check if all corresponding bundles can be locked.
                 boolean lockable = true;
                 if (bundles != null)
@@ -3499,7 +3511,7 @@
                     {
                         lockable = bundles[i].getInfo().isLockable();
                     }
-        
+
                     // If we can lock all bundles, then lock them.
                     if (lockable)
                     {
@@ -3546,100 +3558,4 @@
             m_bundleLock.notifyAll();
         }
     }
-    
-    private static class PrivilegedActivatorCall implements 
-        java.security.PrivilegedExceptionAction
-    {
-        private static final int START = 1;
-        private static final int STOP = 2;
-        private int m_action;
-        private BundleActivator m_activator;
-        private BundleContext m_context;
-        
-        PrivilegedActivatorCall(int action, BundleActivator activator, BundleContext context)
-        {
-            m_action = action;
-            m_activator = activator;
-            m_context = context;
-        }
-        public Object run() throws Exception
-        {
-            switch (m_action)
-            {
-                case START:
-                    m_activator.start(m_context);
-                    break;
-                case STOP:
-                    m_activator.stop(m_context);
-                    break;
-                default:
-                    throw new IllegalStateException("Unknown activator action.");
-            }
-            
-            return null;
-        }
-    }
-    
-    /**
-     * This simple class is used to perform the privileged action of
-     * checking if a bundle has permission to import its packages.
-    **/
-    private class CheckImportsPrivileged implements java.security.PrivilegedExceptionAction
-    {
-        private URL m_url = null;
-        private BundleImpl m_bundle = null;
-
-        public CheckImportsPrivileged(URL url, BundleImpl bundle)
-        {
-            m_url = url;
-            m_bundle = bundle;
-        }
-
-        public Object run() throws Exception
-        {
-            // Get permission collection for code source; we cannot
-            // call AccessController.checkPermission() directly since
-            // the bundle's code is not on the access context yet because
-            // it has not started yet...we are simply resolving it to see
-            // if we can start it. We must check for import permission
-            // on the exports as well, since export implies import.
-            java.security.CodeSource cs = new java.security.CodeSource(m_url,
-                (java.security.cert.Certificate[]) null);
-            
-            java.security.PermissionCollection pc = 
-                java.security.Policy.getPolicy().getPermissions(cs);
-
-            R4Import[] imports =
-                m_bundle.getInfo().getCurrentModule().getDefinition().getImports();
-            
-            for (int i = 0;i < imports.length; i++)
-            {
-                PackagePermission perm = new PackagePermission(imports[i].getName(), 
-                    PackagePermission.IMPORT);
-                if (!pc.implies(perm))
-                {
-                   throw new java.security.AccessControlException(
-                       "PackagePermission.IMPORT denied for import: " + 
-                       imports[i].getName(), perm);
-                }
-            }
-            // Check export permission for all exports of the current module.
-            R4Export[] implicitImports =
-                m_bundle.getInfo().getCurrentModule().getDefinition().getExports();
-            
-            for (int i = 0;i < implicitImports.length; i++)
-            {
-                PackagePermission perm = new PackagePermission(
-                    implicitImports[i].getName(), PackagePermission.EXPORT);
-                if (!pc.implies(perm))
-                {
-                    throw new java.security.AccessControlException(
-                        "PackagePermission.EXPORT denied for implicit export: " + 
-                        implicitImports[i].getName(), perm);
-                }
-            }
-            
-            return null;
-        }
-    }
 }
diff --git a/framework/src/main/java/org/apache/felix/framework/SystemBundle.java b/framework/src/main/java/org/apache/felix/framework/SystemBundle.java
index fdfdf3c..e19f07e 100644
--- a/framework/src/main/java/org/apache/felix/framework/SystemBundle.java
+++ b/framework/src/main/java/org/apache/felix/framework/SystemBundle.java
@@ -23,6 +23,7 @@
 import org.apache.felix.framework.searchpolicy.R4Export;
 import org.apache.felix.framework.searchpolicy.R4Package;
 import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.SecureAction;
 import org.apache.felix.framework.util.StringMap;
 import org.apache.felix.moduleloader.IContentLoader;
 import org.osgi.framework.*;
@@ -34,12 +35,15 @@
     private Thread m_shutdownThread = null;
     private R4Export[] m_exports = null;
     private IContentLoader m_contentLoader = null;
+    private SecureAction m_secureAction = null;
 
-    protected SystemBundle(Felix felix, BundleInfo info, List activatorList)
-        throws BundleException
+    protected SystemBundle(Felix felix, BundleInfo info, List activatorList,
+        SecureAction secureAction) throws BundleException
     {
         super(felix, info);
 
+        m_secureAction = secureAction;
+
         // Create an activator list if necessary.
         if (activatorList == null)
         {
@@ -142,10 +146,13 @@
 
         getInfo().setState(Bundle.STARTING);
 
-        try {
+        try
+        {
             getInfo().setContext(new BundleContextImpl(getFelix(), this));
             getActivator().start(getInfo().getContext());
-        } catch (Throwable throwable) {
+        }
+        catch (Throwable throwable)
+        {
             throw new BundleException(
                 "Unable to start system bundle.", throwable);
         }
@@ -158,13 +165,13 @@
     public synchronized void stop() throws BundleException
     {
         Object sm = System.getSecurityManager();
-        
-        if(sm != null)
+
+        if (sm != null)
         {
-            ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                 AdminPermission.EXECUTE));
         }
-    
+
         // Spec says stop() on SystemBundle should return immediately and
         // shutdown framework on another thread.
         if (getFelix().getStatus() == Felix.RUNNING_STATUS)
@@ -191,23 +198,7 @@
                         ? false : embedded.equals("true");
                     if (!isEmbedded)
                     {
-                        if (System.getSecurityManager() != null)
-                        {
-                            java.security.AccessController.doPrivileged(
-                                new java.security.PrivilegedAction()
-                                {
-                                    public Object run()
-                                    {
-                                        System.exit(0);
-                                        
-                                        return null;
-                                    }
-                                });
-                        }
-                        else
-                        {
-                            System.exit(0);
-                        }
+                        m_secureAction.exit(0);
                     }
                 }
             };
@@ -243,10 +234,10 @@
     public synchronized void update(InputStream is) throws BundleException
     {
         Object sm = System.getSecurityManager();
-        
+
         if (sm != null)
         {
-            ((SecurityManager) sm).checkPermission(new AdminPermission(this, 
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                 AdminPermission.EXECUTE));
         }
 
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
index 9cd4e81..514fdc4 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
@@ -17,6 +17,7 @@
 package org.apache.felix.framework.cache;
 
 import java.io.*;
+import java.util.Collection;
 
 import org.apache.felix.framework.Logger;
 import org.apache.felix.framework.util.ObjectInputStreamX;
@@ -80,7 +81,7 @@
     private static final transient String ACTIVE_STATE = "active";
     private static final transient String INSTALLED_STATE = "installed";
     private static final transient String UNINSTALLED_STATE = "uninstalled";
-    
+
     private Logger m_logger = null;
     private long m_id = -1;
     private File m_archiveRootDir = null;
@@ -89,6 +90,7 @@
     private int m_persistentState = -1;
     private int m_startLevel = -1;
     private BundleRevision[] m_revisions = null;
+    private Collection m_trustedCaCerts = null;
 
     private long m_refreshCount = -1;
 
@@ -120,7 +122,8 @@
      * @throws Exception if any error occurs.
     **/
     public BundleArchive(
-        Logger logger, File archiveRootDir, long id, String location, InputStream is)    
+        Logger logger, File archiveRootDir, long id, String location, InputStream is,
+        Collection trustedCaCerts)
         throws Exception
     {
         m_logger = logger;
@@ -132,6 +135,7 @@
                 "Bundle ID cannot be less than or equal to zero.");
         }
         m_originalLocation = location;
+        m_trustedCaCerts = trustedCaCerts;
 
         // Save state.
         initialize();
@@ -152,11 +156,13 @@
      * @param id the bundle identifier associated with the archive.
      * @throws Exception if any error occurs.
     **/
-    public BundleArchive(Logger logger, File archiveRootDir)    
+    public BundleArchive(Logger logger, File archiveRootDir,
+        Collection trustedCaCerts)
         throws Exception
     {
         m_logger = logger;
         m_archiveRootDir = archiveRootDir;
+        m_trustedCaCerts = trustedCaCerts;
 
         // Add a revision for each one that already exists in the file
         // system. The file system might contain more than one revision
@@ -192,12 +198,12 @@
         }
 
         // Add the revision object for the most recent revision. We first try to read
-        // the location from the current revision - if that fails we likely have 
-        // an old bundle cache and read the location the old way. The next 
+        // the location from the current revision - if that fails we likely have
+        // an old bundle cache and read the location the old way. The next
         // revision will update the bundle cache.
         // TODO: FRAMEWORK - This try catch block can eventually be deleted when we decide to remove
         // support for the old way, then we only need the first call to revise().
-        try 
+        try
         {
             revise(getRevisionLocation(revisionCount - 1), null);
         }
@@ -638,7 +644,7 @@
         setCurrentLocation(location);
 
         setRevisionLocation(location, (m_revisions == null) ? 0 : m_revisions.length);
-        
+
         // Add new revision to revision array.
         if (m_revisions == null)
         {
@@ -672,47 +678,57 @@
         {
             return false;
         }
-        
+
         String location = getRevisionLocation(m_revisions.length - 2);
 
         // TODO: FRAMEWORK - This can eventually be deleted when we removed
         // support for the old way of doing things.
         setCurrentLocation(location);
-        
+
         try
         {
             m_revisions[m_revisions.length - 1].dispose();
-        } 
+        }
         catch(Exception ex)
         {
-           m_logger.log(Logger.LOG_ERROR, getClass().getName() + 
-               ": Unable to dispose latest revision", ex); 
+           m_logger.log(Logger.LOG_ERROR, getClass().getName() +
+               ": Unable to dispose latest revision", ex);
         }
 
-        File revisionDir = new File(m_archiveRootDir, REVISION_DIRECTORY + 
+        File revisionDir = new File(m_archiveRootDir, REVISION_DIRECTORY +
             getRefreshCount() + "." + (m_revisions.length - 1));
-        
+
         if (BundleCache.getSecureAction().fileExists(revisionDir))
         {
             BundleCache.deleteDirectoryTree(revisionDir);
         }
-        
+
         BundleRevision[] tmp = new BundleRevision[m_revisions.length - 1];
         System.arraycopy(m_revisions, 0, tmp, 0, m_revisions.length - 1);
-        
+
         return true;
     }
-    
+
+    public synchronized java.security.cert.Certificate[] getCertificates()
+    {
+        return m_revisions[m_revisions.length -1].getCertificates();
+    }
+
+    public synchronized String[] getDNChains()
+    {
+        return m_revisions[m_revisions.length -1].getDNChains();
+    }
+
     private synchronized String getRevisionLocation(int revision) throws Exception
-    {   
+    {
         InputStream is = null;
         BufferedReader br = null;
         try
         {
             is = BundleCache.getSecureAction().getFileInputStream(new File(
-                new File(m_archiveRootDir, REVISION_DIRECTORY + 
+                new File(m_archiveRootDir, REVISION_DIRECTORY +
                 getRefreshCount() + "." + revision), REVISION_LOCATION_FILE));
-            
+
             br = new BufferedReader(new InputStreamReader(is));
             return br.readLine();
         }
@@ -722,7 +738,7 @@
             if (is != null) is.close();
         }
     }
-    
+
     private synchronized void setRevisionLocation(String location, int revision) throws Exception
     {
         // Save current revision location.
@@ -732,7 +748,7 @@
         {
             os = BundleCache.getSecureAction()
                 .getFileOutputStream(new File(
-                    new File(m_archiveRootDir, REVISION_DIRECTORY + 
+                    new File(m_archiveRootDir, REVISION_DIRECTORY +
                     getRefreshCount() + "." + revision), REVISION_LOCATION_FILE));
             bw = new BufferedWriter(new OutputStreamWriter(os));
             bw.write(location, 0, location.length());
@@ -743,7 +759,7 @@
             if (os != null) os.close();
         }
     }
-    
+
     /**
      * <p>
      * This method removes all old revisions associated with the archive
@@ -785,10 +801,10 @@
         // Save the current revision location for use later when
         // we recreate the revision.
         String location = getRevisionLocation(count -1);
-        
+
         // Increment the refresh count.
         setRefreshCount(refreshCount + 1);
-        
+
         // Rename the current revision directory to be the zero revision
         // of the new refresh level.
         File currentDir = new File(m_archiveRootDir, REVISION_DIRECTORY + (refreshCount + 1) + ".0");
@@ -962,6 +978,8 @@
         File revisionRootDir = new File(m_archiveRootDir,
             REVISION_DIRECTORY + getRefreshCount() + "." + getRevisionCount());
 
+        BundleRevision result = null;
+
         try
         {
             // Check if the location string represents a reference URL.
@@ -986,22 +1004,22 @@
                 // flag set to true.
                 if (BundleCache.getSecureAction().isFileDirectory(file))
                 {
-                    return new DirectoryRevision(m_logger, revisionRootDir, location);
+                    result = new DirectoryRevision(m_logger, revisionRootDir, location);
                 }
                 else
                 {
-                    return new JarRevision(m_logger, revisionRootDir, location, true);
+                    result = new JarRevision(m_logger, revisionRootDir, location, true);
                 }
             }
             else if (location.startsWith(INPUTSTREAM_PROTOCOL))
             {
                 // Assume all input streams point to JAR files.
-                return new JarRevision(m_logger, revisionRootDir, location, false, is);
+                result = new JarRevision(m_logger, revisionRootDir, location, false, is);
             }
             else
             {
                 // Anything else is assumed to be a URL to a JAR file.
-                return new JarRevision(m_logger, revisionRootDir, location, false);
+                result = new JarRevision(m_logger, revisionRootDir, location, false);
             }
         }
         catch (Exception ex)
@@ -1019,6 +1037,10 @@
             }
             throw ex;
         }
+
+        result.setTrustedCaCerts(m_trustedCaCerts);
+
+        return result;
     }
 
     /**
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
index 7141bdc..edcf5bd 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
@@ -17,8 +17,7 @@
 package org.apache.felix.framework.cache;
 
 import java.io.*;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
 
 import org.apache.felix.framework.Logger;
 import org.apache.felix.framework.util.PropertyResolver;
@@ -84,14 +83,17 @@
     private Logger m_logger = null;
     private File m_profileDir = null;
     private BundleArchive[] m_archives = null;
+    private Collection m_trustedCaCerts = null;
 
     private static SecureAction m_secureAction = new SecureAction();
 
-    public BundleCache(PropertyResolver cfg, Logger logger)
+    public BundleCache(PropertyResolver cfg, Logger logger,
+        Collection trustedCaCerts)
         throws Exception
     {
         m_cfg = cfg;
         m_logger = logger;
+        m_trustedCaCerts = trustedCaCerts;
         initialize();
     }
 
@@ -143,7 +145,8 @@
         {
             // Create the archive and add it to the list of archives.
             BundleArchive ba =
-                new BundleArchive(m_logger, archiveRootDir, id, location, is);
+                new BundleArchive(m_logger, archiveRootDir, id, location, is,
+                m_trustedCaCerts);
             BundleArchive[] tmp = new BundleArchive[m_archives.length + 1];
             System.arraycopy(m_archives, 0, tmp, 0, m_archives.length);
             tmp[m_archives.length] = ba;
@@ -330,7 +333,7 @@
                 try
                 {
                     archiveList.add(
-                        new BundleArchive(m_logger, children[i]));
+                        new BundleArchive(m_logger, children[i], m_trustedCaCerts));
                 }
                 catch (Exception ex)
                 {
@@ -340,7 +343,7 @@
                 }
             }
         }
-        
+
         m_archives = (BundleArchive[])
             archiveList.toArray(new BundleArchive[archiveList.size()]);
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleRevision.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleRevision.java
index 7607e5e..55b47b4 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleRevision.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleRevision.java
@@ -17,7 +17,10 @@
 package org.apache.felix.framework.cache;
 
 import java.io.File;
-import java.util.Map;
+import java.io.InputStream;
+import java.security.cert.*;
+import java.util.*;
+import java.util.jar.*;
 
 import org.apache.felix.framework.Logger;
 import org.apache.felix.moduleloader.IContent;
@@ -43,6 +46,11 @@
     private Logger m_logger;
     private File m_revisionRootDir = null;
     private String m_location = null;
+    private Collection m_trustedCaCerts = null;
+    private X509Certificate[] m_certificates = null;
+    private String[] m_subjectDNChain = null;
+    private boolean m_certInitDone = (System.getSecurityManager() == null);
+    private boolean m_subjectDNInitDone = (System.getSecurityManager() == null);
 
     /**
      * <p>
@@ -68,6 +76,7 @@
      * @param revisionRootDir the root directory to be used by the revision
      *        subclass for storing any state.
      * @param location the location string associated with the revision.
+     * @param trustedCaCerts the trusted CA certificates if any.
      * @throws Exception if any errors occur.
     **/
     public BundleRevision(Logger logger, File revisionRootDir, String location)
@@ -163,4 +172,708 @@
      * @throws Exception if any error occurs.
     **/
     public abstract void dispose() throws Exception;
+
+    protected void setTrustedCaCerts(Collection trustedCaCerts)
+    {
+        m_trustedCaCerts = trustedCaCerts;
+    }
+
+    public X509Certificate[] getCertificates()
+    {
+        if (m_certInitDone)
+        {
+            return m_certificates;
+        }
+
+        if (m_trustedCaCerts == null)
+        {
+            return null;
+        }
+
+        try
+        {
+            m_certificates = getRevisionCertificates();
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+            // TODO: log this or something
+        }
+        finally
+        {
+            m_certInitDone = true;
+        }
+
+        return m_certificates;
+    }
+
+    protected abstract X509Certificate[] getRevisionCertificates() throws Exception;
+
+    public String[] getDNChains()
+    {
+        if (m_subjectDNInitDone)
+        {
+            return m_subjectDNChain;
+        }
+
+        try
+        {
+            X509Certificate[] certificates = getCertificates();
+
+            if (certificates == null)
+            {
+                return null;
+            }
+
+            List rootChains = new ArrayList();
+
+            getRootChains(certificates, rootChains);
+
+            List result = new ArrayList();
+
+            for (Iterator rootIter = rootChains.iterator();rootIter.hasNext();)
+            {
+                StringBuffer buffer = new StringBuffer();
+
+                List chain = (List) rootIter.next();
+
+                Iterator iter = chain.iterator();
+
+                X509Certificate current = (X509Certificate) iter.next();
+
+                try
+                {
+                    buffer.append(parseSubjectDN(current.getTBSCertificate()));
+
+                    while (iter.hasNext())
+                    {
+                        buffer.append(';');
+
+                        current = (X509Certificate) iter.next();
+
+                        buffer.append(parseSubjectDN(current.getTBSCertificate()));
+                    }
+
+                    result.add(buffer.toString());
+
+                }
+                catch (Exception ex)
+                {
+                    // something went wrong during parsing -
+                    // it might be that the cert contained an unsupported OID
+                    ex.printStackTrace();
+                    // TODO: log this or something
+                }
+            }
+
+            if (!result.isEmpty())
+            {
+                m_subjectDNChain = (String[]) result.toArray(new String[result.size()]);
+            }
+        }
+        finally
+        {
+            m_subjectDNInitDone = true;
+        }
+
+        return m_subjectDNChain;
+    }
+
+    protected X509Certificate[] getCertificatesForJar(JarFile bundle)
+        throws Exception
+    {
+        if (bundle.getManifest() == null)
+        {
+           return null;
+        }
+
+        List bundleEntries = new ArrayList();
+
+        Enumeration entries = bundle.entries();
+
+        while (entries.hasMoreElements())
+        {
+            JarEntry entry = (JarEntry) entries.nextElement();
+            bundleEntries.add(entry);
+            InputStream is = bundle.getInputStream(entry);
+            byte[] read = new byte[4096];
+            while (is.read(read) != -1)
+            {
+                // read the entry
+            }
+            is.close();
+        }
+        bundle.close();
+
+        List certificateChains = new ArrayList();
+
+        for (Iterator iter = bundleEntries.iterator();iter.hasNext();)
+        {
+            JarEntry entry = (JarEntry) iter.next();
+
+            if (entry.isDirectory() || entry.getName().startsWith("META-INF"))
+            {
+                continue;
+            }
+
+            Certificate[] certificates = entry.getCertificates();
+
+            if ((certificates == null) || (certificates.length == 0))
+            {
+                return null;
+            }
+
+            List chains = new ArrayList();
+
+            getRootChains(certificates, chains);
+
+            if (certificateChains.isEmpty())
+            {
+                certificateChains.addAll(chains);
+            }
+            else
+            {
+                for (Iterator iter2 = certificateChains.iterator();iter2.hasNext();)
+                {
+                    X509Certificate cert = (X509Certificate) ((List) iter2.next()).get(0);
+                    boolean found = false;
+                    for (Iterator iter3 = chains.iterator();iter3.hasNext();)
+                    {
+                        X509Certificate cert2 = (X509Certificate) ((List) iter3.next()).get(0);
+
+                        if (cert.getSubjectDN().equals(cert2.getSubjectDN()) && cert.equals(cert2))
+                        {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (!found)
+                    {
+                        iter2.remove();
+                    }
+                }
+            }
+
+            if (certificateChains.isEmpty())
+            {
+                return null;
+            }
+        }
+
+        List result = new ArrayList();
+
+        for (Iterator iter = certificateChains.iterator();iter.hasNext();)
+        {
+            result.addAll((List) iter.next());
+        }
+
+        return (X509Certificate[]) result.toArray(new X509Certificate[result.size()]);
+    }
+
+    protected void getRootChains(Certificate[] certificates, List chains)
+    {
+        List chain = new ArrayList();
+
+        for (int i = 0; i < certificates.length - 1; i++)
+        {
+            chain.add(certificates[i]);
+            if (!((X509Certificate)certificates[i + 1]).getSubjectDN().equals(
+                ((X509Certificate)certificates[i]).getIssuerDN()))
+            {
+
+                if (trusted((X509Certificate) certificates[i]))
+                {
+                    chains.add(chain);
+                }
+
+                chain = new ArrayList();
+            }
+        }
+        // The final entry in the certs array is always
+        // a "root" certificate
+        chain.add(certificates[certificates.length - 1]);
+
+        if (trusted((X509Certificate) certificates[certificates.length - 1]))
+        {
+            chains.add(chain);
+        }
+    }
+
+    // @return true if
+    // m_trustedCaCerts.contains(cert) || cert issued by any of m_trustedCaCerts
+    protected boolean trusted(X509Certificate cert)
+    {
+        if (m_trustedCaCerts == null)
+        {
+            return false;
+        }
+
+        // m_trustedCaCerts.contains(cert) ? return true
+        for (Iterator iter = m_trustedCaCerts.iterator();iter.hasNext();)
+        {
+            X509Certificate trustedCaCert = (X509Certificate) iter.next();
+
+            // If the cert has the same SubjectDN
+            // as a trusted CA, check whether
+            // the two certs are the same.
+            if (cert.getSubjectDN().equals(trustedCaCert.getSubjectDN()))
+            {
+                if (cert.equals(trustedCaCert))
+                {
+                    try
+                    {
+                        cert.checkValidity();
+                        trustedCaCert.checkValidity();
+                        return true;
+                    }
+                    catch (CertificateException ex)
+                    {
+                        System.err.println("WARNING: Invalid certificate [" + ex + "]");
+                    }
+                }
+            }
+        }
+
+        // cert issued by any of m_trustedCaCerts ? return true : return false
+        for (Iterator iter = m_trustedCaCerts.iterator();iter.hasNext();)
+        {
+            X509Certificate trustedCaCert = (X509Certificate) iter.next();
+
+            if (cert.getIssuerDN().equals(trustedCaCert.getSubjectDN()))
+            {
+                try
+                {
+                    cert.verify(trustedCaCert.getPublicKey());
+                    cert.checkValidity();
+                    trustedCaCert.checkValidity();
+                    return true;
+                }
+                catch (Exception ex)
+                {
+                    System.err.println("WARNING: Invalid certificate [" + ex + "]");
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /*
+     * This is deep magiK, bare with me. The problem is that we don't get
+     * access to the original subject dn in a certificate without resorting to
+     * sun.* classes or running on something > OSGi-minimum/jdk1.3. Furthermore,
+     * we need access to it because there is no other way to escape it properly.
+     * Note, this is due to missing of a public X500Name in OSGI-minimum/jdk1.3
+     * a.k.a foundation.
+     *
+     * The solution is to get the DER encoded TBS certificate bytes via the
+     * available java methods and parse-out the subject dn in canonical form by
+     * hand. This is possible without deploying a full-blown BER encoder/decoder
+     * due to java already having done all the cumbersome verification and
+     * normalization work.
+     *
+     * The following skips through the TBS certificate bytes until it reaches and
+     * subsequently parses the subject dn. If the below makes immediate sense to
+     * you - you either are a X509/X501/DER expert or quite possibly mad. In any
+     * case, please seek medical care immediately.
+     */
+    protected String parseSubjectDN(byte[] tbsCertEncoded) throws Exception
+    {
+        // init
+        tbs_buffer = tbsCertEncoded;
+        tbs_offset = 0;
+
+        try // this is a finally block that resets the tbs_buffer to null after we're done
+        {
+            // TBSCertificate  ::=  SEQUENCE  {
+            //    version         [0]  EXPLICIT Version DEFAULT v1,
+            //    serialNumber         CertificateSerialNumber,
+            //    signature            AlgorithmIdentifier,
+            //    issuer               Name,
+            //    validity             Validity,
+            //    subject              Name,
+            //
+            // WE CAN STOP!
+            //
+            //    subjectPublicKeyInfo SubjectPublicKeyInfo,
+            //    issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
+            //                         -- If present, version must be v2 or v3
+            //    subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
+            //                         -- If present, version must be v2 or v3
+            //    extensions      [3]  EXPLICIT Extensions OPTIONAL
+            //                         -- If present, version must be v3
+            //    }
+
+            next();
+            next();
+            // if a version is present skip it
+            if (tbs_tag == 0)
+            {
+                next();
+                tbs_offset += tbs_length;
+            }
+            tbs_offset += tbs_length;
+            // skip the serialNumber
+            next();
+            next();
+            tbs_offset += tbs_length;
+            // skip the signature
+            next();
+            tbs_offset += tbs_length;
+            // skip the issuer
+            // The issuer is a sequence of sets of issuer dns like the subject later on -
+            // we just skip it.
+            next();
+            int endOffset = tbs_offset + tbs_length;
+
+            int seqTagOffset = tbs_tagOffset;
+
+            // skip the sequence
+            while (endOffset > tbs_offset)
+            {
+                next();
+
+                int endOffset2 = tbs_offset + tbs_length;
+
+                int seqTagOffset2 = tbs_tagOffset;
+
+                // skip each set
+                while (endOffset2 > tbs_offset)
+                {
+                    next();
+                    next();
+                    tbs_offset += tbs_length;
+                    next();
+                    tbs_offset += tbs_length;
+                }
+
+                tbs_tagOffset = seqTagOffset2;
+            }
+
+            tbs_tagOffset = seqTagOffset;
+            // skip the validity which contains two dates to be skiped
+            next();
+            next();
+            tbs_offset += tbs_length;
+            next();
+            tbs_offset += tbs_length;
+            next();
+            // Now extract the subject dns and add them to attributes
+            List attributes = new ArrayList();
+
+            endOffset = tbs_offset + tbs_length;
+
+            seqTagOffset = tbs_tagOffset;
+
+            // for each set of rdns
+            while (endOffset > tbs_offset)
+            {
+                next();
+                int endOffset2 = tbs_offset + tbs_length;
+
+                // store tag offset
+                int seqTagOffset2 = tbs_tagOffset;
+
+                List rdn = new ArrayList();
+
+                // for each rdn in the set
+                while (endOffset2 > tbs_offset)
+                {
+                    next();
+                    next();
+                    tbs_offset += tbs_length;
+                    // parse the oid of the rdn
+                    int oidElement = 1;
+                    for (int i = 0; i < tbs_length; i++, ++oidElement)
+                    {
+                        while ((tbs_buffer[tbs_contentOffset + i] & 0x80) == 0x80)
+                        {
+                            i++;
+                        }
+                    }
+                    int[] oid = new int[oidElement];
+                    for (int id = 1, i = 0; id < oid.length; id++, i++)
+                    {
+                        int octet = tbs_buffer[tbs_contentOffset + i];
+                        oidElement = octet & 0x7F;
+                        while ((octet & 0x80) != 0)
+                        {
+                            i++;
+                            octet = tbs_buffer[tbs_contentOffset + i];
+                            oidElement = oidElement << 7 | (octet & 0x7f);
+                        }
+                        oid[id] = oidElement;
+                    }
+                    // The first OID is special
+                    if (oid[1] > 79)
+                    {
+                        oid[0] = 2;
+                        oid[1] = oid[1] - 80;
+                    }
+                    else
+                    {
+                        oid[0] = oid[1] / 40;
+                        oid[1] = oid[1] % 40;
+                    }
+                    // Now parse the value of the rdn
+                    next();
+                    String str = null;
+                    int tagTmp = tbs_tag;
+                    tbs_offset += tbs_length;
+                    switch(tagTmp)
+                    {
+                        case 30: // BMPSTRING
+                        case 22: // IA5STRING
+                        case 27: // GENERALSTRING
+                        case 19: // PRINTABLESTRING
+                        case 20: // TELETEXSTRING && T61STRING
+                        case 28: // UNIVERSALSTRING
+                            str = new String(tbs_buffer, tbs_contentOffset,
+                                tbs_length);
+                            break;
+                        case 12: // UTF8_STRING
+                            str = new String(tbs_buffer, tbs_contentOffset,
+                                tbs_length, "UTF-8");
+                            break;
+                        default: // OCTET
+                            byte[] encoded = new byte[tbs_offset - tbs_tagOffset];
+                            System.arraycopy(tbs_buffer, tbs_tagOffset, encoded,
+                                0, encoded.length);
+                            // Note, I'm not sure this is allowed by the spec
+                            // i.e., whether OCTET subjects are allowed at all
+                            // but it shouldn't harm doing it anyways (we just
+                            // convert it into a hex string prefixed with \#).
+                            str = toHexString(encoded);
+                            break;
+                    }
+
+                    rdn.add(new Object[]{mapOID(oid), makeCanonical(str)});
+                }
+
+                attributes.add(rdn);
+                tbs_tagOffset = seqTagOffset2;
+            }
+
+            tbs_tagOffset = seqTagOffset;
+
+            StringBuffer result = new StringBuffer();
+
+            for (int i = attributes.size() - 1; i >= 0; i--)
+            {
+                List rdn = (List) attributes.get(i);
+                Collections.sort(rdn, new Comparator()
+                {
+                    public int compare(Object obj1, Object obj2)
+                    {
+                        return ((String) ((Object[]) obj1)[0]).compareTo(
+                            ((String) ((Object[])obj2)[0]));
+                    }
+                });
+
+                for (Iterator iter = rdn.iterator();iter.hasNext();)
+                {
+                    Object[] att = (Object[]) iter.next();
+                    result.append((String) att[0]);
+                    result.append('=');
+                    result.append((String) att[1]);
+
+                    if (iter.hasNext())
+                    {
+                        // multi-valued RDN
+                        result.append('+');
+                    }
+                }
+
+                if (i != 0)
+                {
+                    result.append(',');
+                }
+            }
+
+            // the spec says:
+            // return result.toString().toUpperCase(Locale.US).toLowerCase(Locale.US);
+            // but that doesn't make no sense to me whatsoever hence,
+            return result.toString().toLowerCase(Locale.US);
+        }
+        finally
+        {
+            tbs_buffer = null;
+        }
+    }
+
+    private byte[] tbs_buffer = null;
+    private int tbs_offset  = 0;
+    private int tbs_tagOffset = 0;
+    private int tbs_tag = -1;
+    private int tbs_length = -1;
+    private int tbs_contentOffset = -1;
+
+    // Determine the type of the current sequence (tbs_tab), and the length and
+    // offset of it (tbs_length and tbs_tagOffset) plus increment the global
+    // offset (tbs_offset) accordingly. Note, we don't need to check for
+    // the indefinite length because this is supposed to be DER not BER (and
+    // we implicitly assume that java only gives us valid DER).
+    private void next()
+    {
+        tbs_tagOffset = tbs_offset;
+        tbs_tag = tbs_buffer[tbs_offset++] & 0xFF;
+        tbs_length = tbs_buffer[tbs_offset++] & 0xFF;
+        // There are two kinds of length forms - make sure we use the right one
+        if ((tbs_length & 0x80) != 0)
+        {
+            // its the long kind
+            int numOctets = tbs_length & 0x7F;
+            // hence, convert it
+            tbs_length = tbs_buffer[tbs_offset++] & 0xFF;
+            for (int i = 1; i < numOctets; i++)
+            {
+                int ch = tbs_buffer[tbs_offset++] & 0xFF;
+                tbs_length = (tbs_length << 8) + ch;
+            }
+        }
+        tbs_contentOffset = tbs_offset;
+    }
+
+    private String makeCanonical(String value)
+    {
+        int len = value.length();
+
+        if (len == 0)
+        {
+            return value;
+        }
+
+        StringBuffer result = new StringBuffer(len);
+
+        int i = 0;
+        if (value.charAt(0) == '#')
+        {
+            result.append('\\');
+            result.append('#');
+            i++;
+        }
+        for (;i < len; i++)
+        {
+            char c = value.charAt(i);
+
+            switch (c)
+            {
+                case ' ':
+                    int pos = result.length();
+                    // remove leading spaces and
+                    // remove all spaces except one in any sequence of spaces
+                    if ((pos == 0) || (result.charAt(pos - 1) == ' '))
+                    {
+                        break;
+                    }
+                    result.append(' ');
+                    break;
+                case '"':
+                case '\\':
+                case ',':
+                case '+':
+                case '<':
+                case '>':
+                case ';':
+                    result.append('\\');
+                default:
+                    result.append(c);
+            }
+        }
+
+        // count down until first none space to remove trailing spaces
+        i = result.length() - 1;
+        while ((i > -1) && (result.charAt(i) == ' '))
+        {
+            i--;
+        }
+
+        result.setLength(i + 1);
+
+        return result.toString();
+    }
+
+    private String toHexString(byte[] encoded)
+    {
+        StringBuffer result = new StringBuffer();
+
+        result.append('#');
+
+        for (int i = 0; i < encoded.length; i++)
+        {
+            int c = (encoded[i] >> 4) & 0x0F;
+            if (c < 10)
+            {
+                result.append((char) (c + 48));
+            }
+            else
+            {
+                result.append((char) (c + 87));
+            }
+
+            c = encoded[i] & 0x0F;
+
+            if (c < 10)
+            {
+                result.append((char) (c + 48));
+            }
+            else
+            {
+                result.append((char) (c + 87));
+            }
+        }
+
+        return result.toString();
+    }
+
+    private static final Map OID2NAME = new HashMap();
+
+    static
+    {
+        // see core-spec 2.3.5
+        OID2NAME.put("2.5.4.3", "cn");
+        OID2NAME.put("2.5.4.4", "sn");
+        OID2NAME.put("2.5.4.6", "c");
+        OID2NAME.put("2.5.4.7", "l");
+        OID2NAME.put("2.5.4.8", "st");
+        OID2NAME.put("2.5.4.10", "o");
+        OID2NAME.put("2.5.4.11", "ou");
+        OID2NAME.put("2.5.4.12", "title");
+        OID2NAME.put("2.5.4.42", "givenname");
+        OID2NAME.put("2.5.4.43", "initials");
+        OID2NAME.put("2.5.4.44", "generationqualifier");
+        OID2NAME.put("2.5.4.46", "dnqualifier");
+        OID2NAME.put("2.5.4.9", "street");
+        OID2NAME.put("0.9.2342.19200300.100.1.25", "dc");
+        OID2NAME.put("0.9.2342.19200300.100.1.1", "uid");
+        OID2NAME.put("1.2.840.113549.1.9.1", "emailaddress");
+        OID2NAME.put("2.5.4.5", "serialnumber");
+        // p.s.: it sucks that the spec doesn't list some of the oids used
+        // p.p.s: it sucks that the spec doesn't list the short form for all names
+        // In summary, there is a certain amount of guess-work involved but I'm
+        // fairly certain I've got it right.
+    }
+
+    // This just creates a string of the oid and looks for its name in the
+    // known names map OID2NAME. There might be faster implementations :-)
+    private String mapOID(int[] oid)
+    {
+        StringBuffer oidString = new StringBuffer();
+
+        oidString.append(oid[0]);
+        for (int i = 1;i < oid.length;i++)
+        {
+            oidString.append('.');
+            oidString.append(oid[i]);
+        }
+
+        String result = (String) OID2NAME.get(oidString.toString());
+
+        if (result == null)
+        {
+            throw new IllegalArgumentException("Unknown oid: " + oidString.toString());
+        }
+
+        return result;
+    }
 }
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
index 340be6f..e2ffde5 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
@@ -17,8 +17,9 @@
 package org.apache.felix.framework.cache;
 
 import java.io.*;
+import java.security.cert.X509Certificate;
 import java.util.Map;
-import java.util.jar.Manifest;
+import java.util.jar.*;
 
 import org.apache.felix.framework.Logger;
 import org.apache.felix.framework.util.*;
@@ -33,12 +34,13 @@
 **/
 class DirectoryRevision extends BundleRevision
 {
+    private static final transient String BUNDLE_JAR_FILE = "bundle.jar";
+
     private File m_refDir = null;
     private Map m_header = null;
 
     public DirectoryRevision(
-        Logger logger, File revisionRootDir, String location)    
-        throws Exception
+        Logger logger, File revisionRootDir, String location) throws Exception
     {
         super(logger, revisionRootDir, location);
         m_refDir = new File(location.substring(
@@ -109,23 +111,23 @@
         // class path to determine whether the bundle JAR file itself
         // is on the bundle's class path and then creating content
         // objects for everything on the class path.
-    
+
         // Get the bundle's manifest header.
         Map map = getManifestHeader();
-    
+
         // Find class path meta-data.
         String classPath = (map == null)
             ? null : (String) map.get(FelixConstants.BUNDLE_CLASSPATH);
-    
+
         // Parse the class path into strings.
         String[] classPathStrings = Util.parseDelimitedString(
             classPath, FelixConstants.CLASS_PATH_SEPARATOR);
-    
+
         if (classPathStrings == null)
         {
             classPathStrings = new String[0];
         }
-    
+
         // Create the bundles class path.
         IContent self = new DirectoryContent(m_refDir);
         IContent[] contentPath = new IContent[classPathStrings.length];
@@ -149,14 +151,14 @@
                 }
             }
         }
-    
+
         // If there is nothing on the class path, then include
         // "." by default, as per the spec.
         if (contentPath.length == 0)
         {
             contentPath = new IContent[] { self };
         }
-    
+
         return contentPath;
     }
 
@@ -172,4 +174,181 @@
         // of the revision directory, which will be automatically deleted
         // by the parent bundle archive.
     }
+
+    protected X509Certificate[] getRevisionCertificates() throws Exception
+    {
+        File tmp = new File(getRevisionRootDir(), BUNDLE_JAR_FILE);
+
+        if (BundleCache.getSecureAction().fileExists(tmp))
+        {
+            BundleCache.getSecureAction().deleteFile(tmp);
+        }
+
+        try
+        {
+            BundleCache.copyStreamToFile(new RevisionToInputStream(m_refDir),
+                tmp);
+
+            JarFile bundle = BundleCache.getSecureAction().openJAR(tmp);
+
+            return getCertificatesForJar(bundle);
+        }
+        finally
+        {
+            try
+            {
+                if (BundleCache.getSecureAction().fileExists(tmp))
+                {
+                    BundleCache.getSecureAction().deleteFile(tmp);
+                }
+            }
+            catch (Exception e)
+            {
+                // Not much we can do
+            }
+        }
+    }
+
+    private class RevisionToInputStream extends InputStream
+    {
+        class OutputStreamBuffer extends OutputStream
+        {
+            ByteArrayOutputStream outBuffer = null;
+
+            public void write(int b)
+            {
+                outBuffer.write(b);
+            }
+        }
+
+        private File m_revisionDir = null;
+        private File[] m_content = null;
+        private File m_manifest = null;
+        private ByteArrayInputStream m_buffer = null;
+        private int m_current = 0;
+        private OutputStreamBuffer m_outputBuffer = new OutputStreamBuffer();
+        private JarOutputStream m_output = null;
+
+        RevisionToInputStream(File revisionDir) throws IOException
+        {
+            m_revisionDir = revisionDir;
+
+            m_outputBuffer.outBuffer = new ByteArrayOutputStream();
+
+            m_manifest = new File(m_revisionDir, "META-INF/MANIFEST.MF");
+
+            m_output = new JarOutputStream(m_outputBuffer);
+
+            readNext(m_manifest, false);
+
+            m_content = listFilesRecursive(revisionDir);
+        }
+
+        private File[] listFilesRecursive(File dir)
+        {
+            File[] children = BundleCache.getSecureAction().listDirectory(dir);
+            File[] combined = children;
+            for (int i = 0; i < children.length; i++)
+            {
+                if (BundleCache.getSecureAction().isFileDirectory(children[i]))
+                {
+                    File[] grandchildren = listFilesRecursive(children[i]);
+                    if (grandchildren.length > 0)
+                    {
+                        File[] tmp = new File[combined.length + grandchildren.length];
+                        System.arraycopy(combined, 0, tmp, 0, combined.length);
+                        System.arraycopy(grandchildren, 0, tmp, combined.length, grandchildren.length);
+                        combined = tmp;
+                    }
+                }
+            }
+            return combined;
+        }
+
+        private boolean readNext(File file, boolean close) throws IOException
+        {
+            if (BundleCache.getSecureAction().isFileDirectory(file))
+            {
+                return false;
+            }
+
+            m_outputBuffer.outBuffer = new ByteArrayOutputStream();
+
+            InputStream in = null;
+            try
+            {
+                in = BundleCache.getSecureAction().getFileInputStream(file);
+
+                JarEntry entry = new JarEntry(
+                    file.getPath().substring(m_revisionDir.getPath().length() + 1));
+
+
+                m_output.putNextEntry(entry);
+
+                int c = -1;
+
+                while ((c = in.read()) != -1)
+                {
+                    m_output.write(c);
+                }
+            }
+            finally
+            {
+                if (in != null)
+                {
+                    in.close();
+                }
+            }
+
+            m_output.closeEntry();
+
+            m_output.flush();
+
+            if (close)
+            {
+                m_output.close();
+                m_output = null;
+            }
+
+            m_buffer = new ByteArrayInputStream(m_outputBuffer.outBuffer.toByteArray());
+
+            m_outputBuffer.outBuffer = null;
+
+            return true;
+        }
+
+        public int read() throws IOException
+        {
+            if ((m_output == null) && (m_buffer == null))
+            {
+                return -1;
+            }
+
+            if (m_buffer != null)
+            {
+                int result = m_buffer.read();
+
+                if (result == -1)
+                {
+                    m_buffer = null;
+                    return read();
+                }
+                else
+                {
+                    return result;
+                }
+            }
+
+            while ((m_current < m_content.length) &&
+                (m_content[m_current].equals(m_manifest) ||
+                !readNext(m_content[m_current], (m_current + 1) == m_content.length)))
+            {
+                m_current++;
+            }
+
+            m_current++;
+
+            return read();
+        }
+    }
 }
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java b/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
index faa24c1..23d6f9a 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
@@ -20,9 +20,9 @@
 import java.net.URL;
 import java.net.URLConnection;
 import java.security.PrivilegedActionException;
-import java.util.Map;
-import java.util.jar.JarFile;
-import java.util.jar.Manifest;
+import java.security.cert.X509Certificate;
+import java.util.*;
+import java.util.jar.*;
 import java.util.zip.ZipEntry;
 
 import org.apache.felix.framework.Logger;
@@ -50,7 +50,7 @@
     private Map m_header = null;
 
     public JarRevision(
-        Logger logger, File revisionRootDir, String location, boolean byReference)    
+        Logger logger, File revisionRootDir, String location, boolean byReference)
         throws Exception
     {
         this(logger, revisionRootDir, location, byReference, null);
@@ -58,7 +58,7 @@
 
     public JarRevision(
         Logger logger, File revisionRootDir, String location,
-        boolean byReference, InputStream is)    
+        boolean byReference, InputStream is)
         throws Exception
     {
         super(logger, revisionRootDir, location);
@@ -168,7 +168,7 @@
                     }
                 }
             }
-    
+
             // If there is nothing on the class path, then include
             // "." by default, as per the spec.
             if (contentPath.length == 0)
@@ -272,11 +272,11 @@
             {
                 if (is == null)
                 {
-                    // Do it the manual way to have a chance to 
+                    // Do it the manual way to have a chance to
                     // set request properties such as proxy auth.
                     URL url = new URL(getLocation());
-                    URLConnection conn = url.openConnection(); 
-        
+                    URLConnection conn = url.openConnection();
+
                     // Support for http proxy authentication.
                     String auth = BundleCache.getSecureAction()
                         .getSystemProperty("http.proxyAuth", null);
@@ -292,7 +292,7 @@
                     }
                     is = BundleCache.getSecureAction().getURLConnectionInputStream(conn);
                 }
-    
+
                 // Save the bundle jar file.
                 BundleCache.copyStreamToFile(is, m_bundleFile);
             }
@@ -421,7 +421,7 @@
                             throw new IOException("Unable to create embedded JAR directory.");
                         }
                     }
-    
+
                     // Extract embedded JAR into its directory.
                     is = new BufferedInputStream(bundleJar.getInputStream(ze), BundleCache.BUFSIZE);
                     if (is == null)
@@ -439,4 +439,9 @@
             }
         }
     }
+
+    protected X509Certificate[] getRevisionCertificates() throws Exception
+    {
+        return getCertificatesForJar(BundleCache.getSecureAction().openJAR(m_bundleFile, true));
+    }
 }
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/SystemBundleArchive.java b/framework/src/main/java/org/apache/felix/framework/cache/SystemBundleArchive.java
index 5a53a2b..99bd5b0 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/SystemBundleArchive.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/SystemBundleArchive.java
@@ -18,6 +18,7 @@
 
 import java.io.File;
 import java.io.InputStream;
+import java.security.cert.X509Certificate;
 import java.util.Map;
 
 import org.apache.felix.framework.util.FelixConstants;
@@ -65,6 +66,11 @@
             public void dispose() throws Exception
             {
             }
+
+            protected X509Certificate[] getRevisionCertificates()
+            {
+                return null;
+            }
         };
     }
 
@@ -148,7 +154,7 @@
     {
         return m_headerMap;
     }
-    
+
     public void setManifestHeader(Map headerMap)
     {
         m_headerMap = headerMap;