ace-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j...@apache.org
Subject svn commit: r1332609 - in /ace/trunk: ace-authenticationprocessor-clientcert/src/main/java/org/apache/ace/authenticationprocessor/clientcert/ ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/ ace-co...
Date Tue, 01 May 2012 10:35:06 GMT
Author: jawi
Date: Tue May  1 10:35:05 2012
New Revision: 1332609

URL: http://svn.apache.org/viewvc?rev=1332609&view=rev
Log:
ACE-270: allow custom keystore/truststore to be supplied to the ClientCertAuthProcessor.

Modified:
    ace/trunk/ace-authenticationprocessor-clientcert/src/main/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessor.java
    ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessorTest.java
    ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/MemoryKeyStore.java
    ace/trunk/ace-connectionfactory/pom.xml
    ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImpl.java
    ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentials.java
    ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactory.java
    ace/trunk/ace-connectionfactory/src/test/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImplTest.java
    ace/trunk/ace-connectionfactory/src/test/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactoryTest.java

Modified: ace/trunk/ace-authenticationprocessor-clientcert/src/main/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessor.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-authenticationprocessor-clientcert/src/main/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessor.java?rev=1332609&r1=1332608&r2=1332609&view=diff
==============================================================================
--- ace/trunk/ace-authenticationprocessor-clientcert/src/main/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessor.java (original)
+++ ace/trunk/ace-authenticationprocessor-clientcert/src/main/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessor.java Tue May  1 10:35:05 2012
@@ -32,14 +32,14 @@ import org.apache.ace.authentication.api
 import org.osgi.service.cm.ConfigurationException;
 import org.osgi.service.cm.ManagedService;
 import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.User;
 import org.osgi.service.useradmin.UserAdmin;
 
 /**
- * Provides an {@link AuthenticationProcessor} that implements basic HTTP authentication and looks
- * up a user in the {@link UserAdmin} service using (by default, can be configured otherwise) the
- * keys "username" and "publickey". Only if the public key of the user in UserAdmin equals to the
- * public key of the obtained certificate, the user is considered authenticated.
+ * Provides an {@link AuthenticationProcessor} that implements authentication based on certificates 
+ * and looks up a user in the {@link UserAdmin} service using (by default, can be configured 
+ * otherwise) the key "username". If a matching user is found, it is considered authenticated.
  */
 public class ClientCertAuthenticationProcessor implements AuthenticationProcessor, ManagedService {
 
@@ -48,14 +48,17 @@ public class ClientCertAuthenticationPro
     static final String ATTRIBUTE_X509_CERTIFICATE = "javax.servlet.request.X509Certificate";
     static final String ATTRIBUTE_CIPHER_SUITE = "javax.servlet.request.cipher_suite";
 
-    static final String PROPERTY_KEY_USERNAME = "key.username";
-    static final String PROPERTY_KEY_PUBLICKEY = "key.publickey";
-
-    private static final String DEFAULT_PROPERTY_KEY_USERNAME = "username";
-    private static final String DEFAULT_PROPERTY_KEY_PUBLICKEY = "publickey";
-
-    private volatile String m_keyUsername = DEFAULT_PROPERTY_KEY_USERNAME;
-    private volatile String m_keyPublicKey = DEFAULT_PROPERTY_KEY_PUBLICKEY;
+    static final String PROPERTY_USERNAME_LOOKUPKEY = "user.name.lookupKey";
+    static final String PROPERTY_USERNAME_MATCH_POLICY = "user.name.matchPolicy";
+    static final String PROPERTY_VERIFY_CERT_VALIDITY = "certificate.verifyValidity";
+
+    private static final String DEFAULT_PROPERTY_USERNAME_LOOKUPKEY = "username";
+    private static final String DEFAULT_PROPERTY_USERNAME_MATCHPOLICY = "cn";
+    private static final boolean DEFAULT_PROPERTY_VERIFY_CERT_VALIDITY = true;
+
+    private volatile String m_nameLookupKey = DEFAULT_PROPERTY_USERNAME_LOOKUPKEY;
+    private volatile String m_nameMatchPolicy = DEFAULT_PROPERTY_USERNAME_MATCHPOLICY;
+    private volatile boolean m_verifyCertValidity = DEFAULT_PROPERTY_VERIFY_CERT_VALIDITY;
     private volatile LogService m_log;
 
     /**
@@ -80,7 +83,12 @@ public class ClientCertAuthenticationPro
             throw new IllegalArgumentException("Invalid context!");
         }
 
-        return (context[0] instanceof HttpServletRequest);
+        if (!(context[0] instanceof HttpServletRequest)) {
+            return false;
+        }
+        
+        final HttpServletRequest request = (HttpServletRequest) context[0];
+        return (request.getAttribute(ATTRIBUTE_CIPHER_SUITE) != null) && (request.getAttribute(ATTRIBUTE_X509_CERTIFICATE) != null);
     }
 
     /**
@@ -112,15 +120,15 @@ public class ClientCertAuthenticationPro
             return null;
         }
 
-        String username = getCommonName(cert);
-        if (username == null) {
+        String name = getName(cert);
+        if (name == null) {
             // No common name given; cannot retrieve user credentials...
             m_log.log(LogService.LOG_DEBUG, "Failed to obtain common name of X509 certificate!");
             return null;
         }
-
-        User user = userAdmin.getUser(m_keyUsername, username);
-        if (user == null || !user.hasCredential(m_keyPublicKey, cert.getPublicKey().getEncoded())) {
+        
+        User user = getUser(userAdmin, name);
+        if (user == null) {
             // Invalid/unknown user!
             m_log.log(LogService.LOG_DEBUG, "Failed to validate user using certificate!");
             return null;
@@ -134,38 +142,48 @@ public class ClientCertAuthenticationPro
      */
     public void updated(Dictionary dictionary) throws ConfigurationException {
         if (dictionary != null) {
-            String keyUsername = (String) dictionary.get(PROPERTY_KEY_USERNAME);
-            if (keyUsername == null || "".equals(keyUsername.trim())) {
-                throw new ConfigurationException(PROPERTY_KEY_USERNAME, "Missing property");
+            String usernameLookupKey = (String) dictionary.get(PROPERTY_USERNAME_LOOKUPKEY);
+            if (usernameLookupKey == null || "".equals(usernameLookupKey.trim())) {
+                throw new ConfigurationException(PROPERTY_USERNAME_LOOKUPKEY, "Missing property");
             }
 
-            String keyPassword = (String) dictionary.get(PROPERTY_KEY_PUBLICKEY);
-            if (keyPassword == null || "".equals(keyPassword.trim())) {
-                throw new ConfigurationException(PROPERTY_KEY_PUBLICKEY, "Missing property");
+            String usernameMatchPolicy = (String) dictionary.get(PROPERTY_USERNAME_MATCH_POLICY);
+            if (usernameMatchPolicy == null || "".equals(usernameMatchPolicy.trim())) {
+                throw new ConfigurationException(PROPERTY_USERNAME_MATCH_POLICY, "Missing property");
+            }
+            
+            Object verifyCertValidity = dictionary.get(PROPERTY_VERIFY_CERT_VALIDITY);
+            if (verifyCertValidity == null || !("true".equals(verifyCertValidity) || "false".equals(verifyCertValidity))) {
+                throw new ConfigurationException(PROPERTY_VERIFY_CERT_VALIDITY, "Missing or invalid property!");
             }
 
-            m_keyUsername = keyUsername;
-            m_keyPublicKey = keyPassword;
+            m_nameLookupKey = usernameLookupKey;
+            m_nameMatchPolicy = usernameMatchPolicy;
+            m_verifyCertValidity = Boolean.parseBoolean((String) verifyCertValidity);
         }
         else {
-            m_keyUsername = DEFAULT_PROPERTY_KEY_USERNAME;
-            m_keyPublicKey = DEFAULT_PROPERTY_KEY_PUBLICKEY;
+            m_nameLookupKey = DEFAULT_PROPERTY_USERNAME_LOOKUPKEY;
+            m_nameMatchPolicy = DEFAULT_PROPERTY_USERNAME_MATCHPOLICY;
+            m_verifyCertValidity = DEFAULT_PROPERTY_VERIFY_CERT_VALIDITY;
         }
     }
 
     /**
-     * Retrieves the common name for the given certificate.
+     * Retrieves the name for the given certificate.
      * 
-     * @param certificate the certificate to get its common name for, cannot be <code>null</code>.
-     * @return the common name for the given certificate, can be <code>null</code>.
+     * @param certificate the certificate to get its name for, cannot be <code>null</code>.
+     * @return the name for the given certificate, can be <code>null</code>.
      */
-    private String getCommonName(X509Certificate certificate) {
+    private String getName(X509Certificate certificate) {
         try {
             String dn = certificate.getSubjectX500Principal().getName();
+            if ("dn".equalsIgnoreCase(m_nameMatchPolicy)) {
+                return dn;
+            }
 
             LdapName ldapDN = new LdapName(dn);
             for (Rdn rdn : ldapDN.getRdns()) {
-                if ("CN".equals(rdn.getType())) {
+                if (m_nameMatchPolicy.equalsIgnoreCase(rdn.getType())) {
                     return (String) rdn.getValue();
                 }
             }
@@ -177,6 +195,29 @@ public class ClientCertAuthenticationPro
     }
 
     /**
+     * Searches for a user with a given name.
+     * <p>
+     * This method first looks whether there's a user with the property 
+     * "m_keyUsername" that matches the given username, if not found, it will 
+     * try to retrieve a role with the given name.
+     * </p>
+     * 
+     * @param userAdmin the {@link UserAdmin} service to get users from;
+     * @param name the name of the user to retrieve.
+     * @return a {@link User}, can be <code>null</code> if no such user is found.
+     */
+    private User getUser(UserAdmin userAdmin, String name) {
+        Role user = null;
+        if (m_nameLookupKey != null) {
+            user = userAdmin.getUser(m_nameLookupKey, name);
+        }
+        if (user == null) {
+            user = userAdmin.getRole(name);
+        }
+        return (user instanceof User) ? (User) user : null;
+    }
+
+    /**
      * Validates the certificate chain whether all certificates are valid and not expired.
      * 
      * @param certificateChain the chain of certificates to validate, cannot be <code>null</code>.
@@ -189,7 +230,9 @@ public class ClientCertAuthenticationPro
                     // Bogus certificate given...
                     return null;
                 }
-                cert.checkValidity();
+                if (m_verifyCertValidity) {
+                    cert.checkValidity();
+                }
             }
         }
         catch (CertificateExpiredException e) {

Modified: ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessorTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessorTest.java?rev=1332609&r1=1332608&r2=1332609&view=diff
==============================================================================
--- ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessorTest.java (original)
+++ ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessorTest.java Tue May  1 10:35:05 2012
@@ -20,8 +20,9 @@ package org.apache.ace.authenticationpro
 
 import static org.apache.ace.authenticationprocessor.clientcert.ClientCertAuthenticationProcessor.ATTRIBUTE_CIPHER_SUITE;
 import static org.apache.ace.authenticationprocessor.clientcert.ClientCertAuthenticationProcessor.ATTRIBUTE_X509_CERTIFICATE;
-import static org.apache.ace.authenticationprocessor.clientcert.ClientCertAuthenticationProcessor.PROPERTY_KEY_PUBLICKEY;
-import static org.apache.ace.authenticationprocessor.clientcert.ClientCertAuthenticationProcessor.PROPERTY_KEY_USERNAME;
+import static org.apache.ace.authenticationprocessor.clientcert.ClientCertAuthenticationProcessor.PROPERTY_USERNAME_LOOKUPKEY;
+import static org.apache.ace.authenticationprocessor.clientcert.ClientCertAuthenticationProcessor.PROPERTY_USERNAME_MATCH_POLICY;
+import static org.apache.ace.authenticationprocessor.clientcert.ClientCertAuthenticationProcessor.PROPERTY_VERIFY_CERT_VALIDITY;
 import static org.apache.ace.test.utils.TestUtils.UNIT;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -34,9 +35,9 @@ import java.util.Calendar;
 import java.util.Date;
 import java.util.Properties;
 
+import javax.security.auth.x500.X500Principal;
 import javax.servlet.http.HttpServletRequest;
 
-import org.mockito.Mockito;
 import org.osgi.service.cm.ConfigurationException;
 import org.osgi.service.log.LogService;
 import org.osgi.service.useradmin.User;
@@ -122,7 +123,6 @@ public class ClientCertAuthenticationPro
         m_userAdmin = mock(UserAdmin.class);
         m_servletRequest = mock(HttpServletRequest.class);
 
-        when(m_servletRequest.getAuthType()).thenReturn(HttpServletRequest.CLIENT_CERT_AUTH);
         when(m_servletRequest.getAttribute(ATTRIBUTE_CIPHER_SUITE)).thenReturn("bogus-cipher-suite");
     }
 
@@ -193,13 +193,11 @@ public class ClientCertAuthenticationPro
     @Test(groups = { UNIT })
     public void testAuthenticateKnownUserYieldsValidResult() {
         X509Certificate[] certChain = createValidCertificateChain("bob");
-        PublicKey publicKey = certChain[0].getPublicKey();
 
         when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(certChain);
 
         User user = mock(User.class);
         when(user.getName()).thenReturn("bob");
-        when(user.hasCredential(eq("publickey"), eq(publicKey.getEncoded()))).thenReturn(Boolean.TRUE);
 
         when(m_userAdmin.getUser(eq("username"), eq("bob"))).thenReturn(user);
 
@@ -210,6 +208,37 @@ public class ClientCertAuthenticationPro
     }
 
     /**
+     * Tests that authenticating a known user with a valid certificate chain will not yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateKnownUserWithValidCertificateChainYieldsValidResult() throws ConfigurationException {
+        ClientCertAuthenticationProcessor processor = createAuthorizationProcessor();
+
+        final String lookupKey = "anyKey";
+        final String matchPolicy = "dn";
+
+        Properties props = new Properties();
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, lookupKey);
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, matchPolicy);
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
+        processor.updated(props);
+
+        X509Certificate[] certChain = createValidCertificateChainWithDN("cn=Alice,dc=acme,dc=corp", "cn=Fido,ou=dev,dc=acme,dc=corp", "cn=Bob,ou=dev,dc=acme,dc=corp");
+
+        when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(certChain);
+
+        User user = mock(User.class);
+        when(user.getName()).thenReturn("bob");
+
+        when(m_userAdmin.getUser(eq(lookupKey), eq("CN=Bob,OU=dev,DC=acme,DC=corp"))).thenReturn(user);
+
+        User result = processor.authenticate(m_userAdmin, m_servletRequest);
+        assert result != null : "Expected a valid user to be returned!";
+
+        assert "bob".equals(user.getName()) : "Expected bob to be returned as user!";
+    }
+
+    /**
      * Tests that a missing cipher suite header will the authenticate method to yield null.
      */
     @Test(groups = { UNIT })
@@ -245,7 +274,9 @@ public class ClientCertAuthenticationPro
      */
     @Test(groups = { UNIT })
     public void testCanHandleDoesAcceptServletRequest() {
-        assert createAuthorizationProcessor().canHandle(mock(HttpServletRequest.class));
+        when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(createValidCertificateChain("alice"));
+
+        assert createAuthorizationProcessor().canHandle(m_servletRequest);
     }
 
     /**
@@ -277,31 +308,27 @@ public class ClientCertAuthenticationPro
      */
     @Test(groups = { UNIT })
     public void testUpdatedDoesAcceptCorrectProperties() throws ConfigurationException {
-        final String keyUsername = "foo";
-        final String keyPublicKey = "bar";
-        
-        Mockito.reset(m_userAdmin, m_servletRequest);
+        final String lookupKey = "anyKey";
+        final String matchPolicy = "cn";
 
         Properties props = new Properties();
-        props.put(PROPERTY_KEY_USERNAME, keyUsername);
-        props.put(PROPERTY_KEY_PUBLICKEY, keyPublicKey);
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, lookupKey);
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, matchPolicy);
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
 
         ClientCertAuthenticationProcessor processor = createAuthorizationProcessor();
 
         processor.updated(props);
 
         X509Certificate[] certificateChain = createValidCertificateChain("alice");
-        PublicKey publickey = certificateChain[0].getPublicKey();
 
         // Test whether we can use the new properties...
         when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(certificateChain);
-        when(m_servletRequest.getAttribute(ATTRIBUTE_CIPHER_SUITE)).thenReturn("bogus-cipher-suite");
 
         User user = mock(User.class);
         when(user.getName()).thenReturn("alice");
-        when(user.hasCredential(eq(keyPublicKey), eq(publickey.getEncoded()))).thenReturn(Boolean.TRUE);
 
-        when(m_userAdmin.getUser(eq(keyUsername), eq("alice"))).thenReturn(user);
+        when(m_userAdmin.getUser(eq(lookupKey), eq("alice"))).thenReturn(user);
 
         User result = processor.authenticate(m_userAdmin, m_servletRequest);
         assert result != null : "Expected a valid user to be returned!";
@@ -310,47 +337,76 @@ public class ClientCertAuthenticationPro
     }
 
     /**
-     * Tests that updated throws an exception for missing "key.password" property.
+     * Tests that updated throws an exception for missing "username match policy" property.
      */
     @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
-    public void testUpdatedDoesNotAcceptEmptyKeyPassword() throws ConfigurationException {
+    public void testUpdatedDoesNotAcceptEmptyMatchPolicy() throws ConfigurationException {
         Properties props = new Properties();
-        props.put(PROPERTY_KEY_USERNAME, "foo");
-        props.put(PROPERTY_KEY_PUBLICKEY, "");
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, "foo");
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, "");
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
 
         createAuthorizationProcessor().updated(props);
     }
 
     /**
-     * Tests that updated throws an exception for missing "key.username" property.
+     * Tests that updated throws an exception for missing "username lookup key" property.
      */
     @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
-    public void testUpdatedDoesNotAcceptEmptyKeyUsername() throws ConfigurationException {
+    public void testUpdatedDoesNotAcceptEmptyLookupKey() throws ConfigurationException {
         Properties props = new Properties();
-        props.put(PROPERTY_KEY_USERNAME, "");
-        props.put(PROPERTY_KEY_PUBLICKEY, "foo");
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, "");
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, "foo");
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
 
         createAuthorizationProcessor().updated(props);
     }
 
     /**
-     * Tests that updated throws an exception for missing "key.password" property.
+     * Tests that updated throws an exception for missing "verify cert validity" property.
      */
     @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
-    public void testUpdatedDoesNotAcceptMissingKeyPassword() throws ConfigurationException {
+    public void testUpdatedDoesNotAcceptEmptyVerifyCertValidity() throws ConfigurationException {
         Properties props = new Properties();
-        props.put(PROPERTY_KEY_USERNAME, "foo");
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, "foo");
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, "bar");
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "");
 
         createAuthorizationProcessor().updated(props);
     }
 
     /**
-     * Tests that updated throws an exception for missing "key.username" property.
+     * Tests that updated throws an exception for missing "username match policy" property.
      */
     @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
-    public void testUpdatedDoesNotAcceptMissingKeyUsername() throws ConfigurationException {
+    public void testUpdatedDoesNotAcceptMissingMatchPolicy() throws ConfigurationException {
         Properties props = new Properties();
-        props.put(PROPERTY_KEY_PUBLICKEY, "foo");
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, "foo");
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
+
+        createAuthorizationProcessor().updated(props);
+    }
+
+    /**
+     * Tests that updated throws an exception for missing "user name lookup key" property.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptMissingUsernameLookupKey() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, "foo");
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
+
+        createAuthorizationProcessor().updated(props);
+    }
+
+    /**
+     * Tests that updated throws an exception for missing "verify cert validity" property.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptMissingVerifyCertValidity() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, "foo");
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, "foo");
 
         createAuthorizationProcessor().updated(props);
     }
@@ -378,6 +434,33 @@ public class ClientCertAuthenticationPro
     }
 
     /**
+     * Creates a new (valid) chain with certificate(s) valid from yesterday until tomorrow.
+     * 
+     * @param dns the distinguished names of the certificates in the returned chain.
+     * @return a new chain with {@link X509Certificate}s, never <code>null</code>.
+     */
+    private X509Certificate[] createValidCertificateChainWithDN(String... dns) {
+        X509Certificate[] result = new X509Certificate[dns.length];
+        
+        X500Principal signerDN = m_keystore.getCA_DN();
+        KeyPair signerKeyPair = m_keystore.getCA_KeyPair();
+
+        for (int i = 0; i < result.length; i++) {
+            KeyPair certKeyPair = m_keystore.generateKeyPair();
+            
+            String alias = String.format("alias%d", i);
+            String dn = dns[i];
+            int idx = result.length - i - 1;
+            
+            result[idx] = m_keystore.createCertificate(signerDN, signerKeyPair.getPrivate(), alias, dn, yesterday(), tomorrow(), certKeyPair.getPublic());
+            
+            signerDN = result[idx].getSubjectX500Principal();
+            signerKeyPair = certKeyPair;
+        }
+        return result;
+    }
+
+    /**
      * Creates a new (valid) certificate valid from yesterday until tomorrow.
      * 
      * @param name the (common) name of the certificate;

Modified: ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/MemoryKeyStore.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/MemoryKeyStore.java?rev=1332609&r1=1332608&r2=1332609&view=diff
==============================================================================
--- ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/MemoryKeyStore.java (original)
+++ ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/MemoryKeyStore.java Tue May  1 10:35:05 2012
@@ -22,6 +22,7 @@ package org.apache.ace.authenticationpro
 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;
@@ -34,7 +35,7 @@ import org.bouncycastle.x509.X509V1Certi
  * Provides a memory-only certificate keystore.
  */
 final class MemoryKeyStore {
-    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA"; // MD5withRSA
+    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
 
     private final X509V1CertificateGenerator m_certGen = new X509V1CertificateGenerator();
     private final KeyPair m_caKey;
@@ -59,13 +60,27 @@ final class MemoryKeyStore {
             throw new RuntimeException(e);
         }
     }
+    
+    /**
+     * @return the {@link KeyPair} of the CA, never <code>null</code>.
+     */
+    public KeyPair getCA_KeyPair() {
+        return m_caKey;
+    }
+
+    /**
+     * @return
+     */
+    public X500Principal getCA_DN() {
+        return m_rootCert.getIssuerX500Principal();
+    }
 
     /**
-     * Generates a new 512-bit keypair.
+     * Generates a new 1024-bit keypair.
      * 
      * @return a new {@link KeyPair}, never <code>null</code>.
      */
-    public final KeyPair generateKeyPair() {
+    public KeyPair generateKeyPair() {
         try {
             return m_generator.generateKeyPair();
         }
@@ -78,19 +93,26 @@ final class MemoryKeyStore {
      * @throws IllegalStateException if an internal exception occurs.
      * @throws IllegalArgumentException if the alias already exists.
      */
-    public X509Certificate createCertificate(String alias, String name, Date before, Date after, PublicKey key)
-        throws IllegalStateException, IllegalArgumentException {
+    public X509Certificate createCertificate(String alias, String name, Date before, Date after, PublicKey key) throws IllegalArgumentException {
+        return createCertificate(getCA_DN(), m_caKey.getPrivate(), alias, name, before, after, key);
+    }
+
+    /**
+     * @throws IllegalStateException if an internal exception occurs.
+     * @throws IllegalArgumentException if the alias already exists.
+     */
+    public X509Certificate createCertificate(X500Principal issuerDN, PrivateKey issuerKey, String alias, String name, Date notBefore, Date notAfter, PublicKey key) throws IllegalArgumentException {
         try {
             m_certGen.reset();
             m_certGen.setSerialNumber(BigInteger.valueOf(++m_serial));
-            m_certGen.setIssuerDN(m_rootCert.getIssuerX500Principal());
-            m_certGen.setNotBefore(before);
-            m_certGen.setNotAfter(after);
+            m_certGen.setIssuerDN(issuerDN);
+            m_certGen.setNotBefore(notBefore);
+            m_certGen.setNotAfter(notAfter);
             m_certGen.setSubjectDN(new X500Principal(name));
             m_certGen.setPublicKey(key);
             m_certGen.setSignatureAlgorithm(SIGNATURE_ALGORITHM);
 
-            X509Certificate cert = m_certGen.generate(m_caKey.getPrivate());
+            X509Certificate cert = m_certGen.generate(issuerKey);
 
             return cert;
         }

Modified: ace/trunk/ace-connectionfactory/pom.xml
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/pom.xml?rev=1332609&r1=1332608&r2=1332609&view=diff
==============================================================================
--- ace/trunk/ace-connectionfactory/pom.xml (original)
+++ ace/trunk/ace-connectionfactory/pom.xml Tue May  1 10:35:05 2012
@@ -43,6 +43,7 @@
     <properties>
         <import.package>
         	!org.junit,
+        	javax.net.ssl,
         	org.apache.ace.connectionfactory;version=${project.version},
         	org.osgi.framework,
         	org.osgi.service.cm,

Modified: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImpl.java?rev=1332609&r1=1332608&r2=1332609&view=diff
==============================================================================
--- ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImpl.java (original)
+++ ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImpl.java Tue May  1 10:35:05 2012
@@ -28,6 +28,9 @@ import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+
 import org.apache.ace.connectionfactory.ConnectionFactory;
 import org.apache.ace.connectionfactory.impl.UrlCredentials.AuthType;
 import org.apache.ace.connectionfactory.impl.UrlCredentialsFactory.MissingValueException;
@@ -37,7 +40,7 @@ import org.osgi.service.cm.ManagedServic
 import org.osgi.service.useradmin.User;
 
 /**
- * Provides a default implementation for {@link ConnectionFactory} based on the standard <code>java.net</code> 
+ * Provides a default implementation for {@link ConnectionFactory} based on the standard <code>java.net</code>
  * implementation of {@link URLConnection}.
  */
 public class ConnectionFactoryImpl implements ConnectionFactory, ManagedServiceFactory {
@@ -45,9 +48,9 @@ public class ConnectionFactoryImpl imple
     public static final String FACTORY_PID = "org.apache.ace.connectionfactory";
 
     private static final String HTTP_HEADER_AUTHORIZATION = "Authorization";
-    
+
     private final Map<String /* config PID */, UrlCredentials> m_credentialMapping;
-    
+
     /**
      * Creates a new {@link ConnectionFactoryImpl}.
      */
@@ -122,7 +125,7 @@ public class ConnectionFactoryImpl imple
 
         try {
             creds = UrlCredentialsFactory.getCredentials(properties);
-            
+
             synchronized (m_credentialMapping) {
                 m_credentialMapping.put(pid, creds);
             }
@@ -131,12 +134,12 @@ public class ConnectionFactoryImpl imple
             throw new ConfigurationException(e.getProperty(), e.getMessage());
         }
     }
-    
+
     /**
      * Returns the credentials to access the given URL.
      * 
      * @param url the URL to find the credentials for, cannot be <code>null</code>.
-     * @return a {@link UrlCredentials} instance for the given URL, or <code>null</code> 
+     * @return a {@link UrlCredentials} instance for the given URL, or <code>null</code>
      *         if none were found, or if none were necessary.
      */
     final UrlCredentials getCredentials(URL url) {
@@ -150,20 +153,19 @@ public class ConnectionFactoryImpl imple
                 return c;
             }
         }
-        
+
         return null;
     }
 
     /**
      * Returns the authorization header for HTTP Basic Authentication.
      * 
-     * @param creds the credentials to supply.
+     * @param values the credential values to supply, cannot be <code>null</code> and should be an array of two elements.
      * @return a string that denotes the basic authentication header ("Basic " + encoded credentials), never <code>null</code>.
      */
-    final String getBasicAuthCredentials(UrlCredentials creds) {
-        final Object[] values = creds.getCredentials();
-        if (values.length < 2) {
-            throw new IllegalArgumentException("Insufficient credentials passed! Expected 2 values, got " + values.length + " values.");
+    final String getBasicAuthCredentials(Object[] values) {
+        if ((values == null) || values.length < 2) {
+            throw new IllegalArgumentException("Insufficient credentials passed: expected 2 values!");
         }
 
         StringBuilder sb = new StringBuilder();
@@ -185,19 +187,45 @@ public class ConnectionFactoryImpl imple
     }
 
     /**
+     * Applies basic authentication to the given connection, if it is a {@link HttpURLConnection}.
+     * 
+     * @param conn the connection to apply basic authentication to;
+     * @param values the credentials to apply.
+     */
+    private void applyBasicAuthentication(URLConnection conn, Object[] values) {
+        if (conn instanceof HttpURLConnection) {
+            conn.setRequestProperty(HTTP_HEADER_AUTHORIZATION, getBasicAuthCredentials(values));
+        }
+    }
+
+    /**
+     * Applies the use of client certificates to the given connection, if it a {@link HttpsURLConnection}.
+     * 
+     * @param conn the connection to apply client certs to;
+     * @param values the credentials to apply.
+     */
+    private void applyClientCertificate(URLConnection conn, Object[] values) {
+        if (conn instanceof HttpsURLConnection) {
+            ((HttpsURLConnection) conn).setSSLSocketFactory(((SSLContext) values[0]).getSocketFactory());
+        }
+    }
+
+    /**
      * Supplies the actual credentials to the given {@link URLConnection}.
      * 
      * @param conn the connection to supply the credentials to, cannot be <code>null</code>;
-     * @param creds the credentials to supply, cannot be <code>null</code>.
+     * @param urlCreds the URL credentials to supply, cannot be <code>null</code>.
      * @throws IOException in case of I/O problems.
      */
-    private void supplyCredentials(URLConnection conn, UrlCredentials creds) throws IOException {
-        final AuthType type = creds.getType();
-        
+    private void supplyCredentials(URLConnection conn, UrlCredentials urlCreds) throws IOException {
+        final AuthType type = urlCreds.getType();
+        final Object[] creds = urlCreds.getCredentials();
+
         if (AuthType.BASIC.equals(type)) {
-            if (conn instanceof HttpURLConnection) {
-                conn.setRequestProperty(HTTP_HEADER_AUTHORIZATION, getBasicAuthCredentials(creds));
-            }
+            applyBasicAuthentication(conn, creds);
+        }
+        else if (AuthType.CLIENT_CERT.equals(type)) {
+            applyClientCertificate(conn, creds);
         }
         else if (!AuthType.NONE.equals(type)) {
             throw new IllegalArgumentException("Unknown authentication type: " + type);

Modified: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentials.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentials.java?rev=1332609&r1=1332608&r2=1332609&view=diff
==============================================================================
--- ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentials.java (original)
+++ ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentials.java Tue May  1 10:35:05 2012
@@ -31,7 +31,9 @@ final class UrlCredentials {
         /** Indicates no authentication. */
         NONE,
         /** Indicates basic HTTP authentication. */
-        BASIC;
+        BASIC,
+        /** Indicates the use of client certificates. */
+        CLIENT_CERT;
     }
 
     private final AuthType m_type;

Modified: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactory.java?rev=1332609&r1=1332608&r2=1332609&view=diff
==============================================================================
--- ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactory.java (original)
+++ ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactory.java Tue May  1 10:35:05 2012
@@ -19,10 +19,23 @@
 
 package org.apache.ace.connectionfactory.impl;
 
+import java.io.Closeable;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.SecureRandom;
 import java.util.Dictionary;
 
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
 import org.apache.ace.connectionfactory.impl.UrlCredentials.AuthType;
 
 /**
@@ -63,6 +76,10 @@ final class UrlCredentialsFactory {
     public static final String KEY_AUTH_TYPE = "authentication.type";
     public static final String KEY_AUTH_USER_NAME = "authentication.user.name";
     public static final String KEY_AUTH_USER_PASSWORD = "authentication.user.password";
+    public static final String KEY_AUTH_KEYSTORE_FILE = "authentication.keystore.file";
+    public static final String KEY_AUTH_KEYSTORE_PASS = "authentication.keystore.storepass";
+    public static final String KEY_AUTH_TRUSTSTORE_FILE = "authentication.truststore.file";
+    public static final String KEY_AUTH_TRUSTSTORE_PASS = "authentication.truststore.storepass";
     
     /**
      * Not used.
@@ -130,6 +147,47 @@ final class UrlCredentialsFactory {
             }
 
             creds = new Object[] { userName, password };
+        } else if (AuthType.CLIENT_CERT.equals(type)) {
+            String keystoreFile = getStringProperty(props, prefix.concat(KEY_AUTH_KEYSTORE_FILE));
+            String keystorePass = getStringProperty(props, prefix.concat(KEY_AUTH_KEYSTORE_PASS));
+            if ((keystoreFile != null) && (keystorePass == null)) {
+                throw new MissingValueException(prefix.concat(KEY_AUTH_KEYSTORE_PASS));
+            }
+            if ((keystoreFile == null) && (keystorePass != null)) {
+                throw new MissingValueException(prefix.concat(KEY_AUTH_KEYSTORE_FILE));
+            }
+            
+            String truststoreFile = getStringProperty(props, prefix.concat(KEY_AUTH_TRUSTSTORE_FILE));            
+            String truststorePass = getStringProperty(props, prefix.concat(KEY_AUTH_TRUSTSTORE_PASS));
+            if ((truststoreFile != null) && (truststorePass == null)) {
+                throw new MissingValueException(prefix.concat(KEY_AUTH_TRUSTSTORE_PASS));
+            }
+            if ((truststoreFile == null) && (truststorePass != null)) {
+                throw new MissingValueException(prefix.concat(KEY_AUTH_TRUSTSTORE_FILE));
+            }
+
+            if ((keystoreFile == null) && (truststoreFile == null)) {
+                try {
+                    // No configuration given; use the system-wide defaults...
+                    creds = new Object[] { SSLContext.getDefault() };
+                }
+                catch (Exception e) {
+                    throw new IllegalArgumentException("Failed to obtain SSL context!", e);
+                }
+            } else {
+                try {
+                    KeyManager[] keyManagers = getKeyManagerFactory(keystoreFile, keystorePass);
+                    TrustManager[] trustManagers = getTrustManagerFactory(truststoreFile, truststorePass);
+
+                    SSLContext context = SSLContext.getInstance("TLS");
+                    context.init(keyManagers, trustManagers, new SecureRandom());
+
+                    creds = new Object[] { context };
+                }
+                catch (Exception e) {
+                    throw new IllegalArgumentException("Failed to load keystore!", e);
+                }
+            }
         } else {
             throw new IllegalArgumentException("Invalid/unhandled authentication type: " + authType);
         }
@@ -146,9 +204,78 @@ final class UrlCredentialsFactory {
         }
         return null;
     }
-    
+
     private static String getStringProperty(Dictionary dict, String key, String defaultValue) {
         String value = getStringProperty(dict, key);
         return (value == null) ? defaultValue : value;
     }
+
+    /**
+     * @param keystoreFile
+     * @param storePass
+     * @return
+     * @throws IOException
+     * @throws GeneralSecurityException
+     */
+    private static KeyManager[] getKeyManagerFactory(String keystoreFile, String storePass) throws IOException, GeneralSecurityException {
+        if (keystoreFile == null) {
+            return null;
+        }
+
+        InputStream keyInput = null;
+        try {
+            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+            
+            keyInput = new FileInputStream(keystoreFile);
+            
+            keyStore.load(keyInput, storePass.toCharArray());
+            
+            keyManagerFactory.init(keyStore, storePass.toCharArray());
+            return keyManagerFactory.getKeyManagers();
+        }
+        finally {
+            closeSafely(keyInput);
+        }
+    }
+
+    /**
+     * @param truststoreFile
+     * @param storePass
+     * @return
+     * @throws IOException
+     * @throws GeneralSecurityException
+     */
+    private static TrustManager[] getTrustManagerFactory(String truststoreFile, String storePass) throws IOException, GeneralSecurityException {
+        if (truststoreFile == null) {
+            return null;
+        }
+
+        InputStream trustInput = null;
+        try {
+            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+            
+            trustInput = new FileInputStream(truststoreFile);
+            
+            trustStore.load(trustInput, storePass.toCharArray());
+            
+            trustManagerFactory.init(trustStore);
+            return trustManagerFactory.getTrustManagers();
+        }
+        finally {
+            closeSafely(trustInput);
+        }
+    }
+    
+    private static void closeSafely(Closeable resource) {
+        try {
+            if (resource != null) {
+                resource.close();
+            }
+        }
+        catch (IOException e) {
+            // Ignore; nothing we can/will do...
+        }
+    }
 }

Modified: ace/trunk/ace-connectionfactory/src/test/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImplTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/src/test/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImplTest.java?rev=1332609&r1=1332608&r2=1332609&view=diff
==============================================================================
--- ace/trunk/ace-connectionfactory/src/test/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImplTest.java (original)
+++ ace/trunk/ace-connectionfactory/src/test/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImplTest.java Tue May  1 10:35:05 2012
@@ -99,11 +99,11 @@ public class ConnectionFactoryImplTest {
         Properties props = createBasicAuthConfig(TEST_URL.toExternalForm());
 
         connFactory.updated("pid1", props);
-        
+
         UrlCredentials credentials = connFactory.getCredentials(TEST_URL);
         assert credentials != null : "Expected valid credentials to be found!";
 
-        String header = new ConnectionFactoryImpl().getBasicAuthCredentials(credentials);
+        String header = new ConnectionFactoryImpl().getBasicAuthCredentials(credentials.getCredentials());
         assert header != null : "Expected valid HTTP header to be returned!";
         assert header.equals(header.trim()) : "Expected HTTP header not to contain any leading/trailing whitespace!";
         assert "Basic Zm9vOmJhcg==".equals(header) : "Expected HTTP header to be constant!";

Modified: ace/trunk/ace-connectionfactory/src/test/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactoryTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/src/test/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactoryTest.java?rev=1332609&r1=1332608&r2=1332609&view=diff
==============================================================================
--- ace/trunk/ace-connectionfactory/src/test/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactoryTest.java (original)
+++ ace/trunk/ace-connectionfactory/src/test/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactoryTest.java Tue May  1 10:35:05 2012
@@ -48,6 +48,87 @@ public class UrlCredentialsFactoryTest {
      * Test method for {@link org.apache.ace.connectionfactory.impl.UrlCredentialsFactory#getCredentials(java.util.Dictionary)}.
      */
     @Test(groups = { UNIT }, expectedExceptions = MissingValueException.class)
+    public void testGetCredentialsWithDictionaryClientCertTypeMissingKeystorePasswordFail() {
+        Properties props = new Properties();
+        props.put(UrlCredentialsFactory.KEY_AUTH_BASE_URL, "http://localhost:8080/");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TYPE, "client_cert");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TRUSTSTORE_FILE, "bar");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TRUSTSTORE_PASS, "qux");
+        props.put(UrlCredentialsFactory.KEY_AUTH_KEYSTORE_FILE, "foo");
+
+        UrlCredentialsFactory.getCredentials(props);
+    }
+
+    /**
+     * Test method for {@link org.apache.ace.connectionfactory.impl.UrlCredentialsFactory#getCredentials(java.util.Dictionary)}.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = MissingValueException.class)
+    public void testGetCredentialsWithDictionaryClientCertTypeMissingKeystoreFileFail() {
+        Properties props = new Properties();
+        props.put(UrlCredentialsFactory.KEY_AUTH_BASE_URL, "http://localhost:8080/");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TYPE, "client_cert");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TRUSTSTORE_FILE, "bar");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TRUSTSTORE_PASS, "qux");
+        props.put(UrlCredentialsFactory.KEY_AUTH_KEYSTORE_PASS, "foo");
+
+        UrlCredentialsFactory.getCredentials(props);
+    }
+
+    /**
+     * Test method for {@link org.apache.ace.connectionfactory.impl.UrlCredentialsFactory#getCredentials(java.util.Dictionary)}.
+     */
+    @Test(groups = { UNIT })
+    public void testGetCredentialsWithDictionaryClientCertTypeOk() {
+        Properties props = new Properties();
+        props.put(UrlCredentialsFactory.KEY_AUTH_BASE_URL, "http://localhost:8080/");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TYPE, "client_cert");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TRUSTSTORE_FILE, "foo");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TRUSTSTORE_PASS, "bar");
+        props.put(UrlCredentialsFactory.KEY_AUTH_KEYSTORE_FILE, "qux");
+        props.put(UrlCredentialsFactory.KEY_AUTH_KEYSTORE_PASS, "quu");
+
+        try {
+            UrlCredentialsFactory.getCredentials(props);
+        }
+        catch (IllegalArgumentException e) {
+            // Ok; expected as the implementation tries to open the files "foo" and "qux"...
+        }
+    }
+
+    /**
+     * Test method for {@link org.apache.ace.connectionfactory.impl.UrlCredentialsFactory#getCredentials(java.util.Dictionary)}.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = MissingValueException.class)
+    public void testGetCredentialsWithDictionaryClientCertTypeMissingTruststorePasswordFail() {
+        Properties props = new Properties();
+        props.put(UrlCredentialsFactory.KEY_AUTH_BASE_URL, "http://localhost:8080/");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TYPE, "client_cert");
+        props.put(UrlCredentialsFactory.KEY_AUTH_KEYSTORE_FILE, "bar");
+        props.put(UrlCredentialsFactory.KEY_AUTH_KEYSTORE_PASS, "qux");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TRUSTSTORE_FILE, "foo");
+
+        UrlCredentialsFactory.getCredentials(props);
+    }
+
+    /**
+     * Test method for {@link org.apache.ace.connectionfactory.impl.UrlCredentialsFactory#getCredentials(java.util.Dictionary)}.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = MissingValueException.class)
+    public void testGetCredentialsWithDictionaryClientCertTypeMissingTruststoreFileFail() {
+        Properties props = new Properties();
+        props.put(UrlCredentialsFactory.KEY_AUTH_BASE_URL, "http://localhost:8080/");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TYPE, "client_cert");
+        props.put(UrlCredentialsFactory.KEY_AUTH_KEYSTORE_FILE, "bar");
+        props.put(UrlCredentialsFactory.KEY_AUTH_KEYSTORE_PASS, "qux");
+        props.put(UrlCredentialsFactory.KEY_AUTH_TRUSTSTORE_PASS, "foo");
+
+        UrlCredentialsFactory.getCredentials(props);
+    }
+
+    /**
+     * Test method for {@link org.apache.ace.connectionfactory.impl.UrlCredentialsFactory#getCredentials(java.util.Dictionary)}.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = MissingValueException.class)
     public void testGetCredentialsWithDictionaryBasicTypeMissingUserNameFail() {
         Properties props = new Properties();
         props.put(UrlCredentialsFactory.KEY_AUTH_BASE_URL, "http://localhost:8080/");



Mime
View raw message