Add a way to get the cached bundle location for a bundle and remove support for signed bundles from the core (will be reintroduced as an extension bundle soon) FELIX-22.

git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@506753 13f79535-47bb-0310-9956-ffa450edef68
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 a1e0718..bbc08dc 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -19,7 +19,6 @@
 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;
@@ -94,7 +93,6 @@
     private int m_startLevel = -1;
     private long m_lastModified = -1;
     private BundleRevision[] m_revisions = null;
-    private Collection m_trustedCaCerts = null;
 
     private long m_refreshCount = -1;
 
@@ -125,10 +123,8 @@
      * @param is input stream from which to read the bundle content.
      * @throws Exception if any error occurs.
     **/
-    public BundleArchive(
-        Logger logger, File archiveRootDir, long id, String location, InputStream is,
-        Collection trustedCaCerts)
-        throws Exception
+    public BundleArchive(Logger logger, File archiveRootDir, long id,
+        String location, InputStream is) throws Exception
     {
         m_logger = logger;
         m_archiveRootDir = archiveRootDir;
@@ -139,7 +135,6 @@
                 "Bundle ID cannot be less than or equal to zero.");
         }
         m_originalLocation = location;
-        m_trustedCaCerts = trustedCaCerts;
 
         // Save state.
         initialize();
@@ -160,13 +155,11 @@
      * @param id the bundle identifier associated with the archive.
      * @throws Exception if any error occurs.
     **/
-    public BundleArchive(Logger logger, File archiveRootDir,
-        Collection trustedCaCerts)
+    public BundleArchive(Logger logger, File archiveRootDir)
         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
@@ -479,7 +472,7 @@
         {
             return m_lastModified;
         }
- 
+
         // Get bundle last modification time file.
         File lastModFile = new File(m_archiveRootDir, BUNDLE_LASTMODIFIED_FILE);
 
@@ -514,7 +507,7 @@
      * @param lastModified The time of the last modification to set for
      *      this archive. According to the OSGi specification this time is
      *      set each time a bundle is installed, updated or uninstalled.
-     *      
+     *
      * @throws Exception if any error occurs.
     **/
     public synchronized void setLastModified(long lastModified) throws Exception
@@ -773,16 +766,6 @@
         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;
@@ -1102,8 +1085,6 @@
             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 8a6bdea..b48a070 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -85,17 +85,14 @@
     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,
-        Collection trustedCaCerts)
+    public BundleCache(PropertyResolver cfg, Logger logger)
         throws Exception
     {
         m_cfg = cfg;
         m_logger = logger;
-        m_trustedCaCerts = trustedCaCerts;
         initialize();
     }
 
@@ -147,8 +144,7 @@
         {
             // Create the archive and add it to the list of archives.
             BundleArchive ba =
-                new BundleArchive(m_logger, archiveRootDir, id, location, is,
-                m_trustedCaCerts);
+                new BundleArchive(m_logger, archiveRootDir, id, location, is);
             BundleArchive[] tmp = new BundleArchive[m_archives.length + 1];
             System.arraycopy(m_archives, 0, tmp, 0, m_archives.length);
             tmp[m_archives.length] = ba;
@@ -334,8 +330,7 @@
                 // Recreate the bundle archive.
                 try
                 {
-                    archiveList.add(
-                        new BundleArchive(m_logger, children[i], m_trustedCaCerts));
+                    archiveList.add(new BundleArchive(m_logger, children[i]));
                 }
                 catch (Exception ex)
                 {
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 cb3a9e7..608592b 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -48,11 +48,6 @@
     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>
@@ -175,707 +170,10 @@
     **/
     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.
+    /**
+     * Returns the url of the cached bundle if possible.
      *
-     * 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.
+     * @return the url of the cached bundle as a string or null if not possible.
      */
-    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;
-    }
+    public abstract String getCachedBundleURL();
 }
\ 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 996e535..a686feb 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -19,6 +19,7 @@
 package org.apache.felix.framework.cache;
 
 import java.io.*;
+import java.net.MalformedURLException;
 import java.security.cert.X509Certificate;
 import java.util.*;
 import java.util.jar.*;
@@ -183,180 +184,16 @@
         // by the parent bundle archive.
     }
 
-    protected X509Certificate[] getRevisionCertificates() throws Exception
+    public String getCachedBundleURL()
     {
-        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);
+            return m_refDir.toURL().toString();
         }
-        finally
+        catch (MalformedURLException ex)
         {
-            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();
+            // This should never happen.
+            return null;
         }
     }
 }
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 efc2bb3..c7306af 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -19,8 +19,7 @@
 package org.apache.felix.framework.cache;
 
 import java.io.*;
-import java.net.URL;
-import java.net.URLConnection;
+import java.net.*;
 import java.security.PrivilegedActionException;
 import java.security.cert.X509Certificate;
 import java.util.*;
@@ -253,6 +252,19 @@
         // by the parent bundle archive.
     }
 
+    public String getCachedBundleURL()
+    {
+        try
+        {
+            return m_bundleFile.toURL().toString();
+        }
+        catch (MalformedURLException ex)
+        {
+            // This should never happen.
+            return null;
+        }
+    }
+
     //
     // Private methods.
     //
@@ -449,9 +461,4 @@
             }
         }
     }
-
-    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 9441340..d3ebc81 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -69,6 +69,11 @@
             {
             }
 
+            public String getCachedBundleURL()
+            {
+                return null;
+            }
+
             protected X509Certificate[] getRevisionCertificates()
             {
                 return null;
diff --git a/framework/src/main/java/org/osgi/framework/AdminPermission.java b/framework/src/main/java/org/osgi/framework/AdminPermission.java
index 08bd714..e03a6e8 100644
--- a/framework/src/main/java/org/osgi/framework/AdminPermission.java
+++ b/framework/src/main/java/org/osgi/framework/AdminPermission.java
@@ -18,11 +18,12 @@
  */
 package org.osgi.framework;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.security.*;
 import java.util.*;
 
 import org.apache.felix.framework.FilterImpl;
-import org.apache.felix.framework.SignerMatcher;
 
 /**
  * <p>
@@ -32,17 +33,17 @@
 **/
 public final class AdminPermission extends BasicPermission
 {
-	static final long serialVersionUID = 307051004521261705L;
+    static final long serialVersionUID = 307051004521261705L;
 
-	public static final String CLASS = "class";
-	public static final String EXECUTE = "execute";
-	public static final String EXTENSIONLIFECYCLE = "extensionLifecycle";
-	public static final String LIFECYCLE = "lifecycle";
-	public static final String LISTENER = "listener";
-	public static final String METADATA = "metadata";
-	public static final String RESOLVE = "resolve";
-	public static final String RESOURCE = "resource";
-	public static final String STARTLEVEL = "startlevel";
+    public static final String CLASS = "class";
+    public static final String EXECUTE = "execute";
+    public static final String EXTENSIONLIFECYCLE = "extensionLifecycle";
+    public static final String LIFECYCLE = "lifecycle";
+    public static final String LISTENER = "listener";
+    public static final String METADATA = "metadata";
+    public static final String RESOLVE = "resolve";
+    public static final String RESOURCE = "resource";
+    public static final String STARTLEVEL = "startlevel";
 
     private static final int CLASS_MASK = 1;
     private static final int EXECUTE_MASK = 2;
@@ -76,7 +77,7 @@
 	public AdminPermission()
     {
 		this("*", ALL_MASK);
-	}
+    }
 
     // This constructor is only used when checking a granted admin permission.
     public AdminPermission(Bundle bundle, String actions)
@@ -101,43 +102,43 @@
         m_actionMask = actionMask;
     }
 
-	public boolean equals(Object obj)
+    public boolean equals(Object obj)
     {
-		if (obj == this)
+	if (obj == this)
         {
-			return true;
-		}
-
-		if (!(obj instanceof AdminPermission))
-        {
-			return false;
-		}
-
-		AdminPermission p = (AdminPermission) obj;
-
-		return getName().equals(p.getName()) && (m_actionMask == p.m_actionMask);
+	    return true;
 	}
 
-	public int hashCode()
-    {
-		return getName().hashCode() ^ getActions().hashCode();
+	if (!(obj instanceof AdminPermission))
+        {
+	    return false;
 	}
 
-	public String getActions()
+	AdminPermission p = (AdminPermission) obj;
+
+	return getName().equals(p.getName()) && (m_actionMask == p.m_actionMask);
+    }
+
+    public int hashCode()
+    {
+	return getName().hashCode() ^ getActions().hashCode();
+    }
+
+    public String getActions()
     {
         if (m_actions == null)
         {
             m_actions = createActionString(m_actionMask);
         }
-		return m_actions;
-	}
+	return m_actions;
+    }
 
-	public boolean implies(Permission p)
+    public boolean implies(Permission p)
     {
-		if (!(p instanceof AdminPermission))
+	if (!(p instanceof AdminPermission))
         {
-			return false;
-		}
+	    return false;
+	}
 
         AdminPermission admin = (AdminPermission) p;
 
@@ -185,12 +186,12 @@
         }
 
         return m_filterImpl.match(admin.getBundleDictionary());
-	}
+    }
 
-	public PermissionCollection newPermissionCollection()
+    public PermissionCollection newPermissionCollection()
     {
-		return new AdminPermissionCollection();
-	}
+	return new AdminPermissionCollection();
+    }
 
     private Dictionary getBundleDictionary()
     {
@@ -213,6 +214,8 @@
                     public Object run()
                     {
                         m_bundleDict.put("location", m_bundle.getLocation());
+                        
+                        createSigner(m_bundle, m_bundleDict);
                         return null;
                     }
                 });
@@ -220,13 +223,34 @@
             else
             {
                 m_bundleDict.put("location", m_bundle.getLocation());
+                createSigner(m_bundle, m_bundleDict);
             }
-
-            m_bundleDict.put("signer", new SignerMatcher(m_bundle));
         }
         return m_bundleDict;
     }
 
+    private static void createSigner(Bundle bundle, Dictionary dict)
+    {
+        try
+        {
+            Method method = bundle.getClass().getDeclaredMethod(
+                "getSignerMatcher", null);
+            method.setAccessible(true);
+            
+            Object signer = method.invoke(bundle, null);
+            
+            if (signer != null)
+            {
+                dict.put("signer", signer);
+            }
+        }
+        catch (Exception ex)
+        {
+// TODO: log this or something
+            ex.printStackTrace();
+        }
+    }
+
     private static int parseActions(String actions)
     {
         if (actions == null)
@@ -365,8 +389,8 @@
 
 final class AdminPermissionCollection extends PermissionCollection
 {
-	private static final long serialVersionUID = 3747361397420496672L;
-	private HashMap m_map = new HashMap();
+    private static final long serialVersionUID = 3747361397420496672L;
+    private HashMap m_map = new HashMap();
 
     public void add(Permission permission)
     {