Author: jawi
Date: Thu Jan 14 16:20:31 2016
New Revision: 1724647

URL: http://svn.apache.org/viewvc?rev=1724647&view=rev
Log:
FELIX-518 - add unit test for signed DPs:

- created utility to create signed deployment packages for use in itests;
- verify that signed DPs are correctly installed;
- some small cleanups and typos fixed.


Added:
    
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/DPSignerTest.java
   (with props)
    
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/CertificateUtil.java
   (with props)
    
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/DPSigner.java
   (with props)
Modified:
    
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/AbstractDeploymentPackage.java
    
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ContentCopyingJarInputStream.java
    
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentAdminImpl.java
    
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentPackageManifest.java
    
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/NonCloseableStream.java
    felix/trunk/deploymentadmin/itest/pom.xml
    
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/BaseIntegrationTest.java
    
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/InstallDeploymentPackageTest.java
    
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/ArtifactData.java
    
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/DeploymentPackageBuilder.java

Modified: 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/AbstractDeploymentPackage.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/AbstractDeploymentPackage.java?rev=1724647&r1=1724646&r2=1724647&view=diff
==============================================================================
--- 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/AbstractDeploymentPackage.java
 (original)
+++ 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/AbstractDeploymentPackage.java
 Thu Jan 14 16:20:31 2016
@@ -511,6 +511,6 @@ public abstract class AbstractDeployment
      */
     protected boolean isMetaInfFile(String name) {
         name = name.toUpperCase(Locale.US);
-        return name.startsWith("META-INF/") && (name.endsWith("/INDEX.LIST") 
|| name.endsWith(".SF") || name.endsWith(".DSA") || name.endsWith(".RS"));
+        return name.startsWith("META-INF/") && (name.endsWith("/INDEX.LIST") 
|| name.endsWith(".SF") || name.endsWith(".DSA") || name.endsWith(".RSA") || 
name.endsWith(".EC"));
     }
 }

Modified: 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ContentCopyingJarInputStream.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ContentCopyingJarInputStream.java?rev=1724647&r1=1724646&r2=1724647&view=diff
==============================================================================
--- 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ContentCopyingJarInputStream.java
 (original)
+++ 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ContentCopyingJarInputStream.java
 Thu Jan 14 16:20:31 2016
@@ -18,7 +18,8 @@
  */
 package org.apache.felix.deploymentadmin;
 
-import java.io.Closeable;
+import static org.apache.felix.deploymentadmin.Utils.closeSilently;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FileWriter;
@@ -53,7 +54,7 @@ class ContentCopyingJarInputStream exten
     private OutputStream m_entryOS;
 
     public ContentCopyingJarInputStream(InputStream in, File indexFile, File 
contentDir) throws IOException {
-        super(in);
+        super(in, true /* verify */);
 
         m_contentDir = contentDir;
 
@@ -122,17 +123,6 @@ class ContentCopyingJarInputStream exten
         m_indexFileWriter = null;
     }
 
-    private void closeSilently(Closeable resource) {
-        try {
-            if (resource != null) {
-                resource.close();
-            }
-        }
-        catch (IOException e) {
-            // Ignore, not much we can do about this...
-        }
-    }
-
     /**
      * Creates a verbatim copy of the manifest, when it is read from the 
original JAR.
      */

Modified: 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentAdminImpl.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentAdminImpl.java?rev=1724647&r1=1724646&r2=1724647&view=diff
==============================================================================
--- 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentAdminImpl.java
 (original)
+++ 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentAdminImpl.java
 Thu Jan 14 16:20:31 2016
@@ -181,6 +181,8 @@ public class DeploymentAdminImpl impleme
                 jarInput = new ContentCopyingJarInputStream(sourceInput, 
tempIndex, tempContents);
 
                 if (jarInput.getManifest() == null) {
+                    Utils.closeSilently(jarInput);
+                    
                     m_log.log(LogService.LOG_ERROR, "Stream does not contain a 
valid deployment package: missing manifest!");
                     throw new DeploymentException(CODE_MISSING_HEADER, "No 
manifest present in deployment package!");
                 }

Modified: 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentPackageManifest.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentPackageManifest.java?rev=1724647&r1=1724646&r2=1724647&view=diff
==============================================================================
--- 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentPackageManifest.java
 (original)
+++ 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentPackageManifest.java
 Thu Jan 14 16:20:31 2016
@@ -33,10 +33,8 @@ import org.osgi.service.deploymentadmin.
  * deployment package manifest and can interpret the various manifest entries 
and headers the OSGi specification defines.
  */
 public class DeploymentPackageManifest implements Constants {
-
     private final Manifest m_manifest;
     private final Version m_version;
-
     private final List m_bundleInfos = new ArrayList();
     private final List m_resourceInfos = new ArrayList();
     private final String m_symbolicName;

Modified: 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/NonCloseableStream.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/NonCloseableStream.java?rev=1724647&r1=1724646&r2=1724647&view=diff
==============================================================================
--- 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/NonCloseableStream.java
 (original)
+++ 
felix/trunk/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/NonCloseableStream.java
 Thu Jan 14 16:20:31 2016
@@ -28,75 +28,68 @@ import java.io.InputStream;
  * 
  */
 public class NonCloseableStream extends InputStream {
-
     private final InputStream m_input;
-    private boolean m_closed;
+    private volatile boolean m_closed;
 
-    public NonCloseableStream(InputStream m_input) {
-        this.m_input = m_input;
+    public NonCloseableStream(InputStream input) {
+        m_input = input;
     }
 
-    // stream should not be actually closed, subsequent calls to read methods 
will throw an exception though
+    public int available() throws IOException {
+        return m_input.available();
+    }
 
     public void close() throws IOException {
-        if (m_closed) {
-            throw new IOException("Unable to read, stream is closed.");
-        }
+        // stream should not be actually closed, subsequent calls to read 
methods will throw an exception though
+        assertOpen();
         m_closed = true;
     }
 
-    public int read() throws IOException {
-        return m_input.read();
+    public boolean equals(Object obj) {
+        return m_input.equals(obj);
     }
 
-    public int read(byte[] b, int off, int len) throws IOException {
-        if (m_closed) {
-            throw new IOException("Unable to read, stream is closed.");
-        }
-        return m_input.read(b, off, len);
+    public int hashCode() {
+        return m_input.hashCode();
     }
 
-    public int read(byte[] b) throws IOException {
-        if (m_closed) {
-            throw new IOException("Unable to read, stream is closed.");
-        }
-        return m_input.read(b);
+    public void mark(int readlimit) {
+        // do nothing
     }
 
-    // No mark & reset support
-
     public boolean markSupported() {
         return false;
     }
 
-    public void mark(int readlimit) {
-        // do nothing
+    public int read() throws IOException {
+        return m_input.read();
     }
 
-    public void reset() throws IOException {
-        throw new IOException("Mark and reset are not available on this type 
of stream.");
+    public int read(byte[] b) throws IOException {
+        assertOpen();
+        return m_input.read(b);
     }
 
-    // Unaffected methods
-
-    public int available() throws IOException {
-        return m_input.available();
+    public int read(byte[] b, int off, int len) throws IOException {
+        assertOpen();
+        return m_input.read(b, off, len);
     }
 
-    public int hashCode() {
-        return m_input.hashCode();
+    public void reset() throws IOException {
+        throw new IOException("Mark and reset are not available on this type 
of stream.");
     }
 
     public long skip(long n) throws IOException {
         return m_input.skip(n);
     }
 
-    public boolean equals(Object obj) {
-        return m_input.equals(obj);
-    }
-
     public String toString() {
         return m_input.toString();
     }
 
+    private void assertOpen() throws IOException {
+        if (m_closed) {
+            throw new IOException("Unable to read, stream is closed.");
+        }
+    }
 }

Modified: felix/trunk/deploymentadmin/itest/pom.xml
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/itest/pom.xml?rev=1724647&r1=1724646&r2=1724647&view=diff
==============================================================================
--- felix/trunk/deploymentadmin/itest/pom.xml (original)
+++ felix/trunk/deploymentadmin/itest/pom.xml Thu Jan 14 16:20:31 2016
@@ -117,6 +117,18 @@
                </dependency>
 
                <dependency>
+                       <groupId>org.bouncycastle</groupId>
+                       <artifactId>bcprov-jdk15on</artifactId>
+                       <version>1.54</version>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.bouncycastle</groupId>
+                       <artifactId>bcpkix-jdk15on</artifactId>
+                       <version>1.54</version>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
                        <groupId>ch.qos.logback</groupId>
                        <artifactId>logback-core</artifactId>
                        <version>1.1.3</version>
@@ -146,14 +158,6 @@
                        </plugin>
                        <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
-                               <artifactId>maven-compiler-plugin</artifactId>
-                               <configuration>
-                                       <target>1.6</target>
-                                       <source>1.6</source>
-                               </configuration>
-                       </plugin>
-                       <plugin>
-                               <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
                                        <source>1.7</source>

Modified: 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/BaseIntegrationTest.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/BaseIntegrationTest.java?rev=1724647&r1=1724646&r2=1724647&view=diff
==============================================================================
--- 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/BaseIntegrationTest.java
 (original)
+++ 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/BaseIntegrationTest.java
 Thu Jan 14 16:20:31 2016
@@ -25,6 +25,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.security.Security;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -38,6 +39,7 @@ import javax.inject.Inject;
 import junit.framework.TestCase;
 
 import org.apache.felix.deploymentadmin.itest.util.DeploymentPackageBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.junit.After;
 import org.junit.Before;
 import org.ops4j.pax.exam.Configuration;
@@ -74,24 +76,23 @@ public abstract class BaseIntegrationTes
     protected volatile AtomicInteger m_gate = new AtomicInteger(0);
     protected volatile String m_testBundleBasePath;
     protected volatile Map<String, List<Version>> m_initialBundles;
-    
-    private int cnt = 0;        
-    
+
+    private int cnt = 0;
+
     @Configuration
     public Option[] config() throws Exception {
-        return options(
-            bootDelegationPackage("sun.*"),
-            
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("ERROR"),
+        return options(bootDelegationPackage("sun.*"), 
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("ERROR"),
 
-            mavenBundle("org.apache.felix", 
"org.apache.felix.metatype").versionAsInProject(),
+            mavenBundle("org.apache.felix", 
"org.apache.felix.metatype").versionAsInProject(), 
             mavenBundle("org.apache.felix", 
"org.apache.felix.dependencymanager").versionAsInProject(),
-            mavenBundle("org.apache.felix", 
"org.apache.felix.deploymentadmin").versionAsInProject(),
+            mavenBundle("org.apache.felix", 
"org.apache.felix.deploymentadmin").versionAsInProject(), 
             mavenBundle("org.apache.felix", 
"org.apache.felix.eventadmin").versionAsInProject(),
             mavenBundle("org.apache.felix", 
"org.apache.felix.configadmin").versionAsInProject(),
             mavenBundle("commons-codec", "commons-codec").versionAsInProject(),
-            
-            junitBundles()
-        );
+            mavenBundle("org.bouncycastle", 
"bcprov-jdk15on").versionAsInProject(),
+            mavenBundle("org.bouncycastle", 
"bcpkix-jdk15on").versionAsInProject(),
+
+            junitBundles());
     }
 
     @Before
@@ -110,9 +111,9 @@ public abstract class BaseIntegrationTes
                 }
             }
         });
-        
+
         m_initialBundles = new HashMap<String, List<Version>>();
-        
+
         for (Bundle bundle : m_context.getBundles()) {
             List<Version> versions = 
m_initialBundles.get(bundle.getSymbolicName());
             if (versions == null) {
@@ -121,12 +122,16 @@ public abstract class BaseIntegrationTes
             }
             versions.add(bundle.getVersion());
         }
+
+        Security.addProvider(new BouncyCastleProvider());
     }
-    
+
     @After
     public void tearDown() throws Exception {
         System.setProperty("rp1", "");
         System.setProperty("bundle3", "");
+
+        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
     }
 
     protected void assertBundleExists(String symbolicName, String version) {
@@ -168,24 +173,26 @@ public abstract class BaseIntegrationTes
         }
         return result;
     }
-    
+
     protected final DeploymentPackage 
installDeploymentPackage(DeploymentPackageBuilder dpBuilder) throws Exception {
         return installDeploymentPackage(dpBuilder.generate());
     }
-    
+
     protected final DeploymentPackage installDeploymentPackage(InputStream is) 
throws Exception {
         try {
             return m_deploymentAdmin.installDeploymentPackage(is);
-        } finally {
+        }
+        finally {
             try {
                 is.close();
-            } catch (IOException e) {
+            }
+            catch (IOException e) {
                 // Nothing we can do about this, but log it...
                 e.printStackTrace();
             }
         }
     }
-    
+
     protected final int countDeploymentPackages() {
         return m_deploymentAdmin.listDeploymentPackages().length;
     }
@@ -193,17 +200,17 @@ public abstract class BaseIntegrationTes
     protected DeploymentPackageBuilder 
createNewDeploymentPackageBuilder(String version) {
         return createDeploymentPackageBuilder(String.format("itest%d", ++cnt), 
version);
     }
-    
+
     protected DeploymentPackageBuilder createDeploymentPackageBuilder(String 
symName, String version) {
         return DeploymentPackageBuilder.create(symName, version);
     }
-    
+
     protected Map<String, List<Version>> getCurrentBundles() {
         Map<String, List<Version>> bundles = new HashMap<String, 
List<Version>>();
         for (Bundle bundle : m_context.getBundles()) {
             String symbolicName = bundle.getSymbolicName();
             Version version = bundle.getVersion();
-            
+
             // Is is not part of any of the initially provisioned bundles?
             List<Version> versions = m_initialBundles.get(symbolicName);
             if ((versions == null) || !versions.contains(version)) {
@@ -217,7 +224,7 @@ public abstract class BaseIntegrationTes
         }
         return bundles;
     }
-    
+
     protected String getSymbolicName(String baseName) {
         return "testbundles.".concat(baseName);
     }
@@ -241,10 +248,11 @@ public abstract class BaseIntegrationTes
     }
 
     protected URL getTestBundleURL(String baseName) throws 
MalformedURLException {
-       return getTestBundleURL(baseName, "1.0.0");
+        return getTestBundleURL(baseName, "1.0.0");
     }
+
     protected URL getTestBundleURL(String baseName, String version) throws 
MalformedURLException {
-       return getTestBundleURL(baseName, baseName, version);
+        return getTestBundleURL(baseName, baseName, version);
     }
 
     protected URL getTestBundleURL(String artifactName, String baseName, 
String version) throws MalformedURLException {
@@ -253,7 +261,7 @@ public abstract class BaseIntegrationTes
         assertTrue("No such bundle: " + f, f.exists() && f.isFile());
         return f.toURI().toURL();
     }
-    
+
     protected boolean isBundleActive(Bundle bundle) {
         return isBundleInState(bundle, Bundle.ACTIVE);
     }
@@ -280,7 +288,7 @@ public abstract class BaseIntegrationTes
     protected boolean isBundleRemoved(String symbolicName, String version) {
         return isBundleRemoved(symbolicName, new Version(version));
     }
-    
+
     protected boolean isBundleRemoved(String symbolicName, Version version) {
         Map<String, List<Version>> bundles = getCurrentBundles();
 
@@ -297,13 +305,13 @@ public abstract class BaseIntegrationTes
 
         FrameworkWiring frameworkWiring = 
systemBundle.adapt(FrameworkWiring.class);
         frameworkWiring.resolveBundles(Arrays.asList(bundles));
-        
+
         for (Bundle bundle : bundles) {
             if ((bundle.getState() & Bundle.RESOLVED) == 0) {
                 return false;
             }
         }
-        
+
         return true;
     }
 }

Added: 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/DPSignerTest.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/DPSignerTest.java?rev=1724647&view=auto
==============================================================================
--- 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/DPSignerTest.java
 (added)
+++ 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/DPSignerTest.java
 Thu Jan 14 16:20:31 2016
@@ -0,0 +1,84 @@
+/*
+ * 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
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.deploymentadmin.itest;
+
+import static junit.framework.TestCase.assertNotNull;
+import static 
org.apache.felix.deploymentadmin.itest.BaseIntegrationTest.TEST_FAILING_BUNDLE_RP1;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.net.URL;
+import java.security.Security;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+import org.apache.felix.deploymentadmin.itest.util.CertificateUtil;
+import org.apache.felix.deploymentadmin.itest.util.CertificateUtil.KeyType;
+import org.apache.felix.deploymentadmin.itest.util.CertificateUtil.SignerInfo;
+import org.apache.felix.deploymentadmin.itest.util.DPSigner;
+import org.apache.felix.deploymentadmin.itest.util.DeploymentPackageBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test cases for {@link DPSigner}.
+ */
+public class DPSignerTest {
+
+    @Test
+    public void testSignArtifactsOk() throws Exception {
+        URL dpProps = getClass().getResource("/dp.properties");
+        assertNotNull(dpProps);
+
+        DeploymentPackageBuilder builder = 
DeploymentPackageBuilder.create("dpSignerTest1", "1.0.0");
+        
builder.add(builder.createLocalizationResource().setUrl(dpProps).setResourceProcessorPID(TEST_FAILING_BUNDLE_RP1).setFilename("dp.properties"));
+
+        SignerInfo signerInfo = 
CertificateUtil.createSelfSignedCert("CN=testCert", KeyType.RSA);
+
+        DPSigner signer = new DPSigner();
+
+        byte[] rawData;
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            signer.sign(builder, signerInfo.getPrivate(), 
signerInfo.getCert(), baos);
+            rawData = baos.toByteArray();
+        }
+
+        try (ByteArrayInputStream bais = new ByteArrayInputStream(rawData); 
JarInputStream jis = new JarInputStream(bais, true)) {
+            JarEntry entry;
+            while ((entry = jis.getNextJarEntry()) != null) {
+                assertNotNull(entry);
+                jis.closeEntry();
+            }
+            
+            assertNotNull(jis.getManifest());
+        }
+    }
+
+    @Before
+    public void setUp() {
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
+    @After
+    public void tearDown() {
+        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
+    }
+}

Propchange: 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/DPSignerTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/InstallDeploymentPackageTest.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/InstallDeploymentPackageTest.java?rev=1724647&r1=1724646&r2=1724647&view=diff
==============================================================================
--- 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/InstallDeploymentPackageTest.java
 (original)
+++ 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/InstallDeploymentPackageTest.java
 Thu Jan 14 16:20:31 2016
@@ -18,9 +18,13 @@
  */
 package org.apache.felix.deploymentadmin.itest;
 
+import static 
org.apache.felix.deploymentadmin.itest.util.CertificateUtil.createSelfSignedCert;
+
 import java.io.File;
 import java.net.URL;
 
+import org.apache.felix.deploymentadmin.itest.util.CertificateUtil.KeyType;
+import org.apache.felix.deploymentadmin.itest.util.CertificateUtil.SignerInfo;
 import org.apache.felix.deploymentadmin.itest.util.DeploymentPackageBuilder;
 import 
org.apache.felix.deploymentadmin.itest.util.DeploymentPackageBuilder.JarManifestManipulatingFilter;
 import org.junit.Test;
@@ -35,8 +39,7 @@ import org.osgi.service.deploymentadmin.
  * Provides test cases regarding the use of "normal" deployment packages in 
DeploymentAdmin.
  */
 @RunWith(PaxExam.class)
-public class InstallDeploymentPackageTest extends BaseIntegrationTest
-{
+public class InstallDeploymentPackageTest extends BaseIntegrationTest {
     /**
      * FELIX-518 - Test that DP with localization and signature files are 
properly deployed.
      */
@@ -44,36 +47,40 @@ public class InstallDeploymentPackageTes
     public void 
testInstallDeploymentPackageWithLocalizationAndSignatureFilesOk() throws 
Exception {
         URL dpProps = getClass().getResource("/dp.properties");
         assertNotNull(dpProps);
-        
+
+        SignerInfo signer = createSelfSignedCert("CN=dpTest", KeyType.EC);
+
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
-        dpBuilder
-            .addSignatures()
+        dpBuilder.signOutput(signer.getPrivate(), signer.getCert())
             
.add(dpBuilder.createLocalizationResource().setUrl(dpProps).setResourceProcessorPID(TEST_FAILING_BUNDLE_RP1).setFilename("dp.properties"))
-            
.add(dpBuilder.createResourceProcessorResource().setUrl(getTestBundleURL("rp1")));
+            
.add(dpBuilder.createResourceProcessorResource().setUrl(getTestBundleURL("rp1")))
+            
.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundle1")))
+            
.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundle2")));
 
-        installDeploymentPackage(dpBuilder);
+        installDeploymentPackage(dpBuilder); // should succeed.
+        
+        assertBundleExists("testbundles.bundle1", "1.0.0");
+        assertBundleExists("testbundles.bundle2", "1.0.0");
+        assertBundleExists("testbundles.rp1", "1.0.0");
     }
-    
-    
+
     /**
      * FELIX-4409/4410/4463 - test the installation of an invalid deployment 
package.
      */
     @Test
-    public void testInstallInvalidDeploymentPackageFail() throws Exception
-    {
+    public void testInstallInvalidDeploymentPackageFail() throws Exception {
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
-        // incluse two different versions of the same bundle (with the same 
BSN), this is *not* allowed per the DA spec...
+        // incluse two different versions of the same bundle (with the same 
BSN), this is *not* allowed per the DA
+        // spec...
         dpBuilder
             
.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundleapi1", 
"bundleapi1", "1.0.0")))
             
.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundleapi2", 
"bundleapi2", "2.0.0")));
 
-        try
-        {
+        try {
             installDeploymentPackage(dpBuilder);
             fail("DeploymentException expected!");
         }
-        catch (DeploymentException e)
-        {
+        catch (DeploymentException e) {
             // Ok; expected...
         }
 
@@ -86,10 +93,10 @@ public class InstallDeploymentPackageTes
      * FELIX-1835 - test whether we can install bundles with a non-root path 
inside the DP.
      */
     @Test
-    public void testInstallBundlesWithPathsOk() throws Exception
-    {
+    public void testInstallBundlesWithPathsOk() throws Exception {
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
-        // incluse two different versions of the same bundle (with the same 
BSN), this is *not* allowed per the DA spec...
+        // incluse two different versions of the same bundle (with the same 
BSN), this is *not* allowed per the DA
+        // spec...
         dpBuilder
             
.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundleapi1", 
"bundleapi1", "1.0.0")).setFilename("bundles/bundleapi1.jar"))
             
.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundleimpl1", 
"bundleimpl1", "1.0.0")).setFilename("bundles/bundleimpl1.jar"));
@@ -106,11 +113,11 @@ public class InstallDeploymentPackageTes
     }
 
     /**
-     * Tests that adding the dependency for a bundle in an update package 
causes the depending bundle to be resolved and started.
+     * Tests that adding the dependency for a bundle in an update package 
causes the depending bundle to be resolved and
+     * started.
      */
     @Test
-    public void testInstallBundleWithDependencyInPackageUpdateOk() throws 
Exception
-    {
+    public void testInstallBundleWithDependencyInPackageUpdateOk() throws 
Exception {
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
         // missing bundle1 as dependency...
         
dpBuilder.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundle2")));
@@ -140,11 +147,11 @@ public class InstallDeploymentPackageTes
     }
 
     /**
-     * Tests that installing a bundle with a dependency installed by another 
deployment package is not started, but is resolved.
+     * Tests that installing a bundle with a dependency installed by another 
deployment package is not started, but is
+     * resolved.
      */
     @Test
-    public void testInstallBundleWithDependencyInSeparatePackageOk() throws 
Exception
-    {
+    public void testInstallBundleWithDependencyInSeparatePackageOk() throws 
Exception {
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
         
dpBuilder.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundle2")));
 
@@ -183,8 +190,7 @@ public class InstallDeploymentPackageTes
      * Tests that if an exception is thrown in the start method of a bundle, 
the installation is not rolled back.
      */
     @Test
-    public void 
testInstallBundleWithExceptionThrownInStartCausesNoRollbackOk() throws Exception
-    {
+    public void 
testInstallBundleWithExceptionThrownInStartCausesNoRollbackOk() throws 
Exception {
         System.setProperty("bundle3", "start");
 
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
@@ -208,11 +214,11 @@ public class InstallDeploymentPackageTes
     }
 
     /**
-     * Tests that installing a bundle along with a fragment bundle succeeds 
(DA should not try to start the fragment, see FELIX-4167).
+     * Tests that installing a bundle along with a fragment bundle succeeds 
(DA should not try to start the fragment,
+     * see FELIX-4167).
      */
     @Test
-    public void testInstallBundleWithFragmentOk() throws Exception
-    {
+    public void testInstallBundleWithFragmentOk() throws Exception {
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
         dpBuilder
             
.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundle1")))
@@ -236,8 +242,7 @@ public class InstallDeploymentPackageTes
      * Tests that installing a bundle whose dependencies cannot be met, is 
installed, but not started.
      */
     @Test
-    public void testInstallBundleWithMissingDependencyOk() throws Exception
-    {
+    public void testInstallBundleWithMissingDependencyOk() throws Exception {
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
         
dpBuilder.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundle2")));
 
@@ -258,8 +263,7 @@ public class InstallDeploymentPackageTes
      * Tests that installing a bundle along with other (non-bundle) artifacts 
succeeds.
      */
     @Test
-    public void testInstallBundleWithOtherArtifactsOk() throws Exception
-    {
+    public void testInstallBundleWithOtherArtifactsOk() throws Exception {
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
         dpBuilder
             
.add(dpBuilder.createResourceProcessorResource().setUrl(getTestBundleURL("rp1")))
@@ -282,8 +286,7 @@ public class InstallDeploymentPackageTes
      * Tests that installing a new bundle works as expected.
      */
     @Test
-    public void testInstallSingleValidBundleOk() throws Exception
-    {
+    public void testInstallSingleValidBundleOk() throws Exception {
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
         
dpBuilder.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundle1")));
 
@@ -302,8 +305,7 @@ public class InstallDeploymentPackageTes
      * Tests that installing two bundles works as expected.
      */
     @Test
-    public void testInstallTwoValidBundlesOk() throws Exception
-    {
+    public void testInstallTwoValidBundlesOk() throws Exception {
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
         dpBuilder
             
.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundle1")))
@@ -324,11 +326,11 @@ public class InstallDeploymentPackageTes
     }
 
     /**
-     * Tests that if an exception is thrown during the uninstall of a bundle, 
the installation/update continues and succeeds.
+     * Tests that if an exception is thrown during the uninstall of a bundle, 
the installation/update continues and
+     * succeeds.
      */
     @Test
-    public void 
testUninstallBundleWithExceptionThrownInStopCauseNoRollbackOk() throws Exception
-    {
+    public void 
testUninstallBundleWithExceptionThrownInStopCauseNoRollbackOk() throws 
Exception {
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
         dpBuilder
             
.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundle1")))
@@ -365,8 +367,7 @@ public class InstallDeploymentPackageTes
      * Tests that if an exception is thrown during the stop of a bundle, the 
installation/update continues and succeeds.
      */
     @Test
-    public void testUpdateBundleWithExceptionThrownInStopCauseNoRollbackOk() 
throws Exception
-    {
+    public void testUpdateBundleWithExceptionThrownInStopCauseNoRollbackOk() 
throws Exception {
         DeploymentPackageBuilder dpBuilder = 
createNewDeploymentPackageBuilder("1.0.0");
         dpBuilder
             
.add(dpBuilder.createBundleResource().setUrl(getTestBundleURL("bundle1")))
@@ -402,7 +403,8 @@ public class InstallDeploymentPackageTes
     }
 
     /**
-     * Tests that we can correctly rollback the installation of a deployment 
package for bundles that have their data area populated. 
+     * Tests that we can correctly rollback the installation of a deployment 
package for bundles that have their data
+     * area populated.
      */
     @Test
     public void testRollbackWithPopulatedDataAreaOk() throws Exception {
@@ -434,7 +436,7 @@ public class InstallDeploymentPackageTes
         // Simulate an upgrade for our bundle, which should cause its data 
area to be retained...
         dpBuilder = 
createDeploymentPackageBuilder(dpBuilder.getSymbolicName(), "1.0.1");
         dpBuilder
-        
.add(dpBuilder.createBundleResource().setVersion("1.1.0").setUrl(getTestBundleURL("bundle1")).setFilter(new
 JarManifestManipulatingFilter("Bundle-Version", "1.1.0")))
+            
.add(dpBuilder.createBundleResource().setVersion("1.1.0").setUrl(getTestBundleURL("bundle1")).setFilter(new
 JarManifestManipulatingFilter("Bundle-Version", "1.1.0")))
             
.add(dpBuilder.createResourceProcessorResource().setUrl(getTestBundleURL("rp1")))
             
.add(dpBuilder.createResource().setResourceProcessorPID(TEST_FAILING_BUNDLE_RP1).setUrl(getTestResource("test-config1.xml")));
 
@@ -449,7 +451,7 @@ public class InstallDeploymentPackageTes
         // We should still have this bundle..
         bundle1 = getBundle("testbundles.bundle1");
         assertNotNull("Unable to get installed test bundle?!", bundle1);
-        
+
         dataArea = bundle1.getDataFile("");
         assertNotNull("No data area obtained for test bundle?!", dataArea);
 
@@ -460,7 +462,7 @@ public class InstallDeploymentPackageTes
     }
 
     /**
-     * Tests that we can correctly install a deployment package with bundles 
that have their data area populated. 
+     * Tests that we can correctly install a deployment package with bundles 
that have their data area populated.
      */
     @Test
     public void testUpgradeWithPopulatedDataAreaOk() throws Exception {
@@ -497,7 +499,7 @@ public class InstallDeploymentPackageTes
         // We should still have this bundle..
         bundle1 = getBundle("testbundles.bundle1");
         assertNotNull("Unable to get installed test bundle?!", bundle1);
-        
+
         dataArea = bundle1.getDataFile("");
         assertNotNull("No data area obtained for test bundle?!", dataArea);
 
@@ -508,7 +510,7 @@ public class InstallDeploymentPackageTes
     }
 
     /**
-     * Tests that we can correctly install a deployment package with bundles 
that have their data area populated. 
+     * Tests that we can correctly install a deployment package with bundles 
that have their data area populated.
      */
     @Test
     public void testUninstallBundleWithPopulatedDataAreaOk() throws Exception {
@@ -548,8 +550,9 @@ public class InstallDeploymentPackageTes
         // Data area should be restored exactly as-is...
         assertFalse("Data area not purged?!", dataArea.exists());
     }
+
     /**
-     * Tests that we can correctly install a deployment package with bundles 
that have their data area populated. 
+     * Tests that we can correctly install a deployment package with bundles 
that have their data area populated.
      */
     @Test
     public void testRollbackUninstallBundleWithPopulatedDataAreaOk() throws 
Exception {
@@ -594,7 +597,7 @@ public class InstallDeploymentPackageTes
         // We should still have this bundle..
         bundle1 = getBundle("testbundles.bundle1");
         assertNotNull("Unable to get installed test bundle?!", bundle1);
-        
+
         dataArea = bundle1.getDataFile("");
         assertNotNull("No data area obtained for test bundle?!", dataArea);
 

Modified: 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/ArtifactData.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/ArtifactData.java?rev=1724647&r1=1724646&r2=1724647&view=diff
==============================================================================
--- 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/ArtifactData.java
 (original)
+++ 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/ArtifactData.java
 Thu Jan 14 16:20:31 2016
@@ -18,6 +18,8 @@
  */
 package org.apache.felix.deploymentadmin.itest.util;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.net.URL;
 
 public class ArtifactData {
@@ -38,6 +40,13 @@ public class ArtifactData {
         m_filename = filename;
     }
 
+    public final InputStream createInputStream() throws IOException {
+        if (m_filter != null) {
+            return m_filter.createInputStream(m_url);
+        }
+        return m_url.openStream();
+    }
+
     public String getFilename() {
         return m_filename;
     }
@@ -77,7 +86,7 @@ public class ArtifactData {
     public boolean isMissing() {
         return m_missing;
     }
-    
+
     public boolean isResourceProcessorNeeded() {
         return m_needRP;
     }

Added: 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/CertificateUtil.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/CertificateUtil.java?rev=1724647&view=auto
==============================================================================
--- 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/CertificateUtil.java
 (added)
+++ 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/CertificateUtil.java
 Thu Jan 14 16:20:31 2016
@@ -0,0 +1,123 @@
+/*
+ * 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
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.deploymentadmin.itest.util;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Random;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+/**
+ * @author <a href="mailto:[email protected]";>Felix Project Team</a>
+ */
+public class CertificateUtil {
+
+    public enum KeyType {
+        RSA, DSA, EC;
+    }
+
+    public static class SignerInfo {
+        private final KeyPair m_keyPair;
+        private final X509Certificate m_cert;
+
+        public SignerInfo(KeyPair keyPair, X509Certificate cert) {
+            m_keyPair = keyPair;
+            m_cert = cert;
+        }
+
+        public X509Certificate getCert() {
+            return m_cert;
+        }
+
+        public PrivateKey getPrivate() {
+            return m_keyPair.getPrivate();
+        }
+
+        public PublicKey getPublic() {
+            return m_keyPair.getPublic();
+        }
+    }
+
+    public static SignerInfo createSelfSignedCert(String commonName, KeyType 
type) throws Exception {
+        KeyPairGenerator kpGen;
+        switch (type) {
+            case RSA:
+                kpGen = KeyPairGenerator.getInstance("RSA");
+                kpGen.initialize(1024);
+                break;
+            case DSA:
+                kpGen = KeyPairGenerator.getInstance("DSA");
+                break;
+            case EC:
+                kpGen = KeyPairGenerator.getInstance("EC");
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid key type!");
+        }
+
+        KeyPair keyPair = kpGen.generateKeyPair();
+        X509Certificate cert = createSelfSignedCert(commonName, keyPair);
+
+        return new SignerInfo(keyPair, cert);
+    }
+
+    private static X509Certificate createSelfSignedCert(String commonName, 
KeyPair keypair) throws Exception {
+        PublicKey publicKey = keypair.getPublic();
+        String keyAlg = DPSigner.getSignatureAlgorithm(publicKey);
+
+        X500Name issuer = new X500Name(commonName);
+        BigInteger serial = BigInteger.probablePrime(16, new Random());
+        Date notBefore = new Date(System.currentTimeMillis() - 1000);
+        Date notAfter = new Date(notBefore.getTime() + 6000);
+
+        SubjectPublicKeyInfo pubKeyInfo;
+        try (ASN1InputStream is = new ASN1InputStream(publicKey.getEncoded())) 
{
+            pubKeyInfo = SubjectPublicKeyInfo.getInstance(is.readObject());
+        }
+
+        X509v3CertificateBuilder builder = new 
X509v3CertificateBuilder(issuer, serial, notBefore, notAfter, issuer, 
pubKeyInfo);
+        builder.addExtension(new Extension(Extension.basicConstraints, true, 
new DEROctetString(new BasicConstraints(false))));
+
+        X509CertificateHolder certHolder = builder.build(new 
JcaContentSignerBuilder(keyAlg).build(keypair.getPrivate()));
+        return new JcaX509CertificateConverter().getCertificate(certHolder);
+    }
+
+    /**
+     * Creates a new CertificateUtil instance.
+     */
+    private CertificateUtil() {
+        // Nop
+    }
+
+}

Propchange: 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/CertificateUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/DPSigner.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/DPSigner.java?rev=1724647&view=auto
==============================================================================
--- 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/DPSigner.java
 (added)
+++ 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/DPSigner.java
 Thu Jan 14 16:20:31 2016
@@ -0,0 +1,273 @@
+/*
+ * 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
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.deploymentadmin.itest.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.Key;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.DSAKey;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.RSAKey;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.commons.codec.binary.Base64;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+
+/**
+ * Signs a deployment package using a given keypair.
+ */
+public class DPSigner {
+    private static final String META_INF = "META-INF/";
+
+    public static String getSignatureAlgorithm(Key key) {
+        if (key instanceof RSAKey) {
+            return "SHA256withRSA";
+        }
+        else if (key instanceof DSAKey) {
+            return "SHA1withDSA";
+        }
+        else if (key instanceof ECKey) {
+            return "SHA256withECDSA";
+        }
+        else {
+            throw new IllegalArgumentException("Invalid/unsupported key: " + 
key.getClass().getName());
+        }
+    }
+    private static String getBlockFileExtension(Key key) {
+        if (key instanceof RSAKey) {
+            return ".RSA";
+        }
+        else if (key instanceof DSAKey) {
+            return ".DSA";
+        }
+        else if (key instanceof ECKey) {
+            return ".EC";
+        }
+        else {
+            throw new IllegalArgumentException("Invalid/unsupported key: " + 
key.getClass().getName());
+        }
+    }
+    private final MessageDigest m_digest;
+
+    private final String m_digestAlg;
+
+    private final String m_baseName;
+
+    public DPSigner() {
+        this("DP");
+    }
+
+    public DPSigner(String baseName) {
+        try {
+            m_baseName = META_INF.concat(baseName);
+            m_digest = MessageDigest.getInstance("SHA-256");
+            m_digestAlg = m_digest.getAlgorithm();
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("SHA-256 not supported by default?!");
+        }
+    }
+    
+    public void addDigestAttribute(Attributes attrs, ArtifactData file) throws 
IOException {
+        attrs.putValue(m_digestAlg.concat("-Digest"), calculateDigest(file));
+    }
+
+    public void sign(DeploymentPackageBuilder builder, PrivateKey privKey, 
X509Certificate cert, OutputStream os) throws Exception {
+        Manifest manifest = builder.createManifest();
+        List<ArtifactData> artifacts = builder.getArtifactList();
+        sign(manifest, artifacts, privKey, cert, os);
+    }
+
+    public void sign(Manifest manifest, List<ArtifactData> files, PrivateKey 
privKey, X509Certificate cert, OutputStream os) throws Exception {
+        // For each file, add its signature to the manifest
+        for (ArtifactData file : files) {
+            String filename = file.getFilename();
+            Attributes attrs = manifest.getAttributes(filename);
+            addDigestAttribute(attrs, file);
+        }
+
+        try (ZipOutputStream zos = new ZipOutputStream(os)) {
+            writeSignedManifest(manifest, zos, privKey, cert);
+
+            for (ArtifactData file : files) {
+                ZipEntry entry = new ZipEntry(file.getFilename());
+                zos.putNextEntry(entry);
+
+                try (InputStream is = file.createInputStream()) {
+                    byte[] buf = new byte[1024];
+                    int read;
+                    while ((read = is.read(buf)) > 0) {
+                        zos.write(buf, 0, read);
+                    }
+                }
+            }
+        }
+    }
+
+    public void writeSignedManifest(Manifest manifest, ZipOutputStream zos, 
PrivateKey privKey, X509Certificate cert) throws Exception {
+        zos.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME));
+        manifest.write(zos);
+        zos.closeEntry();
+
+        long now = System.currentTimeMillis();
+
+        // Determine the signature-file manifest...
+        Manifest sf = createSignatureFile(manifest);
+
+        byte[] sfRawBytes;
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            sf.write(baos);
+            sfRawBytes = baos.toByteArray();
+        }
+
+        ZipEntry sigFileEntry = new ZipEntry(m_baseName.concat(".SF"));
+        sigFileEntry.setTime(now);
+        zos.putNextEntry(sigFileEntry);
+        // Write the actual entry data...
+        zos.write(sfRawBytes, 0, sfRawBytes.length);
+        zos.closeEntry();
+
+        // Create a PKCS#7 signature...
+        byte[] encoded = calculateSignatureBlock(privKey, cert, sfRawBytes);
+
+        ZipEntry blockFileEntry = new 
ZipEntry(m_baseName.concat(getBlockFileExtension(privKey)));
+        blockFileEntry.setTime(now);
+        zos.putNextEntry(blockFileEntry);
+        zos.write(encoded);
+        zos.closeEntry();
+    }
+
+    private String calculateDigest(ArtifactData file) throws IOException {
+        m_digest.reset();
+        try (InputStream is = file.createInputStream()) {
+            byte[] buffer = new byte[1024];
+            int read;
+            while ((read = is.read(buffer)) > 0) {
+                m_digest.update(buffer, 0, read);
+            }
+        }
+        return Base64.encodeBase64String(m_digest.digest());
+    }
+
+    private String calculateDigest(byte[] rawData) throws IOException {
+        m_digest.reset();
+        m_digest.update(rawData, 0, rawData.length);
+        return Base64.encodeBase64String(m_digest.digest());
+    }
+
+    private byte[] calculateSignatureBlock(PrivateKey privKey, X509Certificate 
cert, byte[] sfRawBytes) throws Exception {
+        String signatureAlgorithm = getSignatureAlgorithm(privKey);
+        
+        DigestCalculatorProvider digestCalculatorProvider = new 
JcaDigestCalculatorProviderBuilder().build();
+        ContentSigner signer = new 
JcaContentSignerBuilder(signatureAlgorithm).build(privKey);
+
+        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
+        gen.addSignerInfoGenerator(new 
JcaSignerInfoGeneratorBuilder(digestCalculatorProvider).build(signer, cert));
+        gen.addCertificates(new JcaCertStore(Arrays.asList(cert)));
+
+        CMSSignedData sigData = gen.generate(new 
CMSProcessableByteArray(sfRawBytes));
+
+        return sigData.getEncoded();
+    }
+
+    private Manifest createSignatureFile(Manifest manifest) throws IOException 
{
+        byte[] mfRawBytes;
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            manifest.write(baos);
+            mfRawBytes = baos.toByteArray();
+        }
+
+        Manifest sf = new Manifest();
+        Attributes sfMain = sf.getMainAttributes();
+        Map<String, Attributes> sfEntries = sf.getEntries();
+
+        sfMain.put(Attributes.Name.SIGNATURE_VERSION, "1.0");
+        sfMain.putValue("Created-By", "Apache Felix DeploymentPackageBuilder");
+        sfMain.putValue(m_digestAlg + "-Digest-Manifest", 
calculateDigest(mfRawBytes));
+        sfMain.putValue(m_digestAlg + "-Digest-Manifest-Main-Attribute", 
calculateDigest(getRawBytesMainAttributes(manifest)));
+
+        for (Entry<String, Attributes> entry : 
manifest.getEntries().entrySet()) {
+            String name = entry.getKey();
+            byte[] entryData = getRawBytesAttributes(entry.getValue());
+
+            sfEntries.put(name, getDigestAttributes(entryData));
+        }
+        return sf;
+    }
+
+    private Attributes getDigestAttributes(byte[] rawData) throws IOException {
+        Attributes attrs = new Attributes();
+        attrs.putValue(m_digestAlg + "-Digest", calculateDigest(rawData));
+        return attrs;
+    }
+
+    private byte[] getRawBytesAttributes(Attributes attrs) throws IOException {
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
DataOutputStream dos = new DataOutputStream(baos)) {
+
+            Method m = Attributes.class.getDeclaredMethod("write", 
DataOutputStream.class);
+            m.setAccessible(true);
+            m.invoke(attrs, dos);
+
+            return baos.toByteArray();
+        }
+        catch (NoSuchMethodException | SecurityException | 
InvocationTargetException | IllegalAccessException e) {
+            throw new RuntimeException("Failed to get raw bytes of main 
attributes!", e);
+        }
+    }
+
+    private byte[] getRawBytesMainAttributes(Manifest manifest) throws 
IOException {
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
DataOutputStream dos = new DataOutputStream(baos)) {
+            Attributes attrs = manifest.getMainAttributes();
+
+            Method m = Attributes.class.getDeclaredMethod("writeMain", 
DataOutputStream.class);
+            m.setAccessible(true);
+            m.invoke(attrs, dos);
+
+            return baos.toByteArray();
+        }
+        catch (NoSuchMethodException | SecurityException | 
InvocationTargetException | IllegalAccessException e) {
+            throw new RuntimeException("Failed to get raw bytes of main 
attributes!", e);
+        }
+    }
+}

Propchange: 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/DPSigner.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/DeploymentPackageBuilder.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/DeploymentPackageBuilder.java?rev=1724647&r1=1724646&r2=1724647&view=diff
==============================================================================
--- 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/DeploymentPackageBuilder.java
 (original)
+++ 
felix/trunk/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/util/DeploymentPackageBuilder.java
 Thu Jan 14 16:20:31 2016
@@ -25,6 +25,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URL;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
@@ -32,9 +34,11 @@ import java.util.List;
 import java.util.Map;
 import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
 import java.util.jar.JarInputStream;
 import java.util.jar.JarOutputStream;
 import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
 
 import org.osgi.framework.Version;
 
@@ -100,36 +104,47 @@ public class DeploymentPackageBuilder {
 
     private static final int BUFFER_SIZE = 32 * 1024;
 
-    /**
-     * Creates a new deployment package builder.
-     * 
-     * @param name the name of the deployment package
-     * @param version the version of the deployment package
-     * @return a builder to further add data to the deployment package
-     */
-    public static DeploymentPackageBuilder create(String name, String version) 
{
-        return new DeploymentPackageBuilder(name, version);
-    }
-
+    private final DPSigner m_signer;
     private final String m_symbolicName;
     private final String m_version;
     private final List<ArtifactData> m_localizationFiles = new 
ArrayList<ArtifactData>();
     private final List<ArtifactData> m_bundles = new ArrayList<ArtifactData>();
     private final List<ArtifactData> m_processors = new 
ArrayList<ArtifactData>();
-
     private final List<ArtifactData> m_artifacts = new 
ArrayList<ArtifactData>();
 
     private String m_fixPackageVersion;
-
-    private boolean m_addSignatures;
     private boolean m_verification;
+    private PrivateKey m_signingKey;
+    private X509Certificate m_signingCert;
 
     private DeploymentPackageBuilder(String symbolicName, String version) {
         m_symbolicName = symbolicName;
         m_version = version;
 
-        m_addSignatures = false;
         m_verification = true;
+        m_signer = new DPSigner();
+    }
+
+    /**
+     * Creates a new deployment package builder.
+     * 
+     * @param name the name of the deployment package
+     * @param version the version of the deployment package
+     * @return a builder to further add data to the deployment package
+     */
+    public static DeploymentPackageBuilder create(String name, String version) 
{
+        return new DeploymentPackageBuilder(name, version);
+    }
+
+    static void closeSilently(Closeable resource) {
+        if (resource != null) {
+            try {
+                resource.close();
+            }
+            catch (IOException e) {
+                // Ignore...
+            }
+        }
     }
 
     /**
@@ -156,11 +171,6 @@ public class DeploymentPackageBuilder {
         return this;
     }
 
-    public DeploymentPackageBuilder addSignatures() {
-        m_addSignatures = true;
-        return this;
-    }
-
     /**
      * Creates a new deployment package builder with the same symbolic name as 
this builder.
      * 
@@ -210,7 +220,6 @@ public class DeploymentPackageBuilder {
     public InputStream generate() throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         generate(baos);
-
         return new ByteArrayInputStream(baos.toByteArray());
     }
 
@@ -223,26 +232,8 @@ public class DeploymentPackageBuilder {
      * @throws Exception if something goes wrong while validating or generating
      */
     public void generate(OutputStream output) throws Exception {
-        List<ArtifactData> artifacts = new ArrayList<ArtifactData>();
-        artifacts.addAll(m_localizationFiles);
-        artifacts.addAll(m_bundles);
-        artifacts.addAll(m_processors);
-        artifacts.addAll(m_artifacts);
-
-        if (m_verification) {
-            validateProcessedArtifacts();
-            validateMissingArtifacts(artifacts);
-        }
-
-        Manifest m = createManifest(artifacts);
-
-        // The order in which the actual entries are added to the JAR is 
different than we're using for the manifest...
-        artifacts.clear();
-        artifacts.addAll(m_bundles);
-        artifacts.addAll(m_processors);
-        artifacts.addAll(m_localizationFiles);
-        artifacts.addAll(m_artifacts);
-
+        Manifest m = createManifest();
+        List<ArtifactData> artifacts = getArtifactList();
         writeStream(artifacts, m, output);
     }
 
@@ -283,6 +274,47 @@ public class DeploymentPackageBuilder {
         return this;
     }
 
+    /**
+     * Enables the creating of a signed deployment package, equivalent to 
creating a signed JAR file.
+     * <p>
+     * This method assumes the use of self-signed certificates for the signing 
process.
+     * </p>
+     * 
+     * @param signingKey the private key of the signer;
+     * @param signingCert the public certificate of the signer.
+     * @return this builder.
+     */
+    public DeploymentPackageBuilder signOutput(PrivateKey signingKey, 
X509Certificate signingCert) {
+        m_signingKey = signingKey;
+        m_signingCert = signingCert;
+        return this;
+    }
+
+    final Manifest createManifest() throws Exception {
+        List<ArtifactData> artifacts = new ArrayList<ArtifactData>();
+        artifacts.addAll(m_localizationFiles);
+        artifacts.addAll(m_bundles);
+        artifacts.addAll(m_processors);
+        artifacts.addAll(m_artifacts);
+
+        if (m_verification) {
+            validateProcessedArtifacts();
+            validateMissingArtifacts(artifacts);
+        }
+
+        return createManifest(artifacts);
+    }
+
+    final List<ArtifactData> getArtifactList() {
+        // The order in which the actual entries are added to the JAR is 
different than we're using for the manifest...
+        List<ArtifactData> artifacts = new ArrayList<ArtifactData>();
+        artifacts.addAll(m_bundles);
+        artifacts.addAll(m_processors);
+        artifacts.addAll(m_localizationFiles);
+        artifacts.addAll(m_artifacts);
+        return artifacts;
+    }
+
     private Manifest createManifest(List<ArtifactData> files) throws Exception 
{
         Manifest manifest = new Manifest();
         Attributes main = manifest.getMainAttributes();
@@ -296,39 +328,40 @@ public class DeploymentPackageBuilder {
 
         Map<String, Attributes> entries = manifest.getEntries();
 
-        Iterator<ArtifactData> filesIter = files.iterator();
-        while (filesIter.hasNext()) {
-            ArtifactData file = filesIter.next();
-
-            Attributes a = new Attributes();
-            a.putValue("Name", file.getFilename());
+        for (ArtifactData file : files) {
+            Attributes attrs = new Attributes();
+            attrs.putValue("Name", file.getFilename());
 
             if (file.isBundle()) {
-                a.putValue("Bundle-SymbolicName", file.getSymbolicName());
-                a.putValue("Bundle-Version", file.getVersion());
+                attrs.putValue("Bundle-SymbolicName", file.getSymbolicName());
+                attrs.putValue("Bundle-Version", file.getVersion());
                 if (file.isCustomizer()) {
-                    a.putValue("DeploymentPackage-Customizer", "true");
-                    a.putValue("Deployment-ProvidesResourceProcessor", 
file.getProcessorPid());
+                    attrs.putValue("DeploymentPackage-Customizer", "true");
+                    attrs.putValue("Deployment-ProvidesResourceProcessor", 
file.getProcessorPid());
                 }
             }
             else if (file.isResourceProcessorNeeded()) {
-                a.putValue("Resource-Processor", file.getProcessorPid());
+                attrs.putValue("Resource-Processor", file.getProcessorPid());
             }
 
             if (file.isMissing()) {
-                a.putValue("DeploymentPackage-Missing", "true");
+                attrs.putValue("DeploymentPackage-Missing", "true");
             }
 
-            if (m_addSignatures) {
-                a.putValue("SHA-256-Digest", "bogusdata=");
+            if (isAddSignatures()) {
+                m_signer.addDigestAttribute(attrs, file);
             }
 
-            entries.put(file.getFilename(), a);
+            entries.put(file.getFilename(), attrs);
         }
 
         return manifest;
     }
 
+    private boolean isAddSignatures() {
+        return m_signingKey != null && m_signingCert != null;
+    }
+
     private void validateMissingArtifacts(List<ArtifactData> files) throws 
Exception {
         boolean missing = false;
 
@@ -367,44 +400,21 @@ public class DeploymentPackageBuilder {
         }
     }
 
-    private InputStream getArtifactDataInputStream(ArtifactData file) throws 
IOException {
-        ResourceFilter filter = file.getFilter();
-        if (filter != null) {
-            return filter.createInputStream(file.getURL());
-        }
-        return file.getURL().openStream();
-    }
-
     private void writeStream(List<ArtifactData> files, Manifest manifest, 
OutputStream outputStream) throws Exception {
         byte[] buffer = new byte[BUFFER_SIZE];
-        JarOutputStream output = null;
-        InputStream is = null;
-        try {
-            output = new JarOutputStream(outputStream, manifest);
-
-            if (m_addSignatures) {
-                // Empty file index...
-                output.putNextEntry(new JarEntry("META-INF/INDEX.LIST"));
-                output.write(new byte[0]);
-                output.closeEntry();
-                
-                // Create a signature file + signature block
-                Manifest mf = new Manifest();
-                mf.getMainAttributes().put(Attributes.Name.SIGNATURE_VERSION, 
"1.0");
-                mf.getMainAttributes().putValue("SHA-256-Digest-Manifest", 
"bogusdata=");
-
-                output.putNextEntry(new JarEntry("META-INF/DP.SF"));
-                mf.write(output);
-                output.closeEntry();
 
-                output.putNextEntry(new JarEntry("META-INF/DP.DSA"));
-                output.write(new byte[] { 1, 2, 3, 4 });
+        try (JarOutputStream output = new JarOutputStream(outputStream)) {
+            // Write out the manifest...
+            if (isAddSignatures()) {
+                m_signer.writeSignedManifest(manifest, output, m_signingKey, 
m_signingCert);
+            }
+            else {
+                output.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME));
+                manifest.write(output);
                 output.closeEntry();
             }
 
-            Iterator<ArtifactData> filesIter = files.iterator();
-            while (filesIter.hasNext()) {
-                ArtifactData file = filesIter.next();
+            for (ArtifactData file : files) {
                 if (file.isMissing()) {
                     // No need to write the 'missing' files...
                     continue;
@@ -412,34 +422,16 @@ public class DeploymentPackageBuilder {
 
                 output.putNextEntry(new JarEntry(file.getFilename()));
 
-                is = getArtifactDataInputStream(file);
-                try {
+                try (InputStream is = file.createInputStream()) {
                     int bytes;
                     while ((bytes = is.read(buffer)) != -1) {
                         output.write(buffer, 0, bytes);
                     }
                 }
                 finally {
-                    closeSilently(is);
-
                     output.closeEntry();
                 }
             }
         }
-        finally {
-            closeSilently(is);
-            closeSilently(output);
-        }
-    }
-
-    static void closeSilently(Closeable resource) {
-        if (resource != null) {
-            try {
-                resource.close();
-            }
-            catch (IOException e) {
-                // Ignore...
-            }
-        }
     }
 }


Reply via email to