- Introduce PKI utils
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 10 Sep 2015 21:38:53 +0000 (21:38 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 10 Sep 2015 21:38:53 +0000 (21:38 +0000)
- Kernel login
- Remove optimizations and improve LDIF/LDAP user admin

git-svn-id: https://svn.argeo.org/commons/trunk@8381 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

20 files changed:
org.argeo.cms/src/org/argeo/cms/KernelHeader.java
org.argeo.cms/src/org/argeo/cms/internal/auth/ImpliedByPrincipal.java
org.argeo.cms/src/org/argeo/cms/internal/auth/KernelLoginModule.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/auth/UserAdminLoginModule.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeAuthorization.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg
org.argeo.security.core/src/org/argeo/osgi/auth/BuncleContextCallbackHander.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/auth/BundleContextCallbackHander.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractLdapUserAdmin.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapNames.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifAuthorization.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifGroup.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java
org.argeo.security.core/src/org/argeo/security/crypto/PkiUtils.java [new file with mode: 0644]

index 649fc9c0450607947972c821c55180b69e84bc70..f0e738a60988c899ebe457db62fd69bb7a86afee 100644 (file)
@@ -9,7 +9,8 @@ public interface KernelHeader {
        final static String LOGIN_CONTEXT_SINGLE_USER = "SINGLE_USER";
 
        // RESERVED ROLES
-       final static String ROLES_BASEDN = "ou=roles,ou=node";
+       public final static String ROLE_KERNEL = "OU=node";
+       public final static String ROLES_BASEDN = "ou=roles,ou=node";
        public final static String ROLE_ADMIN = "cn=admin," + ROLES_BASEDN;
        public final static String ROLE_GROUP_ADMIN = "cn=groupAdmin,"
                        + ROLES_BASEDN;
index 829c17e35d78be824cc5913445fcbe020cb1b952..6f83a9a28171d4f8d7d8ae3a97f1ca3d03891e87 100644 (file)
@@ -85,6 +85,7 @@ public final class ImpliedByPrincipal implements Group {
 
        @Override
        public String toString() {
-               return name.toString() + " implied by " + causes;
+               // return name.toString() + " implied by " + causes;
+               return name.toString();
        }
 }
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/KernelLoginModule.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/KernelLoginModule.java
new file mode 100644 (file)
index 0000000..ee36d35
--- /dev/null
@@ -0,0 +1,91 @@
+package org.argeo.cms.internal.auth;
+
+import java.security.Principal;
+import java.security.cert.CertPath;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+import javax.security.auth.x500.X500Principal;
+import javax.security.auth.x500.X500PrivateCredential;
+
+import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
+import org.argeo.cms.KernelHeader;
+
+public class KernelLoginModule implements LoginModule {
+       private Subject subject;
+
+       @Override
+       public void initialize(Subject subject, CallbackHandler callbackHandler,
+                       Map<String, ?> sharedState, Map<String, ?> options) {
+               this.subject = subject;
+       }
+
+       @Override
+       public boolean login() throws LoginException {
+               // TODO check permission at code level
+               return true;
+       }
+
+       @Override
+       public boolean commit() throws LoginException {
+               // Check that kernel has been logged in w/ certificate
+               // Name
+               Set<X500Principal> names = subject.getPrincipals(X500Principal.class);
+               if (names.isEmpty() || names.size() > 1)
+                       throw new LoginException("Kernel must have been named");
+               X500Principal name = names.iterator().next();
+               if (!KernelHeader.ROLE_KERNEL.equals(name.getName()))
+                       throw new LoginException("Kernel must be named named "
+                                       + KernelHeader.ROLE_KERNEL);
+               // Private certificate
+               Set<X500PrivateCredential> privateCerts = subject
+                               .getPrivateCredentials(X500PrivateCredential.class);
+               X500PrivateCredential privateCert = null;
+               for (X500PrivateCredential pCert : privateCerts) {
+                       if (pCert.getCertificate().getSubjectX500Principal().equals(name)) {
+                               privateCert = pCert;
+                       }
+               }
+               if (privateCert == null)
+                       throw new LoginException("Kernel must have a private certificate");
+               // Certificate path
+               Set<CertPath> certPaths = subject.getPublicCredentials(CertPath.class);
+               CertPath certPath = null;
+               for (CertPath cPath : certPaths) {
+                       if (cPath.getCertificates().get(0)
+                                       .equals(privateCert.getCertificate())) {
+                               certPath = cPath;
+                       }
+               }
+               if (certPath == null)
+                       throw new LoginException("Kernel must have a certificate path");
+
+               Set<Principal> principals = subject.getPrincipals();
+               // Add admin roles
+
+               // Add data access roles
+               principals.add(new AdminPrincipal(SecurityConstants.ADMIN_ID));
+
+               return true;
+       }
+
+       @Override
+       public boolean abort() throws LoginException {
+               return true;
+       }
+
+       @Override
+       public boolean logout() throws LoginException {
+               // clear everything
+               subject.getPrincipals().clear();
+               subject.getPublicCredentials().clear();
+               subject.getPrivateCredentials().clear();
+               return true;
+       }
+
+}
index f598515217d6dbce821fd4bba5e4181cfdce699b..16a7d7265212777515aeae360c5c1d360b0c39d4 100644 (file)
@@ -40,18 +40,19 @@ public class UserAdminLoginModule implements LoginModule {
        private CallbackHandler callbackHandler;
        private boolean isAnonymous = false;
 
-       private final static LdapName ROLE_ADMIN_NAME, ROLE_USER_NAME,
-                       ROLE_ANONYMOUS_NAME;
+       private final static LdapName ROLE_KERNEL_NAME, ROLE_ADMIN_NAME,
+                       ROLE_ANONYMOUS_NAME, ROLE_USER_NAME;
        private final static List<LdapName> RESERVED_ROLES;
        private final static X500Principal ROLE_ANONYMOUS_PRINCIPAL;
        static {
                try {
+                       ROLE_KERNEL_NAME = new LdapName(KernelHeader.ROLE_KERNEL);
                        ROLE_ADMIN_NAME = new LdapName(KernelHeader.ROLE_ADMIN);
                        ROLE_USER_NAME = new LdapName(KernelHeader.ROLE_USER);
                        ROLE_ANONYMOUS_NAME = new LdapName(KernelHeader.ROLE_ANONYMOUS);
                        RESERVED_ROLES = Collections.unmodifiableList(Arrays
-                                       .asList(new LdapName[] { ROLE_ANONYMOUS_NAME,
-                                                       ROLE_USER_NAME, ROLE_ADMIN_NAME,
+                                       .asList(new LdapName[] { ROLE_KERNEL_NAME, ROLE_ADMIN_NAME,
+                                                       ROLE_ANONYMOUS_NAME, ROLE_USER_NAME,
                                                        new LdapName(KernelHeader.ROLE_GROUP_ADMIN),
                                                        new LdapName(KernelHeader.ROLE_USER_ADMIN) }));
                        ROLE_ANONYMOUS_PRINCIPAL = new X500Principal(
@@ -218,7 +219,8 @@ public class UserAdminLoginModule implements LoginModule {
 
        private void checkImpliedPrincipalName(LdapName roleName) {
                if (ROLE_USER_NAME.equals(roleName)
-                               || ROLE_ANONYMOUS_NAME.equals(roleName))
+                               || ROLE_ANONYMOUS_NAME.equals(roleName)
+                               || ROLE_KERNEL_NAME.equals(roleName))
                        throw new CmsException(roleName + " cannot be listed as role");
        }
 }
index 67f0c3737caab318e3acd0c8a28d0e207ffe0760..08697503916bfca8c38f154d76d65915c3970e0a 100644 (file)
@@ -1,16 +1,27 @@
 package org.argeo.cms.internal.kernel;
 
+import java.io.File;
+import java.io.IOException;
 import java.lang.management.ManagementFactory;
 import java.net.URL;
+import java.security.KeyStore;
 import java.security.PrivilegedAction;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
 import javax.jcr.Repository;
 import javax.jcr.RepositoryFactory;
 import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
+import javax.security.auth.x500.X500Principal;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -21,6 +32,7 @@ import org.argeo.cms.KernelHeader;
 import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory;
 import org.argeo.jcr.ArgeoJcrConstants;
 import org.argeo.security.core.InternalAuthentication;
+import org.argeo.security.crypto.PkiUtils;
 import org.eclipse.equinox.http.servlet.ExtendedHttpService;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceEvent;
@@ -41,6 +53,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
  * </ul>
  */
 final class Kernel implements ServiceListener {
+
        private final static Log log = LogFactory.getLog(Kernel.class);
 
        private final BundleContext bundleContext = Activator.getBundleContext();
@@ -59,9 +72,27 @@ final class Kernel implements ServiceListener {
                                KernelConstants.JAAS_CONFIG);
                System.setProperty("java.security.auth.login.config",
                                url.toExternalForm());
+               createKeyStoreIfNeeded();
+
+               CallbackHandler cbHandler = new CallbackHandler() {
+
+                       @Override
+                       public void handle(Callback[] callbacks) throws IOException,
+                                       UnsupportedCallbackException {
+                               // alias
+                               ((NameCallback) callbacks[1]).setName(KernelHeader.ROLE_KERNEL);
+                               // store pwd
+                               ((PasswordCallback) callbacks[2]).setPassword("changeit"
+                                               .toCharArray());
+                               // key pwd
+                               ((PasswordCallback) callbacks[3]).setPassword("changeit"
+                                               .toCharArray());
+                       }
+               };
                try {
                        LoginContext kernelLc = new LoginContext(
-                                       KernelHeader.LOGIN_CONTEXT_SYSTEM, kernelSubject);
+                                       KernelConstants.LOGIN_CONTEXT_KERNEL, kernelSubject,
+                                       cbHandler);
                        kernelLc.login();
                } catch (LoginException e) {
                        throw new CmsException("Cannot log in kernel", e);
@@ -151,7 +182,7 @@ final class Kernel implements ServiceListener {
 
                try {
                        LoginContext kernelLc = new LoginContext(
-                                       KernelHeader.LOGIN_CONTEXT_SYSTEM, kernelSubject);
+                                       KernelConstants.LOGIN_CONTEXT_KERNEL, kernelSubject);
                        kernelLc.logout();
                } catch (LoginException e) {
                        throw new CmsException("Cannot log in kernel", e);
@@ -206,6 +237,25 @@ final class Kernel implements ServiceListener {
                return httpService;
        }
 
+       private void createKeyStoreIfNeeded() {
+               char[] ksPwd = "changeit".toCharArray();
+               char[] keyPwd = Arrays.copyOf(ksPwd, ksPwd.length);
+               File keyStoreFile = KernelUtils.getOsgiConfigurationFile("node.p12");
+               if (!keyStoreFile.exists()) {
+                       try {
+                               KeyStore keyStore = PkiUtils.getKeyStore(keyStoreFile, ksPwd);
+                               X509Certificate cert = PkiUtils.generateSelfSignedCertificate(
+                                               keyStore, new X500Principal(KernelHeader.ROLE_KERNEL),
+                                               keyPwd);
+                               PkiUtils.saveKeyStore(keyStoreFile, ksPwd, keyStore);
+
+                       } catch (Exception e) {
+                               throw new CmsException("Cannot create key store "
+                                               + keyStoreFile, e);
+                       }
+               }
+       }
+
        final private static void directorsCut(long initDuration) {
                // final long ms = 128l + (long) (Math.random() * 128d);
                long ms = initDuration / 100;
index a9a3e7e9af80fef8411066e885d8ac7428b028ce..63fe750be2c8693f400cfb1587112a255d932602 100644 (file)
@@ -25,6 +25,7 @@ public interface KernelConstants {
        // Security
        final static String DEFAULT_SECURITY_KEY = "argeo";
        final static String JAAS_CONFIG = "/org/argeo/cms/internal/kernel/jaas.cfg";
+       final static String LOGIN_CONTEXT_KERNEL = "KERNEL";
 
        // DAV
        final static String WEBDAV_CONFIG = "/org/argeo/cms/internal/kernel/webdav-config.xml";
index 0a9e6c53e44a2c24d7b438b526ce15ddda5f8214..912d9fa995d2c58ea38fb350361ca4c219fa5092 100644 (file)
@@ -2,6 +2,7 @@ package org.argeo.cms.internal.kernel;
 
 import java.io.File;
 import java.io.IOException;
+import java.net.URI;
 import java.util.Collections;
 import java.util.Dictionary;
 import java.util.Enumeration;
@@ -25,6 +26,7 @@ import org.springframework.security.core.userdetails.UserDetails;
 /** Package utilities */
 class KernelUtils implements KernelConstants {
        private final static String OSGI_INSTANCE_AREA = "osgi.instance.area";
+       private final static String OSGI_CONFIGURATION_AREA = "osgi.configuration.area";
 
        static Dictionary<String, ?> asDictionary(Properties props) {
                Hashtable<String, Object> hashtable = new Hashtable<String, Object>();
@@ -51,6 +53,17 @@ class KernelUtils implements KernelConstants {
                                .getAbsoluteFile();
        }
 
+       static File getOsgiConfigurationFile(String relativePath) {
+               try {
+                       return new File(new URI(Activator.getBundleContext().getProperty(
+                                       OSGI_CONFIGURATION_AREA)
+                                       + relativePath)).getCanonicalFile();
+               } catch (Exception e) {
+                       throw new CmsException("Cannot get configuration file for "
+                                       + relativePath, e);
+               }
+       }
+
        static String getFrameworkProp(String key, String def) {
                String value = Activator.getBundleContext().getProperty(key);
                if (value == null)
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeAuthorization.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeAuthorization.java
new file mode 100644 (file)
index 0000000..2846069
--- /dev/null
@@ -0,0 +1,71 @@
+package org.argeo.cms.internal.kernel;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.osgi.service.useradmin.Authorization;
+
+class NodeAuthorization implements Authorization {
+       private final String name;
+       private final String displayName;
+       private final List<String> systemRoles;
+       private final List<String> roles;
+
+       public NodeAuthorization(String name, String displayName,
+                       Collection<String> systemRoles, String[] roles) {
+               this.name = name;
+               this.displayName = displayName;
+               this.systemRoles = Collections.unmodifiableList(new ArrayList<String>(
+                               systemRoles));
+               this.roles = Collections.unmodifiableList(Arrays.asList(roles));
+       }
+
+       @Override
+       public String getName() {
+               return name;
+       }
+
+       @Override
+       public boolean hasRole(String name) {
+               if (systemRoles.contains(name))
+                       return true;
+               if (roles.contains(name))
+                       return true;
+               return false;
+       }
+
+       @Override
+       public String[] getRoles() {
+               int size = systemRoles.size() + roles.size();
+               List<String> res = new ArrayList<String>(size);
+               res.addAll(systemRoles);
+               res.addAll(roles);
+               return res.toArray(new String[size]);
+       }
+
+       @Override
+       public int hashCode() {
+               if (name == null)
+                       return super.hashCode();
+               return name.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (!(obj instanceof Authorization))
+                       return false;
+               Authorization that = (Authorization) obj;
+               if (name == null)
+                       return that.getName() == null;
+               return name.equals(that.getName());
+       }
+
+       @Override
+       public String toString() {
+               return displayName;
+       }
+
+}
index 5459a2d13cab9ab3b1d860dd98bfeecce5fb8bfc..19e52937eed8f1dc401578b9ebc11ee3403e7ce3 100644 (file)
@@ -74,15 +74,17 @@ public class NodeUserAdmin implements UserAdmin, UserAdminAggregator {
        @Override
        public Authorization getAuthorization(User user) {
                UserAdmin userAdmin = findUserAdmin(user.getName());
-               // FIXME clarify assumptions
-               return userAdmin.getAuthorization(user);
-               // String[] roles = auth.getRoles();
-               // // Gather system roles
-               // Set<String> systemRoles = new HashSet<String>();
-               // for(String businessRole:roles){
-               //
-               // }
-               // return null;
+               Authorization rawAuthorization = userAdmin.getAuthorization(user);
+               // gather system roles
+               Set<String> systemRoles = new HashSet<String>();
+               for (String role : rawAuthorization.getRoles()) {
+                       Authorization auth = nodeRoles.getAuthorization((User) userAdmin
+                                       .getRole(role));
+                       systemRoles.addAll(Arrays.asList(auth.getRoles()));
+               }
+               return new NodeAuthorization(rawAuthorization.getName(),
+                               rawAuthorization.toString(), systemRoles,
+                               rawAuthorization.getRoles());
        }
 
        //
index 783d1f71fe05cd4511ee602c281cfd8a968ddb15..8cd11ba447cc481dbbca80d776db1c3ebd2d85f4 100644 (file)
@@ -20,6 +20,12 @@ SYSTEM {
     org.argeo.security.core.SystemLoginModule requisite;
 };
 
+KERNEL {
+    com.sun.security.auth.module.UnixLoginModule requisite;
+    com.sun.security.auth.module.KeyStoreLoginModule requisite keyStoreURL="${osgi.configuration.area}/node.p12" keyStoreType=PKCS12 keyStoreProvider=BC;
+    org.argeo.cms.internal.auth.KernelLoginModule requisite;
+};
+
 OLD_SYSTEM {
     org.argeo.cms.internal.auth.SystemLoginModule requisite;
     org.springframework.security.authentication.jaas.SecurityContextLoginModule requisite;
diff --git a/org.argeo.security.core/src/org/argeo/osgi/auth/BuncleContextCallbackHander.java b/org.argeo.security.core/src/org/argeo/osgi/auth/BuncleContextCallbackHander.java
deleted file mode 100644 (file)
index 08cf37c..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.argeo.osgi.auth;
-
-import java.io.IOException;
-
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.UnsupportedCallbackException;
-
-import org.osgi.framework.BundleContext;
-
-public class BuncleContextCallbackHander implements CallbackHandler {
-       private final BundleContext bundleContext;
-
-       public BuncleContextCallbackHander(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-       @Override
-       public void handle(Callback[] callbacks) throws IOException,
-                       UnsupportedCallbackException {
-               for (Callback callback : callbacks) {
-                       if (!(callback instanceof BundleContextCallback))
-                               throw new UnsupportedCallbackException(callback);
-                       ((BundleContextCallback) callback).setBundleContext(bundleContext);
-               }
-
-       }
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/auth/BundleContextCallbackHander.java b/org.argeo.security.core/src/org/argeo/osgi/auth/BundleContextCallbackHander.java
new file mode 100644 (file)
index 0000000..60510b5
--- /dev/null
@@ -0,0 +1,29 @@
+package org.argeo.osgi.auth;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.osgi.framework.BundleContext;
+
+public class BundleContextCallbackHander implements CallbackHandler {
+       private final BundleContext bundleContext;
+
+       public BundleContextCallbackHander(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+       @Override
+       public void handle(Callback[] callbacks) throws IOException,
+                       UnsupportedCallbackException {
+               for (Callback callback : callbacks) {
+                       if (!(callback instanceof BundleContextCallback))
+                               throw new UnsupportedCallbackException(callback);
+                       ((BundleContextCallback) callback).setBundleContext(bundleContext);
+               }
+
+       }
+
+}
index 254e5423c272e2972ceb98eeb9961cf81eb747e7..8dcd6c216bff7c3963d01a7879bfe258877de4fd 100644 (file)
@@ -1,9 +1,13 @@
 package org.argeo.osgi.useradmin;
 
 import java.net.URI;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
 import org.osgi.service.useradmin.UserAdmin;
 
 public abstract class AbstractLdapUserAdmin implements UserAdmin {
@@ -11,6 +15,8 @@ public abstract class AbstractLdapUserAdmin implements UserAdmin {
        private URI uri;
 
        private UserAdmin externalRoles;
+       private List<String> indexedUserProperties = Arrays.asList(new String[] {
+                       "uid", "mail", "cn" });
 
        public AbstractLdapUserAdmin() {
        }
@@ -20,8 +26,38 @@ public abstract class AbstractLdapUserAdmin implements UserAdmin {
                this.isReadOnly = isReadOnly;
        }
 
-       private List<String> indexedUserProperties = Arrays.asList(new String[] {
-                       "uid", "mail", "cn" });
+       public void init() {
+
+       }
+
+       public void destroy() {
+
+       }
+
+       /** Returns the {@link Group}s this user is a direct member of. */
+       protected abstract List<? extends Group> getDirectGroups(User user);
+
+       List<Role> getAllRoles(User user) {
+               List<Role> allRoles = new ArrayList<Role>();
+               if (user != null) {
+                       collectRoles(user, allRoles);
+                       allRoles.add(user);
+               } else
+                       collectAnonymousRoles(allRoles);
+               return allRoles;
+       }
+
+       private void collectRoles(User user, List<Role> allRoles) {
+               for (Group group : getDirectGroups(user)) {
+                       // TODO check for loops
+                       allRoles.add(group);
+                       collectRoles(group, allRoles);
+               }
+       }
+
+       private void collectAnonymousRoles(List<Role> allRoles) {
+               // TODO gather anonymous roles
+       }
 
        protected URI getUri() {
                return uri;
@@ -47,14 +83,6 @@ public abstract class AbstractLdapUserAdmin implements UserAdmin {
                return isReadOnly;
        }
 
-       public void init() {
-
-       }
-
-       public void destroy() {
-
-       }
-
        UserAdmin getExternalRoles() {
                return externalRoles;
        }
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapNames.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapNames.java
new file mode 100644 (file)
index 0000000..0b9c3ad
--- /dev/null
@@ -0,0 +1,14 @@
+package org.argeo.osgi.useradmin;
+
+/**
+ * Standard LDAP attributes and object classes leverages in ths implementation
+ * of user admin.
+ */
+public interface LdapNames {
+       public final static String LDAP_PREFIX = "ldap:";
+       
+       // Attributes
+       public final static String LDAP_CN = LDAP_PREFIX + "cn";
+       public final static String LDAP_UID = LDAP_PREFIX + "uid";
+       public final static String LDAP_DISPLAY_NAME = LDAP_PREFIX + "displayName";
+}
index dcb639d9643f51718a56ceb0c5172c304cfec2f2..dabae718c2f1c9f71124d7f9ee4400086f2683e7 100644 (file)
@@ -22,6 +22,7 @@ import org.apache.commons.logging.LogFactory;
 import org.argeo.ArgeoException;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Group;
 import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.User;
 
@@ -89,7 +90,7 @@ public class LdapUserAdmin extends AbstractLdapUserAdmin {
                        Attributes attrs = initialLdapContext.getAttributes(name);
                        LdifUser res;
                        if (attrs.get("objectClass").contains("groupOfNames"))
-                               res = new LdifGroup(new LdapName(name), attrs);
+                               res = new LdifGroup(this, new LdapName(name), attrs);
                        else if (attrs.get("objectClass").contains("inetOrgPerson"))
                                res = new LdifUser(new LdapName(name), attrs);
                        else
@@ -105,6 +106,8 @@ public class LdapUserAdmin extends AbstractLdapUserAdmin {
        public Role[] getRoles(String filter) throws InvalidSyntaxException {
                try {
                        String searchFilter = filter;
+                       if (searchFilter == null)
+                               searchFilter = "(|(objectClass=inetOrgPerson)(objectClass=groupOfNames))";
                        SearchControls searchControls = new SearchControls();
                        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
 
@@ -116,15 +119,16 @@ public class LdapUserAdmin extends AbstractLdapUserAdmin {
                        while (results.hasMoreElements()) {
                                SearchResult searchResult = results.next();
                                Attributes attrs = searchResult.getAttributes();
-                               String name = searchResult.getName();
                                LdifUser role;
                                if (attrs.get("objectClass").contains("groupOfNames"))
-                                       role = new LdifGroup(new LdapName(name), attrs);
+                                       role = new LdifGroup(this, toDn(searchBase, searchResult),
+                                                       attrs);
                                else if (attrs.get("objectClass").contains("inetOrgPerson"))
-                                       role = new LdifUser(new LdapName(name), attrs);
+                                       role = new LdifUser(toDn(searchBase, searchResult), attrs);
                                else
                                        throw new ArgeoUserAdminException(
-                                                       "Unsupported LDAP type for " + name);
+                                                       "Unsupported LDAP type for "
+                                                                       + searchResult.getName());
                                res.add(role);
                        }
                        return res.toArray(new Role[res.size()]);
@@ -179,8 +183,8 @@ public class LdapUserAdmin extends AbstractLdapUserAdmin {
        @Override
        public Authorization getAuthorization(User user) {
                LdifUser u = (LdifUser) user;
-               populateDirectMemberOf(u);
-               return new LdifAuthorization(u);
+               // populateDirectMemberOf(u);
+               return new LdifAuthorization(u, getAllRoles(u));
        }
 
        private LdapName toDn(String baseDn, Binding binding)
@@ -189,8 +193,38 @@ public class LdapUserAdmin extends AbstractLdapUserAdmin {
                                + baseDn : binding.getName());
        }
 
-       void populateDirectMemberOf(LdifUser user) {
+       // void populateDirectMemberOf(LdifUser user) {
+       //
+       // try {
+       // String searchFilter = "(&(objectClass=groupOfNames)(member="
+       // + user.getName() + "))";
+       //
+       // SearchControls searchControls = new SearchControls();
+       // searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+       //
+       // String searchBase = "ou=node";
+       // NamingEnumeration<SearchResult> results = initialLdapContext
+       // .search(searchBase, searchFilter, searchControls);
+       //
+       // // TODO synchro
+       // //user.directMemberOf.clear();
+       // while (results.hasMoreElements()) {
+       // SearchResult searchResult = (SearchResult) results
+       // .nextElement();
+       // LdifGroup group = new LdifGroup(toDn(searchBase, searchResult),
+       // searchResult.getAttributes());
+       // populateDirectMemberOf(group);
+       // //user.directMemberOf.add(group);
+       // }
+       // } catch (Exception e) {
+       // throw new ArgeoException("Cannot populate direct members of "
+       // + user, e);
+       // }
+       // }
 
+       @Override
+       protected List<? extends Group> getDirectGroups(User user) {
+               List<Group> directGroups = new ArrayList<Group>();
                try {
                        String searchFilter = "(&(objectClass=groupOfNames)(member="
                                        + user.getName() + "))";
@@ -198,24 +232,26 @@ public class LdapUserAdmin extends AbstractLdapUserAdmin {
                        SearchControls searchControls = new SearchControls();
                        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
 
-                       String searchBase = "ou=node";
+                       String searchBase = getGroupsSearchBase();
                        NamingEnumeration<SearchResult> results = initialLdapContext
                                        .search(searchBase, searchFilter, searchControls);
 
-                       // TODO synchro
-                       user.directMemberOf.clear();
                        while (results.hasMoreElements()) {
                                SearchResult searchResult = (SearchResult) results
                                                .nextElement();
-                               LdifGroup group = new LdifGroup(toDn(searchBase, searchResult),
-                                               searchResult.getAttributes());
-                               populateDirectMemberOf(group);
-                               user.directMemberOf.add(group);
+                               LdifGroup group = new LdifGroup(this, toDn(searchBase,
+                                               searchResult), searchResult.getAttributes());
+                               directGroups.add(group);
                        }
+                       return directGroups;
                } catch (Exception e) {
                        throw new ArgeoException("Cannot populate direct members of "
                                        + user, e);
                }
        }
 
+       protected String getGroupsSearchBase() {
+               // TODO configure group search base
+               return baseDn;
+       }
 }
index 8f167c3683c58f7e7537194f50b14881cd6aaae9..3600866af77aa8e8d32f74ff5525bce9fdf4c1e3 100644 (file)
@@ -1,64 +1,80 @@
 package org.argeo.osgi.useradmin;
 
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Dictionary;
 import java.util.List;
 
 import org.osgi.service.useradmin.Authorization;
 import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
 
-public class LdifAuthorization implements Authorization {
-       private final LdifUser user;
+public class LdifAuthorization implements Authorization, LdapNames {
+       private final String name;
+       private final String displayName;
+       private final List<String> allRoles;
 
-       public LdifAuthorization(LdifUser user) {
-               this.user = user;
+       public LdifAuthorization(User user, List<Role> allRoles) {
+               if (user == null) {
+                       this.name = null;
+                       this.displayName = "anonymous";
+               } else {
+                       this.name = user.getName();
+                       Dictionary<String, Object> props = user.getProperties();
+                       Object displayName = props.get(LDAP_DISPLAY_NAME);
+                       if (displayName == null)
+                               displayName = props.get(LDAP_CN);
+                       if (displayName == null)
+                               displayName = props.get(LDAP_UID);
+                       if (displayName == null)
+                               displayName = user.getName();
+                       if (displayName == null)
+                               throw new ArgeoUserAdminException(
+                                               "Cannot set display name for " + user);
+                       this.displayName = displayName.toString();
+               }
+               // roles
+               String[] roles = new String[allRoles.size()];
+               for (int i = 0; i < allRoles.size(); i++) {
+                       roles[i] = allRoles.get(i).getName();
+               }
+               this.allRoles = Collections.unmodifiableList(Arrays.asList(roles));
        }
 
        @Override
        public String getName() {
-               if (user == null)
-                       return null;
-               return user.getName();
+               return name;
        }
 
        @Override
        public boolean hasRole(String name) {
-               for (Role role : getAllRoles()) {
-                       if (role.getName().equals(name))
-                               return true;
-               }
-               return false;
+               return allRoles.contains(name);
        }
 
        @Override
        public String[] getRoles() {
-               List<Role> allRoles = getAllRoles();
-               if (user != null)
-                       allRoles.add(0, user);
-               String[] res = new String[allRoles.size()];
-               for (int i = 0; i < allRoles.size(); i++)
-                       res[i] = allRoles.get(i).getName();
-               return res;
+               return allRoles.toArray(new String[allRoles.size()]);
        }
 
-       List<Role> getAllRoles() {
-               List<Role> allRoles = new ArrayList<Role>();
-               if (user != null)
-                       collectRoles(user, allRoles);
-               else
-                       collectAnonymousRoles(allRoles);
-               return allRoles;
+       @Override
+       public int hashCode() {
+               if (name == null)
+                       return super.hashCode();
+               return name.hashCode();
        }
 
-       private void collectRoles(LdifUser user, List<Role> allRoles) {
-               for (LdifGroup group : user.directMemberOf) {
-                       // TODO check for loops
-                       allRoles.add(group);
-                       collectRoles(group, allRoles);
-               }
+       @Override
+       public boolean equals(Object obj) {
+               if (!(obj instanceof Authorization))
+                       return false;
+               Authorization that = (Authorization) obj;
+               if (name == null)
+                       return that.getName() == null;
+               return name.equals(that.getName());
        }
 
-       private void collectAnonymousRoles(List<Role> allRoles) {
-               // TODO gather anonymous roles
+       @Override
+       public String toString() {
+               return displayName;
        }
-
 }
index fbc678c52c7589e9484967d3352d48f76a7368b7..a19052425491759a9c2c55687522fa9532a49397 100644 (file)
@@ -10,15 +10,18 @@ import javax.naming.ldap.LdapName;
 
 import org.osgi.service.useradmin.Group;
 import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.UserAdmin;
 
 public class LdifGroup extends LdifUser implements Group {
        // optimisation
-       List<Role> directMembers = null;
+       // List<Role> directMembers = null;
 
+       private final UserAdmin userAdmin;
        private String memberAttrName = "member";
 
-       public LdifGroup(LdapName dn, Attributes attributes) {
+       public LdifGroup(UserAdmin userAdmin, LdapName dn, Attributes attributes) {
                super(dn, attributes);
+               this.userAdmin = userAdmin;
        }
 
        @Override
@@ -29,9 +32,9 @@ public class LdifGroup extends LdifUser implements Group {
                                return false;
                } else
                        getAttributes().put(memberAttrName, role.getName());
-               directMembers.add(role);
-               if (role instanceof LdifUser)
-                       ((LdifUser) role).directMemberOf.add(this);
+               // directMembers.add(role);
+               // if (role instanceof LdifUser)
+               // ((LdifUser) role).directMemberOf.add(this);
                return true;
        }
 
@@ -47,9 +50,9 @@ public class LdifGroup extends LdifUser implements Group {
                        if (!member.contains(role.getName()))
                                return false;
                        member.remove(role.getName());
-                       directMembers.remove(role);
-                       if (role instanceof LdifUser)
-                               ((LdifUser) role).directMemberOf.remove(this);
+                       // directMembers.remove(role);
+                       // if (role instanceof LdifUser)
+                       // ((LdifUser) role).directMemberOf.remove(this);
                        return true;
                } else
                        return false;
@@ -57,10 +60,29 @@ public class LdifGroup extends LdifUser implements Group {
 
        @Override
        public Role[] getMembers() {
-               if (directMembers != null)
-                       return directMembers.toArray(new Role[directMembers.size()]);
-               else
-                       throw new ArgeoUserAdminException("Members have not been loaded.");
+               List<Role> directMembers = new ArrayList<Role>();
+               for (LdapName ldapName : getMemberNames()) {
+                       Role role = userAdmin.getRole(ldapName.toString());
+                       if (role == null && userAdmin instanceof AbstractLdapUserAdmin) {
+                               AbstractLdapUserAdmin ua = (AbstractLdapUserAdmin) userAdmin;
+                               if (ua.getExternalRoles() != null)
+                                       role = ua.getExternalRoles().getRole(ldapName.toString());
+                       }
+                       if (role == null)
+                               throw new ArgeoUserAdminException("No role found for "
+                                               + ldapName);
+
+                       // role.directMemberOf.add(group);
+                       // if (!directMemberOf.containsKey(role.getDn()))
+                       // directMemberOf.put(role.getDn(), new ArrayList<LdifGroup>());
+                       // directMemberOf.get(role.getDn()).add(group);
+                       directMembers.add(role);
+               }
+               return directMembers.toArray(new Role[directMembers.size()]);
+               // if (directMembers != null)
+               // return directMembers.toArray(new Role[directMembers.size()]);
+               // else
+               // throw new ArgeoUserAdminException("Members have not been loaded.");
 
                // Attribute memberAttribute = getAttributes().get(memberAttrName);
                // if (memberAttribute == null)
@@ -78,21 +100,21 @@ public class LdifGroup extends LdifUser implements Group {
                // }
        }
 
-//     void loadMembers(LdifUserAdmin userAdmin) {
-//             directMembers = new ArrayList<Role>();
-//             for (LdapName ldapName : getMemberNames()) {
-//                     LdifUser role;
-//                     if (userAdmin.groups.containsKey(ldapName))
-//                             role = userAdmin.groups.get(ldapName);
-//                     else if (userAdmin.users.containsKey(ldapName))
-//                             role = userAdmin.users.get(ldapName);
-//                     else
-//                             throw new ArgeoUserAdminException("No role found for "
-//                                             + ldapName);
-//                     role.directMemberOf.add(this);
-//                     directMembers.add(role);
-//             }
-//     }
+       // void loadMembers(LdifUserAdmin userAdmin) {
+       // directMembers = new ArrayList<Role>();
+       // for (LdapName ldapName : getMemberNames()) {
+       // LdifUser role;
+       // if (userAdmin.groups.containsKey(ldapName))
+       // role = userAdmin.groups.get(ldapName);
+       // else if (userAdmin.users.containsKey(ldapName))
+       // role = userAdmin.users.get(ldapName);
+       // else
+       // throw new ArgeoUserAdminException("No role found for "
+       // + ldapName);
+       // role.directMemberOf.add(this);
+       // directMembers.add(role);
+       // }
+       // }
 
        List<LdapName> getMemberNames() {
                Attribute memberAttribute = getAttributes().get(memberAttrName);
@@ -124,6 +146,5 @@ public class LdifGroup extends LdifUser implements Group {
        public String getMemberAttrName() {
                return memberAttrName;
        }
-       
-       
+
 }
index 9f378f1510163396fb8ccb2cbcc965e929bb360a..85d18c082b1d5ad93bd8e78d1740a9f29bd60759 100644 (file)
@@ -12,7 +12,7 @@ import org.osgi.service.useradmin.User;
 
 class LdifUser implements User {
        // optimisation
-       List<LdifGroup> directMemberOf = new ArrayList<LdifGroup>();
+       //List<LdifGroup> directMemberOf = new ArrayList<LdifGroup>();
 
        private final LdapName dn;
        private Attributes attributes;
index c96f2de6625e4af88f0bdae5d32c8f2e9768c7c8..c5ca49300425ad2910d361b2253c9e5728d7de35 100644 (file)
@@ -37,6 +37,9 @@ public class LdifUserAdmin extends AbstractLdapUserAdmin {
 
        private Map<String, Map<String, LdifUser>> userIndexes = new LinkedHashMap<String, Map<String, LdifUser>>();
 
+       // private Map<LdapName, List<LdifGroup>> directMemberOf = new
+       // TreeMap<LdapName, List<LdifGroup>>();
+
        public LdifUserAdmin(String uri) {
                this(uri, true);
        }
@@ -106,15 +109,15 @@ public class LdifUserAdmin extends AbstractLdapUserAdmin {
                                                users.put(key, new LdifUser(key, attributes));
                                                break objectClasses;
                                        } else if (objectClass.equals("groupOfNames")) {
-                                               groups.put(key, new LdifGroup(key, attributes));
+                                               groups.put(key, new LdifGroup(this, key, attributes));
                                                break objectClasses;
                                        }
                                }
                        }
 
                        // optimise
-                       for (LdifGroup group : groups.values())
-                               loadMembers(group);
+//                     for (LdifGroup group : groups.values())
+//                             loadMembers(group);
 
                        // indexes
                        for (String attr : getIndexedUserProperties())
@@ -168,7 +171,8 @@ public class LdifUserAdmin extends AbstractLdapUserAdmin {
 
        @Override
        public Authorization getAuthorization(User user) {
-               return new LdifAuthorization((LdifUser) user);
+               return new LdifAuthorization((LdifUser) user,
+                               getAllRoles((LdifUser) user));
        }
 
        @Override
@@ -188,7 +192,7 @@ public class LdifUserAdmin extends AbstractLdapUserAdmin {
                                newRole = new LdifUser(dn, attrs);
                                users.put(dn, newRole);
                        } else if (type == Role.GROUP) {
-                               newRole = new LdifGroup(dn, attrs);
+                               newRole = new LdifGroup(this, dn, attrs);
                                groups.put(dn, (LdifGroup) newRole);
                        } else
                                throw new ArgeoUserAdminException("Unsupported type " + type);
@@ -211,17 +215,18 @@ public class LdifUserAdmin extends AbstractLdapUserAdmin {
                                throw new ArgeoUserAdminException("There is no role " + name);
                        if (role == null)
                                return false;
-                       for (LdifGroup group : role.directMemberOf) {
-                               group.directMembers.remove(role);
+                       for (LdifGroup group : getDirectGroups(role)) {
+//                             group.directMembers.remove(role);
                                group.getAttributes().get(group.getMemberAttrName())
                                                .remove(dn.toString());
                        }
                        if (role instanceof LdifGroup) {
                                LdifGroup group = (LdifGroup) role;
-                               for (Role user : group.directMembers) {
-                                       if (user instanceof LdifUser)
-                                               ((LdifUser) user).directMemberOf.remove(group);
-                               }
+                               // for (Role user : group.directMembers) {
+                               // if (user instanceof LdifUser)
+                               // directMemberOf.get(((LdifUser) user).getDn()).remove(
+                               // group);
+                               // }
                        }
                        return true;
                } catch (InvalidNameException e) {
@@ -280,25 +285,54 @@ public class LdifUserAdmin extends AbstractLdapUserAdmin {
                // throw new UnsupportedOperationException();
        }
 
-       protected void loadMembers(LdifGroup group) {
-               group.directMembers = new ArrayList<Role>();
-               for (LdapName ldapName : group.getMemberNames()) {
-                       LdifUser role = null;
-                       if (groups.containsKey(ldapName))
-                               role = groups.get(ldapName);
-                       else if (users.containsKey(ldapName))
-                               role = users.get(ldapName);
-                       else {
-                               if (getExternalRoles() != null)
-                                       role = (LdifUser) getExternalRoles().getRole(
-                                                       ldapName.toString());
-                               if (role == null)
-                                       throw new ArgeoUserAdminException("No role found for "
-                                                       + ldapName);
+//     protected void loadMembers(LdifGroup group) {
+//             group.directMembers = new ArrayList<Role>();
+//             for (LdapName ldapName : group.getMemberNames()) {
+//                     LdifUser role = null;
+//                     if (groups.containsKey(ldapName))
+//                             role = groups.get(ldapName);
+//                     else if (users.containsKey(ldapName))
+//                             role = users.get(ldapName);
+//                     else {
+//                             if (getExternalRoles() != null)
+//                                     role = (LdifUser) getExternalRoles().getRole(
+//                                                     ldapName.toString());
+//                             if (role == null)
+//                                     throw new ArgeoUserAdminException("No role found for "
+//                                                     + ldapName);
+//                     }
+//                     // role.directMemberOf.add(group);
+//                     // if (!directMemberOf.containsKey(role.getDn()))
+//                     // directMemberOf.put(role.getDn(), new ArrayList<LdifGroup>());
+//                     // directMemberOf.get(role.getDn()).add(group);
+//                     group.directMembers.add(role);
+//             }
+//     }
+
+       @Override
+       protected List<LdifGroup> getDirectGroups(User user) {
+               LdapName dn;
+               if (user instanceof LdifUser)
+                       dn = ((LdifUser) user).getDn();
+               else
+                       try {
+                               dn = new LdapName(user.getName());
+                       } catch (InvalidNameException e) {
+                               throw new ArgeoUserAdminException("Badly formatted user name "
+                                               + user.getName(), e);
                        }
-                       role.directMemberOf.add(group);
-                       group.directMembers.add(role);
+
+               List<LdifGroup> directGroups = new ArrayList<LdifGroup>();
+               for (LdapName name : groups.keySet()) {
+                       LdifGroup group = groups.get(name);
+                       if (group.getMemberNames().contains(dn))
+                               directGroups.add(group);
                }
+               return directGroups;
+               // if (directMemberOf.containsKey(dn))
+               // return Collections.unmodifiableList(directMemberOf.get(dn));
+               // else
+               // return Collections.EMPTY_LIST;
        }
 
 }
diff --git a/org.argeo.security.core/src/org/argeo/security/crypto/PkiUtils.java b/org.argeo.security.core/src/org/argeo/security/crypto/PkiUtils.java
new file mode 100644 (file)
index 0000000..ed6640f
--- /dev/null
@@ -0,0 +1,97 @@
+package org.argeo.security.crypto;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.argeo.ArgeoException;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+/**
+ * Utilities around private keys and certificate, mostly wrapping BouncyCastle
+ * implementations.
+ */
+public class PkiUtils {
+       private final static String SECURITY_PROVIDER;
+       static {
+               Security.addProvider(new BouncyCastleProvider());
+               SECURITY_PROVIDER = "BC";
+       }
+
+       public static X509Certificate generateSelfSignedCertificate(
+                       KeyStore keyStore, X500Principal x500Principal, char[] keyPassword) {
+               try {
+                       KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA",
+                                       SECURITY_PROVIDER);
+                       kpGen.initialize(1024, new SecureRandom());
+                       KeyPair pair = kpGen.generateKeyPair();
+                       Date notBefore = new Date(System.currentTimeMillis() - 10000);
+                       Date notAfter = new Date(
+                                       System.currentTimeMillis() + 24L * 3600 * 1000);
+                       BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
+                       X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(
+                                       x500Principal, serial, notBefore, notAfter, x500Principal,
+                                       pair.getPublic());
+                       ContentSigner sigGen = new JcaContentSignerBuilder(
+                                       "SHA256WithRSAEncryption").setProvider(SECURITY_PROVIDER)
+                                       .build(pair.getPrivate());
+                       X509Certificate cert = new JcaX509CertificateConverter()
+                                       .setProvider(SECURITY_PROVIDER).getCertificate(
+                                                       certGen.build(sigGen));
+                       cert.checkValidity(new Date());
+                       cert.verify(cert.getPublicKey());
+
+                       keyStore.setKeyEntry(x500Principal.getName(), pair.getPrivate(),
+                                       keyPassword, new Certificate[] { cert });
+                       return cert;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot generate self-signed certificate",
+                                       e);
+               }
+       }
+
+       public static KeyStore getKeyStore(File keyStoreFile,
+                       char[] keyStorePassword) {
+               try {
+                       KeyStore store = KeyStore.getInstance("PKCS12", SECURITY_PROVIDER);
+                       if (keyStoreFile.exists()) {
+                               try (FileInputStream fis = new FileInputStream(keyStoreFile)) {
+                                       store.load(fis, keyStorePassword);
+                               }
+                       } else {
+                               store.load(null);
+                       }
+                       return store;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot load keystore " + keyStoreFile, e);
+               }
+       }
+
+       public static void saveKeyStore(File keyStoreFile, char[] keyStorePassword,
+                       KeyStore keyStore) {
+               try {
+                       try (FileOutputStream fis = new FileOutputStream(keyStoreFile)) {
+                               keyStore.store(fis, keyStorePassword);
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot save keystore " + keyStoreFile, e);
+               }
+       }
+
+}