--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.cms.auth;
+
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.acl.Group;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.security.auth.x500.X500Principal;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsView;
+import org.argeo.cms.util.CmsUtils;
+import org.osgi.service.useradmin.Authorization;
+
+/** Static utilities */
+public final class CurrentUser {
+ /**
+ * @return the authenticated username or null if not authenticated /
+ * anonymous
+ */
+ public static String getUsername() {
+ return getUsername(currentSubject());
+ }
+
+ public static String getDisplayName() {
+ return getDisplayName(currentSubject());
+ }
+
+ private static Subject currentSubject() {
+ Subject subject = Subject.getSubject(AccessController.getContext());
+ if (subject != null)
+ return subject;
+ if (subject == null) {
+ CmsView cmsView = CmsUtils.getCmsView();
+ if (cmsView != null)
+ return cmsView.getSubject();
+ }
+ throw new CmsException("Cannot find related subject");
+ }
+
+ public final static String getUsername(Subject subject) {
+ // Subject subject = Subject.getSubject(AccessController.getContext());
+ // if (subject == null)
+ // return null;
+ if (subject.getPrincipals(X500Principal.class).size() != 1)
+ return null;
+ Principal principal = subject.getPrincipals(X500Principal.class)
+ .iterator().next();
+ return principal.getName();
+
+ }
+
+ public final static String getDisplayName(Subject subject) {
+ return getAuthorization(subject).toString();
+ }
+
+ private static Authorization getAuthorization(Subject subject) {
+ return subject.getPrivateCredentials(Authorization.class).iterator()
+ .next();
+ }
+
+ public final static Set<String> roles() {
+ return roles(currentSubject());
+ }
+
+ public final static Set<String> roles(Subject subject) {
+ Set<String> roles = new HashSet<String>();
+ X500Principal userPrincipal = subject
+ .getPrincipals(X500Principal.class).iterator().next();
+ roles.add(userPrincipal.getName());
+ for (Principal group : subject.getPrincipals(Group.class)) {
+ roles.add(group.getName());
+ }
+ return roles;
+ }
+
+ private CurrentUser() {
+ }
+}
// WebDav / JCR remoting
private OpenInViewSessionProvider sessionProvider;
- DataHttp(HttpService httpService, JackrabbitNode node) {
+ DataHttp(HttpService httpService, NodeRepository node) {
this.httpService = httpService;
sessionProvider = new OpenInViewSessionProvider();
registerRepositoryServlets(ALIAS_NODE, node);
+++ /dev/null
-package org.argeo.cms.internal.kernel;
-
-import static org.argeo.cms.internal.kernel.JackrabbitNodeType.h2;
-
-import java.io.File;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.Properties;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.core.RepositoryContext;
-import org.apache.jackrabbit.core.RepositoryImpl;
-import org.apache.jackrabbit.core.cache.CacheManager;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
-import org.apache.jackrabbit.stats.RepositoryStatisticsImpl;
-import org.argeo.ArgeoException;
-import org.argeo.cms.CmsException;
-import org.argeo.jackrabbit.JackrabbitWrapper;
-import org.argeo.jcr.ArgeoJcrConstants;
-import org.argeo.jcr.DefaultRepositoryRegister;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-import org.xml.sax.InputSource;
-
-/** Jacrabbit based data layer */
-class JackrabbitNode extends JackrabbitWrapper implements KernelConstants,
- ArgeoJcrConstants {
- private static Log log = LogFactory.getLog(JackrabbitNode.class);
-
- private RepositoryContext repositoryContext;
-
- private ServiceRegistration<Repository> repositoryReg;
-
- public JackrabbitNode(BundleContext bundleContext) {
- setBundleContext(bundleContext);
- JackrabbitNodeType type = JackrabbitNodeType.valueOf(prop(REPO_TYPE,
- h2.name()));
- try {
- repositoryContext = createNode(type);
- setCndFiles(Arrays.asList(DEFAULT_CNDS));
- prepareDataModel();
- } catch (Exception e) {
- throw new ArgeoException(
- "Cannot create Jackrabbit repository of type " + type, e);
- }
- }
-
- void publish(DefaultRepositoryRegister repositoryRegister) {
- Hashtable<String, String> regProps = new Hashtable<String, String>();
- regProps.put(JCR_REPOSITORY_ALIAS, ALIAS_NODE);
- repositoryReg = getBundleContext().registerService(Repository.class,
- this, regProps);
- repositoryRegister.register(this, regProps);
- }
-
- public void destroy() {
- repositoryReg.unregister();
- ((RepositoryImpl) getRepository()).shutdown();
- }
-
- RepositoryStatisticsImpl getRepositoryStatistics() {
- return repositoryContext.getRepositoryStatistics();
- }
-
- private RepositoryConfig getConfiguration(JackrabbitNodeType type,
- Hashtable<String, Object> vars) throws RepositoryException {
- ClassLoader cl = getClass().getClassLoader();
- InputStream in = null;
- try {
- final String base = "/org/argeo/cms/internal/kernel";
- switch (type) {
- case h2:
- in = cl.getResourceAsStream(base + "/repository-h2.xml");
- break;
- case postgresql:
- in = cl.getResourceAsStream(base + "/repository-postgresql.xml");
- break;
- case memory:
- in = cl.getResourceAsStream(base + "/repository-memory.xml");
- break;
- default:
- throw new CmsException("Unsupported node type " + type);
- }
-
- if (in == null)
- throw new CmsException("Repository configuration not found");
- InputSource config = new InputSource(in);
- Properties jackrabbitProps = new Properties();
- jackrabbitProps.putAll(vars);
- RepositoryConfig repositoryConfig = RepositoryConfig.create(config,
- jackrabbitProps);
- return repositoryConfig;
- } finally {
- IOUtils.closeQuietly(in);
- }
- }
-
- private Hashtable<String, Object> getConfigurationProperties(
- JackrabbitNodeType type) {
- // use Hashtable to ease integration with Properties
- Hashtable<String, Object> defaults = new Hashtable<String, Object>();
-
- // home
- File osgiInstanceDir = KernelUtils.getOsgiInstanceDir();
- File homeDir = new File(osgiInstanceDir, "node");
- // home cannot be overridden
- defaults.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
- homeDir.getAbsolutePath());
-
- // common
- setProp(defaults, REPO_DEFAULT_WORKSPACE, "main");
- setProp(defaults, REPO_MAX_POOL_SIZE, "10");
- // Jackrabbit defaults
- setProp(defaults, REPO_BUNDLE_CACHE_MB, "8");
- // See http://wiki.apache.org/jackrabbit/Search
- setProp(defaults, REPO_EXTRACTOR_POOL_SIZE, "0");
- setProp(defaults, REPO_SEARCH_CACHE_SIZE, "1000");
- setProp(defaults, REPO_MAX_VOLATILE_INDEX_SIZE, "1048576");
-
- // specific
- String dburl;
- switch (type) {
- case h2:
- dburl = "jdbc:h2:" + homeDir.getPath() + "/h2/repository";
- setProp(defaults, REPO_DBURL, dburl);
- setProp(defaults, REPO_DBUSER, "sa");
- setProp(defaults, REPO_DBPASSWORD, "");
- break;
- case postgresql:
- dburl = "jdbc:postgresql://localhost/demo";
- setProp(defaults, REPO_DBURL, dburl);
- setProp(defaults, REPO_DBUSER, "argeo");
- setProp(defaults, REPO_DBPASSWORD, "argeo");
- break;
- case memory:
- break;
- default:
- throw new CmsException("Unsupported node type " + type);
- }
- return defaults;
- }
-
- private void setProp(Dictionary<String, Object> props, String key,
- String defaultValue) {
- String value = prop(key, defaultValue);
- props.put(key, value);
- }
-
- private String prop(String key, String defaultValue) {
- // TODO use OSGi CM instead of Framework/System properties
- return KernelUtils.getFrameworkProp(key, defaultValue);
- }
-
- private RepositoryContext createNode(JackrabbitNodeType type)
- throws RepositoryException {
- Hashtable<String, Object> vars = getConfigurationProperties(type);
- RepositoryConfig repositoryConfig = getConfiguration(type, vars);
- RepositoryContext repositoryContext = createJackrabbitRepository(repositoryConfig);
- RepositoryImpl repository = repositoryContext.getRepository();
-
- // cache
- String maxCacheMbStr = prop(REPO_MAX_CACHE_MB, null);
- if (maxCacheMbStr != null) {
- Integer maxCacheMB = Integer.parseInt(maxCacheMbStr);
- CacheManager cacheManager = repository.getCacheManager();
- cacheManager.setMaxMemory(maxCacheMB * 1024l * 1024l);
- cacheManager.setMaxMemoryPerCache((maxCacheMB / 4) * 1024l * 1024l);
- }
-
- // wrap the repository
- setRepository(repository);
- return repositoryContext;
- }
-
- private RepositoryContext createJackrabbitRepository(
- RepositoryConfig repositoryConfig) throws RepositoryException {
- File homeDirectory = null;
- long begin = System.currentTimeMillis();
- //
- // Actual repository creation
- //
- RepositoryContext repositoryContext = RepositoryContext
- .create(repositoryConfig);
-
- double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
- if (log.isTraceEnabled())
- log.trace("Created Jackrabbit repository in " + duration
- + " s, home: " + homeDirectory);
-
- return repositoryContext;
- }
-}
package org.argeo.cms.internal.kernel;
+import static org.argeo.jcr.ArgeoJcrConstants.ALIAS_NODE;
+import static org.argeo.jcr.ArgeoJcrConstants.JCR_REPOSITORY_ALIAS;
+
import java.lang.management.ManagementFactory;
import java.security.PrivilegedAction;
import java.util.HashMap;
+import java.util.Hashtable;
import java.util.Map;
import javax.jcr.Repository;
import javax.jcr.RepositoryFactory;
-import javax.jcr.Session;
import javax.security.auth.Subject;
import javax.transaction.TransactionManager;
-import javax.transaction.TransactionSynchronizationRegistry;
import javax.transaction.UserTransaction;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jackrabbit.util.TransientFileFactory;
import org.argeo.ArgeoException;
+import org.argeo.ArgeoLogger;
import org.argeo.cms.CmsException;
import org.argeo.cms.internal.transaction.SimpleTransactionManager;
import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.util.tracker.ServiceTracker;
/**
* Argeo CMS Kernel. Responsible for :
* </ul>
*/
final class Kernel implements ServiceListener {
- private final static Log log = LogFactory.getLog(Kernel.class);
-
- private final BundleContext bundleContext = Activator.getBundleContext();
- private final NodeSecurity nodeSecurity;
-
- ThreadGroup threadGroup = new ThreadGroup(Kernel.class.getSimpleName());
- JackrabbitNode node;
- private NodeUserAdmin userAdmin;
+ /*
+ * REGISTERED SERVICES
+ */
+ private ServiceRegistration<ArgeoLogger> loggerReg;
+ private ServiceRegistration<TransactionManager> tmReg;
+ private ServiceRegistration<UserTransaction> utReg;
+ // private ServiceRegistration<TransactionSynchronizationRegistry> tsrReg;
+ private ServiceRegistration<Repository> repositoryReg;
+ private ServiceRegistration<RepositoryFactory> repositoryFactoryReg;
+ private ServiceRegistration<UserAdmin> userAdminReg;
+
+ /*
+ * SERVICES IMPLEMENTATIONS
+ */
+ private NodeLogger logger;
private SimpleTransactionManager transactionManager;
private OsgiJackrabbitRepositoryFactory repositoryFactory;
- private DataHttp nodeHttp;
+ NodeRepository repository;
+ private NodeUserAdmin userAdmin;
+
+ // Members
+ private final static Log log = LogFactory.getLog(Kernel.class);
+ ThreadGroup threadGroup = new ThreadGroup(Kernel.class.getSimpleName());
+ private final BundleContext bc = Activator.getBundleContext();
+ private final NodeSecurity nodeSecurity;
+ private DataHttp dataHttp;
private KernelThread kernelThread;
public Kernel() {
final void init() {
Subject.doAs(nodeSecurity.getKernelSubject(),
new PrivilegedAction<Void>() {
-
@Override
public Void run() {
doInit();
return null;
}
-
});
}
long begin = System.currentTimeMillis();
try {
- // Transaction
+ // Initialise services
+ logger = new NodeLogger();
transactionManager = new SimpleTransactionManager();
-
- // Jackrabbit node
- node = new JackrabbitNode(bundleContext);
-
- // JCR repository factory
+ repository = new NodeRepository(bc);
repositoryFactory = new OsgiJackrabbitRepositoryFactory();
+ userAdmin = new NodeUserAdmin(transactionManager, repository);
- // Authentication
- Session adminSession = node.login();
- userAdmin = new NodeUserAdmin(adminSession);
- userAdmin.setTransactionManager(transactionManager);
- bundleContext.registerService(UserAdmin.class, userAdmin,
- userAdmin.currentState());
-
- // Equinox dependency
- // ExtendedHttpService httpService = waitForHttpService();
- // nodeHttp = new NodeHttp(httpService, node);
- ServiceReference<ExtendedHttpService> sr = bundleContext
+ // HTTP
+ ServiceReference<ExtendedHttpService> sr = bc
.getServiceReference(ExtendedHttpService.class);
if (sr != null)
addHttpService(sr);
kernelThread.start();
// Publish services to OSGi
- bundleContext.registerService(TransactionManager.class,
- transactionManager, null);
- bundleContext.registerService(UserTransaction.class,
- transactionManager, null);
- bundleContext.registerService(
- TransactionSynchronizationRegistry.class,
- transactionManager.getTransactionSynchronizationRegistry(),
- null);
- node.publish(repositoryFactory);
- bundleContext.registerService(RepositoryFactory.class,
- repositoryFactory, null);
-
- bundleContext.addServiceListener(Kernel.this);
+ publish();
} catch (Exception e) {
log.error("Cannot initialize Argeo CMS", e);
throw new ArgeoException("Cannot initialize", e);
directorsCut(initDuration);
}
+ private void publish() {
+ // Listen to service publication (also ours)
+ bc.addServiceListener(Kernel.this);
+
+ // Logging
+ loggerReg = bc.registerService(ArgeoLogger.class, logger, null);
+ // Transaction
+ tmReg = bc.registerService(TransactionManager.class,
+ transactionManager, null);
+ utReg = bc.registerService(UserTransaction.class, transactionManager,
+ null);
+ // tsrReg = bc.registerService(TransactionSynchronizationRegistry.class,
+ // transactionManager.getTsr(), null);
+ // User admin
+ userAdminReg = bc.registerService(UserAdmin.class, userAdmin,
+ userAdmin.currentState());
+ // JCR
+ Hashtable<String, String> regProps = new Hashtable<String, String>();
+ regProps.put(JCR_REPOSITORY_ALIAS, ALIAS_NODE);
+ repositoryReg = bc.registerService(Repository.class, repository,
+ regProps);
+ repositoryFactoryReg = bc.registerService(RepositoryFactory.class,
+ repositoryFactory, null);
+ }
+
void destroy() {
long begin = System.currentTimeMillis();
+ unpublish();
kernelThread.destroyAndJoin();
- if (nodeHttp != null)
- nodeHttp.destroy();
+ if (dataHttp != null)
+ dataHttp.destroy();
if (userAdmin != null)
userAdmin.destroy();
- if (node != null)
- node.destroy();
+ if (repository != null)
+ repository.destroy();
- bundleContext.removeServiceListener(this);
+ bc.removeServiceListener(this);
// Clean hanging threads from Jackrabbit
TransientFileFactory.shutdown();
+ (duration % 1000) + "s ##");
}
+ private void unpublish() {
+ userAdminReg.unregister();
+ repositoryFactoryReg.unregister();
+ repositoryReg.unregister();
+ tmReg.unregister();
+ utReg.unregister();
+ loggerReg.unregister();
+ }
+
@Override
public void serviceChanged(ServiceEvent event) {
ServiceReference<?> sr = event.getServiceReference();
- Object service = bundleContext.getService(sr);
+ Object service = bc.getService(sr);
if (service instanceof Repository) {
Object jcrRepoAlias = sr
.getProperty(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS);
if (jcrRepoAlias != null) {// JCR repository
String alias = jcrRepoAlias.toString();
- Repository repository = (Repository) bundleContext
- .getService(sr);
+ Repository repository = (Repository) bc.getService(sr);
Map<String, Object> props = new HashMap<String, Object>();
for (String key : sr.getPropertyKeys())
props.put(key, sr.getProperty(key));
if (ServiceEvent.REGISTERED == event.getType()) {
try {
repositoryFactory.register(repository, props);
- nodeHttp.registerRepositoryServlets(alias, repository);
+ dataHttp.registerRepositoryServlets(alias, repository);
} catch (Exception e) {
throw new CmsException(
"Could not publish JCR repository " + alias, e);
}
} else if (ServiceEvent.UNREGISTERING == event.getType()) {
repositoryFactory.unregister(repository, props);
- nodeHttp.unregisterRepositoryServlets(alias);
+ dataHttp.unregisterRepositoryServlets(alias);
}
}
} else if (service instanceof ExtendedHttpService) {
if (ServiceEvent.REGISTERED == event.getType()) {
addHttpService(sr);
} else if (ServiceEvent.UNREGISTERING == event.getType()) {
- nodeHttp.destroy();
- nodeHttp = null;
+ dataHttp.destroy();
+ dataHttp = null;
}
}
}
private void addHttpService(ServiceReference<?> sr) {
// for (String key : sr.getPropertyKeys())
// log.debug(key + "=" + sr.getProperty(key));
- ExtendedHttpService httpService = (ExtendedHttpService) bundleContext
+ ExtendedHttpService httpService = (ExtendedHttpService) bc
.getService(sr);
// TODO find constants
Object httpPort = sr.getProperty("http.port");
Object httpsPort = sr.getProperty("https.port");
- nodeHttp = new DataHttp(httpService, node);
+ dataHttp = new DataHttp(httpService, repository);
if (log.isDebugEnabled())
log.debug("HTTP " + httpPort
+ (httpsPort != null ? " - HTTPS " + httpsPort : ""));
}
- private ExtendedHttpService waitForHttpService() {
- final ServiceTracker<ExtendedHttpService, ExtendedHttpService> st = new ServiceTracker<ExtendedHttpService, ExtendedHttpService>(
- bundleContext, ExtendedHttpService.class, null);
- st.open();
- ExtendedHttpService httpService;
- try {
- httpService = st.waitForService(1000);
- } catch (InterruptedException e) {
- httpService = null;
- }
-
- if (httpService == null)
- throw new CmsException("Could not find "
- + ExtendedHttpService.class + " service.");
- return httpService;
- }
+ // private ExtendedHttpService waitForHttpService() {
+ // final ServiceTracker<ExtendedHttpService, ExtendedHttpService> st = new
+ // ServiceTracker<ExtendedHttpService, ExtendedHttpService>(
+ // bc, ExtendedHttpService.class, null);
+ // st.open();
+ // ExtendedHttpService httpService;
+ // try {
+ // httpService = st.waitForService(1000);
+ // } catch (InterruptedException e) {
+ // httpService = null;
+ // }
+ //
+ // if (httpService == null)
+ // throw new CmsException("Could not find "
+ // + ExtendedHttpService.class + " service.");
+ // return httpService;
+ // }
final private static void directorsCut(long initDuration) {
// final long ms = 128l + (long) (Math.random() * 128d);
public KernelThread(Kernel kernel) {
super(kernel.threadGroup, kernel.getClass().getSimpleName());
this.kernel = kernel;
- this.repoStats = kernel.node.getRepositoryStatistics();
+ this.repoStats = kernel.repository.getRepositoryStatistics();
}
private void doSmallestPeriod() {
// WebDav / JCR remoting
private OpenInViewSessionProvider sessionProvider;
- NodeHttp(ExtendedHttpService httpService, JackrabbitNode node) {
+ NodeHttp(ExtendedHttpService httpService, NodeRepository node) {
// this.bundleContext = bundleContext;
// this.authenticationManager = authenticationManager;
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.cms.internal.kernel;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.LoggingEvent;
+import org.argeo.ArgeoException;
+import org.argeo.ArgeoLogListener;
+import org.argeo.ArgeoLogger;
+import org.argeo.cms.auth.CurrentUser;
+
+/** Not meant to be used directly in standard log4j config */
+class NodeLogger implements ArgeoLogger {
+
+ private Boolean disabled = false;
+
+ private String level = null;
+
+ private Level log4jLevel = null;
+ // private Layout layout;
+
+ private Properties configuration;
+
+ private AppenderImpl appender;
+
+ private final List<ArgeoLogListener> everythingListeners = Collections
+ .synchronizedList(new ArrayList<ArgeoLogListener>());
+ private final List<ArgeoLogListener> allUsersListeners = Collections
+ .synchronizedList(new ArrayList<ArgeoLogListener>());
+ private final Map<String, List<ArgeoLogListener>> userListeners = Collections
+ .synchronizedMap(new HashMap<String, List<ArgeoLogListener>>());
+
+ private BlockingQueue<LogEvent> events;
+ private LogDispatcherThread logDispatcherThread = new LogDispatcherThread();
+
+ private Integer maxLastEventsCount = 10 * 1000;
+
+ /** Marker to prevent stack overflow */
+ private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
+
+ @Override
+ protected Boolean initialValue() {
+ return false;
+ }
+ };
+
+ public void init() {
+ try {
+ events = new LinkedBlockingQueue<LogEvent>();
+
+ // if (layout != null)
+ // setLayout(layout);
+ // else
+ // setLayout(new PatternLayout(pattern));
+ appender = new AppenderImpl();
+ reloadConfiguration();
+ Logger.getRootLogger().addAppender(appender);
+
+ logDispatcherThread = new LogDispatcherThread();
+ logDispatcherThread.start();
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot initialize log4j");
+ }
+ }
+
+ public void destroy() throws Exception {
+ Logger.getRootLogger().removeAppender(appender);
+ allUsersListeners.clear();
+ for (List<ArgeoLogListener> lst : userListeners.values())
+ lst.clear();
+ userListeners.clear();
+
+ events.clear();
+ events = null;
+ logDispatcherThread.interrupt();
+ }
+
+ // public void setLayout(Layout layout) {
+ // this.layout = layout;
+ // }
+
+ public synchronized void register(ArgeoLogListener listener,
+ Integer numberOfPreviousEvents) {
+ String username = CurrentUser.getUsername();
+ if (username == null)
+ throw new ArgeoException(
+ "Only authenticated users can register a log listener");
+
+ if (!userListeners.containsKey(username)) {
+ List<ArgeoLogListener> lst = Collections
+ .synchronizedList(new ArrayList<ArgeoLogListener>());
+ userListeners.put(username, lst);
+ }
+ userListeners.get(username).add(listener);
+ List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username,
+ numberOfPreviousEvents);
+ for (LogEvent evt : lastEvents)
+ dispatchEvent(listener, evt);
+ }
+
+ public synchronized void registerForAll(ArgeoLogListener listener,
+ Integer numberOfPreviousEvents, boolean everything) {
+ if (everything)
+ everythingListeners.add(listener);
+ else
+ allUsersListeners.add(listener);
+ List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(null,
+ numberOfPreviousEvents);
+ for (LogEvent evt : lastEvents)
+ if (everything || evt.getUsername() != null)
+ dispatchEvent(listener, evt);
+ }
+
+ public synchronized void unregister(ArgeoLogListener listener) {
+ String username = CurrentUser.getUsername();
+ if (username == null)// FIXME
+ return;
+ if (!userListeners.containsKey(username))
+ throw new ArgeoException("No user listeners " + listener
+ + " registered for user " + username);
+ if (!userListeners.get(username).contains(listener))
+ throw new ArgeoException("No user listeners " + listener
+ + " registered for user " + username);
+ userListeners.get(username).remove(listener);
+ if (userListeners.get(username).isEmpty())
+ userListeners.remove(username);
+
+ }
+
+ public synchronized void unregisterForAll(ArgeoLogListener listener) {
+ everythingListeners.remove(listener);
+ allUsersListeners.remove(listener);
+ }
+
+ /** For development purpose, since using regular logging is not easy here */
+ static void stdOut(Object obj) {
+ System.out.println(obj);
+ }
+
+ // public void setPattern(String pattern) {
+ // this.pattern = pattern;
+ // }
+
+ public void setDisabled(Boolean disabled) {
+ this.disabled = disabled;
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ public void setConfiguration(Properties configuration) {
+ this.configuration = configuration;
+ }
+
+ public void updateConfiguration(Properties configuration) {
+ setConfiguration(configuration);
+ reloadConfiguration();
+ }
+
+ public Properties getConfiguration() {
+ return configuration;
+ }
+
+ /** Reloads configuration (if the configuration {@link Properties} is set) */
+ protected void reloadConfiguration() {
+ if (configuration != null) {
+ LogManager.resetConfiguration();
+ PropertyConfigurator.configure(configuration);
+ }
+ }
+
+ protected synchronized void processLoggingEvent(LogEvent event) {
+ if (disabled)
+ return;
+
+ if (dispatching.get())
+ return;
+
+ if (level != null && !level.trim().equals("")) {
+ if (log4jLevel == null || !log4jLevel.toString().equals(level))
+ try {
+ log4jLevel = Level.toLevel(level);
+ } catch (Exception e) {
+ System.err
+ .println("Log4j level could not be set for level '"
+ + level + "', resetting it to null.");
+ e.printStackTrace();
+ level = null;
+ }
+
+ if (log4jLevel != null
+ && !event.getLoggingEvent().getLevel()
+ .isGreaterOrEqual(log4jLevel)) {
+ return;
+ }
+ }
+
+ try {
+ // admin listeners
+ Iterator<ArgeoLogListener> everythingIt = everythingListeners
+ .iterator();
+ while (everythingIt.hasNext())
+ dispatchEvent(everythingIt.next(), event);
+
+ if (event.getUsername() != null) {
+ Iterator<ArgeoLogListener> allUsersIt = allUsersListeners
+ .iterator();
+ while (allUsersIt.hasNext())
+ dispatchEvent(allUsersIt.next(), event);
+
+ if (userListeners.containsKey(event.getUsername())) {
+ Iterator<ArgeoLogListener> userIt = userListeners.get(
+ event.getUsername()).iterator();
+ while (userIt.hasNext())
+ dispatchEvent(userIt.next(), event);
+ }
+ }
+ } catch (Exception e) {
+ stdOut("Cannot process logging event");
+ e.printStackTrace();
+ }
+ }
+
+ protected void dispatchEvent(ArgeoLogListener logListener, LogEvent evt) {
+ LoggingEvent event = evt.getLoggingEvent();
+ logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event
+ .getLevel().toString(), event.getLoggerName(), event
+ .getThreadName(), event.getMessage(), event
+ .getThrowableStrRep());
+ }
+
+ private class AppenderImpl extends AppenderSkeleton {
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ public void close() {
+ }
+
+ @Override
+ protected void append(LoggingEvent event) {
+ if (events != null) {
+ try {
+ String username = CurrentUser.getUsername();
+ events.put(new LogEvent(username, event));
+ } catch (InterruptedException e) {
+ // silent
+ }
+ }
+ }
+
+ }
+
+ private class LogDispatcherThread extends Thread {
+ /** encapsulated in order to simplify concurrency management */
+ private LinkedList<LogEvent> lastEvents = new LinkedList<LogEvent>();
+
+ public LogDispatcherThread() {
+ super("Argeo Logging Dispatcher Thread");
+ }
+
+ public void run() {
+ while (events != null) {
+ try {
+ LogEvent loggingEvent = events.take();
+ processLoggingEvent(loggingEvent);
+ addLastEvent(loggingEvent);
+ } catch (InterruptedException e) {
+ if (events == null)
+ return;
+ }
+ }
+ }
+
+ protected synchronized void addLastEvent(LogEvent loggingEvent) {
+ if (lastEvents.size() >= maxLastEventsCount)
+ lastEvents.poll();
+ lastEvents.add(loggingEvent);
+ }
+
+ public synchronized List<LogEvent> getLastEvents(String username,
+ Integer maxCount) {
+ LinkedList<LogEvent> evts = new LinkedList<LogEvent>();
+ ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents
+ .size());
+ int count = 0;
+ while (it.hasPrevious() && (count < maxCount)) {
+ LogEvent evt = it.previous();
+ if (username == null || username.equals(evt.getUsername())) {
+ evts.push(evt);
+ count++;
+ }
+ }
+ return evts;
+ }
+ }
+
+ private class LogEvent {
+ private final String username;
+ private final LoggingEvent loggingEvent;
+
+ public LogEvent(String username, LoggingEvent loggingEvent) {
+ super();
+ this.username = username;
+ this.loggingEvent = loggingEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ return loggingEvent.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return loggingEvent.equals(obj);
+ }
+
+ @Override
+ public String toString() {
+ return username + "@ " + loggingEvent.toString();
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public LoggingEvent getLoggingEvent() {
+ return loggingEvent;
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.cms.internal.kernel;
+
+import static org.argeo.cms.internal.kernel.JackrabbitNodeType.h2;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.core.RepositoryContext;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.cache.CacheManager;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
+import org.apache.jackrabbit.stats.RepositoryStatisticsImpl;
+import org.argeo.ArgeoException;
+import org.argeo.cms.CmsException;
+import org.argeo.jackrabbit.JackrabbitWrapper;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.osgi.framework.BundleContext;
+import org.xml.sax.InputSource;
+
+/** Jacrabbit based data layer */
+class NodeRepository extends JackrabbitWrapper implements KernelConstants,
+ ArgeoJcrConstants {
+ private static Log log = LogFactory.getLog(NodeRepository.class);
+
+ private RepositoryContext repositoryContext;
+
+ public NodeRepository(BundleContext bundleContext) {
+ setBundleContext(bundleContext);
+ JackrabbitNodeType type = JackrabbitNodeType.valueOf(prop(REPO_TYPE,
+ h2.name()));
+ try {
+ repositoryContext = createNode(type);
+ setCndFiles(Arrays.asList(DEFAULT_CNDS));
+ prepareDataModel();
+ } catch (Exception e) {
+ throw new ArgeoException(
+ "Cannot create Jackrabbit repository of type " + type, e);
+ }
+ }
+
+ public void destroy() {
+ ((RepositoryImpl) getRepository()).shutdown();
+ }
+
+ RepositoryStatisticsImpl getRepositoryStatistics() {
+ return repositoryContext.getRepositoryStatistics();
+ }
+
+ private RepositoryConfig getConfiguration(JackrabbitNodeType type,
+ Hashtable<String, Object> vars) throws RepositoryException {
+ ClassLoader cl = getClass().getClassLoader();
+ InputStream in = null;
+ try {
+ final String base = "/org/argeo/cms/internal/kernel";
+ switch (type) {
+ case h2:
+ in = cl.getResourceAsStream(base + "/repository-h2.xml");
+ break;
+ case postgresql:
+ in = cl.getResourceAsStream(base + "/repository-postgresql.xml");
+ break;
+ case memory:
+ in = cl.getResourceAsStream(base + "/repository-memory.xml");
+ break;
+ default:
+ throw new CmsException("Unsupported node type " + type);
+ }
+
+ if (in == null)
+ throw new CmsException("Repository configuration not found");
+ InputSource config = new InputSource(in);
+ Properties jackrabbitProps = new Properties();
+ jackrabbitProps.putAll(vars);
+ RepositoryConfig repositoryConfig = RepositoryConfig.create(config,
+ jackrabbitProps);
+ return repositoryConfig;
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+ private Hashtable<String, Object> getConfigurationProperties(
+ JackrabbitNodeType type) {
+ // use Hashtable to ease integration with Properties
+ Hashtable<String, Object> defaults = new Hashtable<String, Object>();
+
+ // home
+ File osgiInstanceDir = KernelUtils.getOsgiInstanceDir();
+ File homeDir = new File(osgiInstanceDir, "node");
+ // home cannot be overridden
+ defaults.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
+ homeDir.getAbsolutePath());
+
+ // common
+ setProp(defaults, REPO_DEFAULT_WORKSPACE, "main");
+ setProp(defaults, REPO_MAX_POOL_SIZE, "10");
+ // Jackrabbit defaults
+ setProp(defaults, REPO_BUNDLE_CACHE_MB, "8");
+ // See http://wiki.apache.org/jackrabbit/Search
+ setProp(defaults, REPO_EXTRACTOR_POOL_SIZE, "0");
+ setProp(defaults, REPO_SEARCH_CACHE_SIZE, "1000");
+ setProp(defaults, REPO_MAX_VOLATILE_INDEX_SIZE, "1048576");
+
+ // specific
+ String dburl;
+ switch (type) {
+ case h2:
+ dburl = "jdbc:h2:" + homeDir.getPath() + "/h2/repository";
+ setProp(defaults, REPO_DBURL, dburl);
+ setProp(defaults, REPO_DBUSER, "sa");
+ setProp(defaults, REPO_DBPASSWORD, "");
+ break;
+ case postgresql:
+ dburl = "jdbc:postgresql://localhost/demo";
+ setProp(defaults, REPO_DBURL, dburl);
+ setProp(defaults, REPO_DBUSER, "argeo");
+ setProp(defaults, REPO_DBPASSWORD, "argeo");
+ break;
+ case memory:
+ break;
+ default:
+ throw new CmsException("Unsupported node type " + type);
+ }
+ return defaults;
+ }
+
+ private void setProp(Dictionary<String, Object> props, String key,
+ String defaultValue) {
+ String value = prop(key, defaultValue);
+ props.put(key, value);
+ }
+
+ private String prop(String key, String defaultValue) {
+ // TODO use OSGi CM instead of Framework/System properties
+ return KernelUtils.getFrameworkProp(key, defaultValue);
+ }
+
+ private RepositoryContext createNode(JackrabbitNodeType type)
+ throws RepositoryException {
+ Hashtable<String, Object> vars = getConfigurationProperties(type);
+ RepositoryConfig repositoryConfig = getConfiguration(type, vars);
+ RepositoryContext repositoryContext = createJackrabbitRepository(repositoryConfig);
+ RepositoryImpl repository = repositoryContext.getRepository();
+
+ // cache
+ String maxCacheMbStr = prop(REPO_MAX_CACHE_MB, null);
+ if (maxCacheMbStr != null) {
+ Integer maxCacheMB = Integer.parseInt(maxCacheMbStr);
+ CacheManager cacheManager = repository.getCacheManager();
+ cacheManager.setMaxMemory(maxCacheMB * 1024l * 1024l);
+ cacheManager.setMaxMemoryPerCache((maxCacheMB / 4) * 1024l * 1024l);
+ }
+
+ // wrap the repository
+ setRepository(repository);
+ return repositoryContext;
+ }
+
+ private RepositoryContext createJackrabbitRepository(
+ RepositoryConfig repositoryConfig) throws RepositoryException {
+ File homeDirectory = null;
+ long begin = System.currentTimeMillis();
+ //
+ // Actual repository creation
+ //
+ RepositoryContext repositoryContext = RepositoryContext
+ .create(repositoryConfig);
+
+ double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+ if (log.isTraceEnabled())
+ log.trace("Created Jackrabbit repository in " + duration
+ + " s, home: " + homeDirectory);
+
+ return repositoryContext;
+ }
+}
import org.apache.commons.logging.LogFactory;
import org.argeo.cms.CmsException;
import org.argeo.cms.auth.AuthConstants;
-import org.argeo.security.crypto.PkiUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
-/** Authentication and user management. */
+/** Low-level kernel security */
class NodeSecurity {
+ public final static int HARDENED = 3;
+ public final static int STAGING = 2;
+ public final static int DEV = 1;
+
final static String SECURITY_PROVIDER = "BC";// Bouncy Castle
private final static Log log;
}
private final Subject kernelSubject;
+ private int securityLevel = STAGING;
public NodeSecurity() {
// Configure JAAS first
System.setProperty("java.security.auth.login.config",
url.toExternalForm());
- this.kernelSubject = logKernel();
+ this.kernelSubject = logInKernel();
}
- private Subject logKernel() {
+ private Subject logInKernel() {
final Subject kernelSubject = new Subject();
createKeyStoreIfNeeded();
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
// alias
- ((NameCallback) callbacks[1]).setName(AuthConstants.ROLE_KERNEL);
+ ((NameCallback) callbacks[1])
+ .setName(AuthConstants.ROLE_KERNEL);
// store pwd
((PasswordCallback) callbacks[2]).setPassword("changeit"
.toCharArray());
KernelConstants.LOGIN_CONTEXT_KERNEL, kernelSubject);
kernelLc.logout();
} catch (LoginException e) {
- throw new CmsException("Cannot log in kernel", e);
+ throw new CmsException("Cannot log out kernel", e);
}
Security.removeProvider(SECURITY_PROVIDER);
return kernelSubject;
}
+ public synchronized int getSecurityLevel() {
+ return securityLevel;
+ }
+
+ public void setSecurityLevel(int newValue) {
+ if (newValue != STAGING || newValue != DEV)
+ throw new CmsException("Invalid value for security level "
+ + newValue);
+ if (newValue >= securityLevel)
+ throw new CmsException(
+ "Impossible to increase security level (from "
+ + securityLevel + " to " + newValue + ")");
+ securityLevel = newValue;
+ }
+
private void createKeyStoreIfNeeded() {
char[] ksPwd = "changeit".toCharArray();
char[] keyPwd = Arrays.copyOf(ksPwd, ksPwd.length);
package org.argeo.cms.internal.kernel;
+import static org.argeo.cms.internal.kernel.KernelUtils.getFrameworkProp;
+import static org.argeo.cms.internal.kernel.KernelUtils.getOsgiInstanceDir;
+
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Set;
import javax.jcr.Node;
+import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import org.osgi.service.useradmin.User;
import org.osgi.service.useradmin.UserAdmin;
+/**
+ * Aggregates multiple {@link UserDirectory} and integrates them with this node
+ * system roles.
+ */
public class NodeUserAdmin implements UserAdmin {
private final static Log log = LogFactory.getLog(NodeUserAdmin.class);
final static LdapName ROLES_BASE;
}
}
+ // DAOs
private UserAdmin nodeRoles = null;
private Map<LdapName, UserAdmin> userAdmins = new HashMap<LdapName, UserAdmin>();
+ // JCR
/** The home base path. */
private String homeBasePath = "/home";
private String peopleBasePath = ArgeoJcrConstants.PEOPLE_BASE_PATH;
+ private Repository repository;
private Session adminSession;
- public NodeUserAdmin(Session adminSession) {
- this.adminSession = adminSession;
- File osgiInstanceDir = KernelUtils.getOsgiInstanceDir();
- File nodeBaseDir = new File(osgiInstanceDir, "node");
- nodeBaseDir.mkdirs();
-
- String userAdminUri = KernelUtils
- .getFrameworkProp(KernelConstants.USERADMIN_URIS);
- if (userAdminUri == null) {
- String demoBaseDn = "dc=example,dc=com";
- File businessRolesFile = new File(nodeBaseDir, demoBaseDn + ".ldif");
- if (!businessRolesFile.exists())
- try {
- FileUtils.copyInputStreamToFile(getClass()
- .getResourceAsStream(demoBaseDn + ".ldif"),
- businessRolesFile);
- } catch (IOException e) {
- throw new CmsException("Cannot copy demo resource", e);
- }
- userAdminUri = businessRolesFile.toURI().toString();
- }
-
- String[] uris = userAdminUri.split(" ");
- for (String uri : uris) {
- URI u;
- try {
- u = new URI(uri);
- if (u.getPath() == null)
- throw new CmsException("URI " + uri
- + " must have a path in order to determine base DN");
- if (u.getScheme() == null) {
- if (uri.startsWith("/") || uri.startsWith("./")
- || uri.startsWith("../"))
- u = new File(uri).getCanonicalFile().toURI();
- else if (!uri.contains("/"))
- u = new File(nodeBaseDir, uri).getCanonicalFile()
- .toURI();
- else
- throw new CmsException("Cannot interpret " + uri
- + " as an uri");
- } else if (u.getScheme().equals("file")) {
- u = new File(u).getCanonicalFile().toURI();
- }
- } catch (Exception e) {
- throw new CmsException(
- "Cannot interpret " + uri + " as an uri", e);
- }
- Dictionary<String, ?> properties = UserAdminConf.uriAsProperties(u
- .toString());
- UserDirectory businessRoles;
- if (u.getScheme().startsWith("ldap")) {
- businessRoles = new LdapUserAdmin(properties);
- } else {
- businessRoles = new LdifUserAdmin(properties);
- }
- businessRoles.init();
- addUserAdmin(businessRoles.getBaseDn(), (UserAdmin) businessRoles);
- if (log.isDebugEnabled())
- log.debug("User directory " + businessRoles.getBaseDn() + " ["
- + u.getScheme() + "] enabled.");
+ public NodeUserAdmin(TransactionManager transactionManager,
+ Repository repository) {
+ this.repository = repository;
+ try {
+ this.adminSession = this.repository.login();
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot log-in", e);
}
- // Node roles
- String nodeRolesUri = KernelUtils
- .getFrameworkProp(KernelConstants.ROLES_URI);
- String baseNodeRoleDn = AuthConstants.ROLES_BASEDN;
- if (nodeRolesUri == null) {
- File nodeRolesFile = new File(nodeBaseDir, baseNodeRoleDn + ".ldif");
- if (!nodeRolesFile.exists())
- try {
- FileUtils.copyInputStreamToFile(getClass()
- .getResourceAsStream("demo.ldif"), nodeRolesFile);
- } catch (IOException e) {
- throw new CmsException("Cannot copy demo resource", e);
- }
- nodeRolesUri = nodeRolesFile.toURI().toString();
- }
+ // DAOs
+ File nodeBaseDir = new File(getOsgiInstanceDir(), "node");
+ nodeBaseDir.mkdirs();
+ String userAdminUri = getFrameworkProp(KernelConstants.USERADMIN_URIS);
+ initUserAdmins(userAdminUri, nodeBaseDir);
+ String nodeRolesUri = getFrameworkProp(KernelConstants.ROLES_URI);
+ initNodeRoles(nodeRolesUri, nodeBaseDir);
- Dictionary<String, ?> nodeRolesProperties = UserAdminConf
- .uriAsProperties(nodeRolesUri);
- if (!nodeRolesProperties.get(UserAdminConf.baseDn.property()).equals(
- baseNodeRoleDn)) {
- throw new CmsException("Invalid base dn for node roles");
- // TODO deal with "mounted" roles with a different baseDN
- }
- UserDirectory nodeRoles;
- if (nodeRolesUri.startsWith("ldap")) {
- nodeRoles = new LdapUserAdmin(nodeRolesProperties);
- } else {
- nodeRoles = new LdifUserAdmin(nodeRolesProperties);
+ // Transaction manager
+ ((UserDirectory) nodeRoles).setTransactionManager(transactionManager);
+ for (UserAdmin userAdmin : userAdmins.values()) {
+ if (userAdmin instanceof UserDirectory)
+ ((UserDirectory) userAdmin)
+ .setTransactionManager(transactionManager);
}
- nodeRoles.setExternalRoles(this);
- nodeRoles.init();
- addUserAdmin(baseNodeRoleDn, (UserAdmin) nodeRoles);
- if (log.isTraceEnabled())
- log.trace("Node roles enabled.");
// JCR
initJcr(adminSession);
// USER ADMIN AGGREGATOR
//
public synchronized void addUserAdmin(String baseDn, UserAdmin userAdmin) {
- if (baseDn.equals(AuthConstants.ROLES_BASEDN)) {
- nodeRoles = userAdmin;
- return;
- }
-
if (userAdmins.containsKey(baseDn))
throw new UserDirectoryException(
"There is already a user admin for " + baseDn);
}
}
- public synchronized void removeUserAdmin(String baseDn) {
- if (baseDn.equals(AuthConstants.ROLES_BASEDN))
- throw new UserDirectoryException("Node roles cannot be removed.");
- LdapName base;
- try {
- base = new LdapName(baseDn);
- } catch (InvalidNameException e) {
- throw new UserDirectoryException("Badly formatted base DN "
- + baseDn, e);
- }
- if (!userAdmins.containsKey(base))
- throw new UserDirectoryException("There is no user admin for "
- + base);
- userAdmins.remove(base);
- }
-
private UserAdmin findUserAdmin(String name) {
try {
return findUserAdmin(new LdapName(name));
}
}
+ private void initUserAdmins(String userAdminUri, File nodeBaseDir) {
+ if (userAdminUri == null) {
+ String demoBaseDn = "dc=example,dc=com";
+ File businessRolesFile = new File(nodeBaseDir, demoBaseDn + ".ldif");
+ if (!businessRolesFile.exists())
+ try {
+ FileUtils.copyInputStreamToFile(getClass()
+ .getResourceAsStream(demoBaseDn + ".ldif"),
+ businessRolesFile);
+ } catch (IOException e) {
+ throw new CmsException("Cannot copy demo resource", e);
+ }
+ userAdminUri = businessRolesFile.toURI().toString();
+ }
+ String[] uris = userAdminUri.split(" ");
+ for (String uri : uris) {
+ URI u;
+ try {
+ u = new URI(uri);
+ if (u.getPath() == null)
+ throw new CmsException("URI " + uri
+ + " must have a path in order to determine base DN");
+ if (u.getScheme() == null) {
+ if (uri.startsWith("/") || uri.startsWith("./")
+ || uri.startsWith("../"))
+ u = new File(uri).getCanonicalFile().toURI();
+ else if (!uri.contains("/"))
+ u = new File(nodeBaseDir, uri).getCanonicalFile()
+ .toURI();
+ else
+ throw new CmsException("Cannot interpret " + uri
+ + " as an uri");
+ } else if (u.getScheme().equals("file")) {
+ u = new File(u).getCanonicalFile().toURI();
+ }
+ } catch (Exception e) {
+ throw new CmsException(
+ "Cannot interpret " + uri + " as an uri", e);
+ }
+ Dictionary<String, ?> properties = UserAdminConf.uriAsProperties(u
+ .toString());
+ UserDirectory businessRoles;
+ if (u.getScheme().startsWith("ldap")) {
+ businessRoles = new LdapUserAdmin(properties);
+ } else {
+ businessRoles = new LdifUserAdmin(properties);
+ }
+ businessRoles.init();
+ String baseDn = businessRoles.getBaseDn();
+ if (userAdmins.containsKey(baseDn))
+ throw new UserDirectoryException(
+ "There is already a user admin for " + baseDn);
+ try {
+ userAdmins.put(new LdapName(baseDn), (UserAdmin) businessRoles);
+ } catch (InvalidNameException e) {
+ throw new UserDirectoryException("Badly formatted base DN "
+ + baseDn, e);
+ }
+ addUserAdmin(businessRoles.getBaseDn(), (UserAdmin) businessRoles);
+ if (log.isDebugEnabled())
+ log.debug("User directory " + businessRoles.getBaseDn() + " ["
+ + u.getScheme() + "] enabled.");
+ }
+
+ }
+
+ private void initNodeRoles(String nodeRolesUri, File nodeBaseDir) {
+ String baseNodeRoleDn = AuthConstants.ROLES_BASEDN;
+ if (nodeRolesUri == null) {
+ File nodeRolesFile = new File(nodeBaseDir, baseNodeRoleDn + ".ldif");
+ if (!nodeRolesFile.exists())
+ try {
+ FileUtils.copyInputStreamToFile(getClass()
+ .getResourceAsStream("demo.ldif"), nodeRolesFile);
+ } catch (IOException e) {
+ throw new CmsException("Cannot copy demo resource", e);
+ }
+ nodeRolesUri = nodeRolesFile.toURI().toString();
+ }
+
+ Dictionary<String, ?> nodeRolesProperties = UserAdminConf
+ .uriAsProperties(nodeRolesUri);
+ if (!nodeRolesProperties.get(UserAdminConf.baseDn.property()).equals(
+ baseNodeRoleDn)) {
+ throw new CmsException("Invalid base dn for node roles");
+ // TODO deal with "mounted" roles with a different baseDN
+ }
+ if (nodeRolesUri.startsWith("ldap")) {
+ nodeRoles = new LdapUserAdmin(nodeRolesProperties);
+ } else {
+ nodeRoles = new LdifUserAdmin(nodeRolesProperties);
+ }
+ ((UserDirectory) nodeRoles).setExternalRoles(this);
+ ((UserDirectory) nodeRoles).init();
+ addUserAdmin(baseNodeRoleDn, (UserAdmin) nodeRoles);
+ if (log.isTraceEnabled())
+ log.trace("Node roles enabled.");
+
+ }
+
/*
* JCR
*/
--- /dev/null
+package org.argeo.cms.internal.kernel;
+
+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.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.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);
+ }
+ }
+
+}
};
KEYRING {
- org.argeo.security.crypto.KeyringLoginModule required;
+ org.argeo.util.security.KeyringLoginModule required;
};
SINGLE_USER {
return transaction;
}
- public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
+ public TransactionSynchronizationRegistry getTsr() {
return syncRegistry;
}
/** Static utilities for the CMS framework. */
public class CmsUtils implements CmsConstants {
- /** The CMS view related to this display. */
+ /**
+ * The CMS view related to this display, or null if none is available from
+ * this call.
+ */
public static CmsView getCmsView() {
Display display = Display.getCurrent();
if (display == null)
- throw new CmsException("No display available");
+ return null;
CmsView cmsView = (CmsView) display.getData(CmsView.KEY);
if (cmsView == null)
- throw new CmsException("No CMS view available");
+ return null;
return cmsView;
}
import org.argeo.cms.CmsStyles;
import org.argeo.cms.CmsView;
import org.argeo.cms.auth.AuthConstants;
-import org.argeo.security.SecurityUtils;
+import org.argeo.cms.auth.CurrentUser;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU);
- String username = SecurityUtils.getUsername(CmsUtils.getCmsView().getSubject());
+ String username = CurrentUser.getUsername(CmsUtils.getCmsView().getSubject());
if (username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS)) {
username = null;
anonymousUi();
import org.argeo.cms.CmsMsg;
import org.argeo.cms.CmsStyles;
import org.argeo.cms.auth.AuthConstants;
-import org.argeo.security.SecurityUtils;
+import org.argeo.cms.auth.CurrentUser;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
@Override
public Control createUi(Composite parent, Node context) {
- String username = SecurityUtils.getUsername(CmsUtils.getCmsView()
+ String username = CurrentUser.getUsername(CmsUtils.getCmsView()
.getSubject());
if (username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS))
setLabel(CmsMsg.login.lead());
else {
- setLabel(SecurityUtils.getDisplayName(CmsUtils.getCmsView()
+ setLabel(CurrentUser.getDisplayName(CmsUtils.getCmsView()
.getSubject()));
}
Label link = (Label) ((Composite) super.createUi(parent, context))
filter="(argeo.jcr.repository.alias=node)" />\r
<reference id="repositoryFactory" interface="javax.jcr.RepositoryFactory" />\r
\r
- <reference id="keyring" interface="org.argeo.security.crypto.CryptoKeyring" />\r
+ <reference id="keyring" interface="org.argeo.util.security.CryptoKeyring" />\r
\r
</beans:beans>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
-
- <!-- Log4j appender singleton -->
- <bean id="secureLogger" class="org.argeo.security.log4j.SecureLogger"
- init-method="init" destroy-method="destroy">
-<!-- <property name="configuration"> -->
-<!-- <value><![CDATA[ -->
-<!-- log4j.rootLogger=WARN, console -->
-
-<!-- ## Levels -->
-<!-- log4j.logger.org.argeo=DEBUG -->
-<!-- log4j.logger.org.argeo.jackrabbit.remote.ExtendedDispatcherServlet=ERROR -->
-<!-- log4j.logger.org.springframework.web.servlet.PageNotFound=ERROR -->
-<!-- log4j.logger.org.argeo.server.webextender.TomcatDeployer=WARN -->
-
-<!-- log4j.logger.org.apache.coyote=INFO -->
-<!-- log4j.logger.org.apache.catalina.core.ContainerBase=INFO -->
-<!-- log4j.logger.org.apache.directory.server=ERROR -->
-<!-- log4j.logger.org.apache.jackrabbit.core.query.lucene=ERROR -->
-<!-- log4j.logger.org.apache.jackrabbit.core.config.ConfigurationErrorHandler=ERROR -->
-<!-- log4j.logger.org.apache.jackrabbit.core.util.db.DbUtility=FATAL -->
-<!-- log4j.logger.org.apache.activemq=INFO -->
-<!-- log4j.logger.org.apache.activemq.ActiveMQMessageConsumer=INFO -->
-<!-- log4j.logger.org.apache.activemq.ActiveMQMessageProducer=INFO -->
-
-<!-- log4j.appender.console=org.apache.log4j.ConsoleAppender -->
-<!-- log4j.appender.console.layout=org.apache.log4j.PatternLayout -->
-<!-- log4j.appender.console.layout.ConversionPattern=%d{yyMMdd HH:mm:ss} %-5p %m [%t] %c%n -->
-<!-- ]]></value> -->
-<!-- </property> -->
- </bean>
-</beans>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>\r
-<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
- xmlns:osgi="http://www.springframework.org/schema/osgi"\r
- xsi:schemaLocation="http://www.springframework.org/schema/osgi \r
- http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
- http://www.springframework.org/schema/beans \r
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
-\r
- <!-- SERVICES -->\r
- <service ref="secureLogger" interface="org.argeo.ArgeoLogger" />\r
-</beans:beans>
\ No newline at end of file
-Bundle-ActivationPolicy: lazy
Import-Package:org.bouncycastle.*;resolution:=optional,\
javax.jcr.security,\
org.apache.commons.codec,\
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.crypto;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-
-import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.PBEParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import javax.xml.bind.DatatypeConverter;
-
-import junit.framework.TestCase;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.StreamUtils;
-import org.argeo.security.crypto.PasswordBasedEncryption;
-
-public class PasswordBasedEncryptionTest extends TestCase {
- private final static Log log = LogFactory
- .getLog(PasswordBasedEncryptionTest.class);
-
- public void testEncryptDecrypt() {
- final String password = "test long password since they are safer";
- PasswordBasedEncryption pbeEnc = new PasswordBasedEncryption(
- password.toCharArray());
- String message = "Hello World!";
- log.info("Password:\t'" + password + "'");
- log.info("Message:\t'" + message + "'");
- byte[] encrypted = pbeEnc.encryptString(message);
- log.info("Encrypted:\t'"
- + DatatypeConverter.printBase64Binary(encrypted) + "'");
- PasswordBasedEncryption pbeDec = new PasswordBasedEncryption(
- password.toCharArray());
- InputStream in = null;
- in = new ByteArrayInputStream(encrypted);
- String decrypted = pbeDec.decryptAsString(in);
- log.info("Decrypted:\t'" + decrypted + "'");
- StreamUtils.closeQuietly(in);
- assertEquals(message, decrypted);
- }
-
- public void testPBEWithMD5AndDES() throws Exception {
- String password = "test";
- String message = "Hello World!";
-
- byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
- (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
-
- int count = 1024;
-
- String cipherAlgorithm = "PBEWithMD5AndDES";
- String secretKeyAlgorithm = "PBEWithMD5AndDES";
- SecretKeyFactory keyFac = SecretKeyFactory
- .getInstance(secretKeyAlgorithm);
- PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
- PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
- SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
- Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
- ecipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
- Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
- dcipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
-
- byte[] encrypted = ecipher.doFinal(message.getBytes());
- byte[] decrypted = dcipher.doFinal(encrypted);
- assertEquals(message, new String(decrypted));
-
- }
-
- public void testPBEWithSHA1AndAES() throws Exception {
- String password = "test";
- String message = "Hello World!";
-
- byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
- (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
- byte[] iv = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
- (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99,
- (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
- (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
-
- int count = 1024;
- // int keyLength = 256;
- int keyLength = 128;
-
- String cipherAlgorithm = "AES/CBC/PKCS5Padding";
- String secretKeyAlgorithm = "PBKDF2WithHmacSHA1";
- SecretKeyFactory keyFac = SecretKeyFactory
- .getInstance(secretKeyAlgorithm);
- PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt,
- count, keyLength);
- SecretKey tmp = keyFac.generateSecret(pbeKeySpec);
- SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
- Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
- ecipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
-
- // decrypt
- keyFac = SecretKeyFactory.getInstance(secretKeyAlgorithm);
- pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, count,
- keyLength);
- tmp = keyFac.generateSecret(pbeKeySpec);
- secret = new SecretKeySpec(tmp.getEncoded(), "AES");
- // AlgorithmParameters params = ecipher.getParameters();
- // byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
- Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
- dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
-
- byte[] encrypted = ecipher.doFinal(message.getBytes());
- byte[] decrypted = dcipher.doFinal(encrypted);
- assertEquals(message, new String(decrypted));
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- CipherOutputStream cipherOut = new CipherOutputStream(out, ecipher);
- cipherOut.write(message.getBytes());
- StreamUtils.closeQuietly(cipherOut);
- byte[] enc = out.toByteArray();
-
- ByteArrayInputStream in = new ByteArrayInputStream(enc);
- CipherInputStream cipherIn = new CipherInputStream(in, dcipher);
- ByteArrayOutputStream dec = new ByteArrayOutputStream();
- StreamUtils.copy(cipherIn, dec);
- assertEquals(message, new String(dec.toByteArray()));
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security;
-
-import java.security.AccessController;
-import java.security.Principal;
-import java.security.acl.Group;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.security.auth.Subject;
-import javax.security.auth.x500.X500Principal;
-
-import org.argeo.ArgeoException;
-import org.osgi.service.useradmin.Authorization;
-
-/** Static utilities */
-public final class SecurityUtils {
- private SecurityUtils() {
- }
-
- /** Whether the current thread has the admin role */
- public static boolean hasCurrentThreadAuthority(String authority) {
- return roles().contains(authority);
- }
-
- /**
- * @return the authenticated username or null if not authenticated /
- * anonymous
- */
- public static String getCurrentThreadUsername() {
- Subject subject = Subject.getSubject(AccessController.getContext());
- if (subject == null)
- return null;
- return getUsername(subject);
- }
-
- public final static String getUsername(Subject subject) {
- // Subject subject = Subject.getSubject(AccessController.getContext());
- // if (subject == null)
- // return null;
- if (subject.getPrincipals(X500Principal.class).size() != 1)
- return null;
- Principal principal = subject.getPrincipals(X500Principal.class)
- .iterator().next();
- return principal.getName();
-
- }
-
- public final static String getDisplayName(Subject subject) {
- return getAuthorization(subject).toString();
- }
-
- public final static Authorization getAuthorization(Subject subject) {
- return subject.getPrivateCredentials(Authorization.class).iterator()
- .next();
- }
-
- public final static Set<String> roles() {
- Set<String> roles = Collections.synchronizedSet(new HashSet<String>());
- Subject subject = Subject.getSubject(AccessController.getContext());
- if (subject == null)
- throw new ArgeoException("Not authenticated.");
- X500Principal userPrincipal = subject
- .getPrincipals(X500Principal.class).iterator().next();
- roles.add(userPrincipal.getName());
- for (Principal group : subject.getPrincipals(Group.class)) {
- roles.add(group.getName());
- }
- return roles;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.crypto;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.CharArrayWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.security.AccessController;
-import java.security.MessageDigest;
-import java.security.Provider;
-import java.security.Security;
-import java.util.Arrays;
-import java.util.Iterator;
-
-import javax.crypto.SecretKey;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.TextOutputCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.argeo.ArgeoException;
-import org.argeo.StreamUtils;
-import org.argeo.util.security.Keyring;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-/** username / password based keyring. TODO internationalize */
-public abstract class AbstractKeyring implements Keyring, CryptoKeyring {
- static {
- Security.addProvider(new BouncyCastleProvider());
- }
-
- public final static String DEFAULT_KEYRING_LOGIN_CONTEXT = "KEYRING";
-
- private String loginContextName = DEFAULT_KEYRING_LOGIN_CONTEXT;
- private CallbackHandler defaultCallbackHandler;
-
- private String charset = "UTF-8";
-
- /**
- * Default provider is bouncy castle, in order to have consistent behaviour
- * across implementations
- */
- private String securityProviderName = "BC";
-
- /**
- * Whether the keyring has already been created in the past with a master
- * password
- */
- protected abstract Boolean isSetup();
-
- /**
- * Setup the keyring persistently, {@link #isSetup()} must return true
- * afterwards
- */
- protected abstract void setup(char[] password);
-
- /** Populates the key spec callback */
- protected abstract void handleKeySpecCallback(PBEKeySpecCallback pbeCallback);
-
- protected abstract void encrypt(String path, InputStream unencrypted);
-
- protected abstract InputStream decrypt(String path);
-
- /** Triggers lazy initialization */
- protected SecretKey getSecretKey() {
- Subject subject = Subject.getSubject(AccessController.getContext());
- // we assume only one secrete key is available
- Iterator<SecretKey> iterator = subject.getPrivateCredentials(
- SecretKey.class).iterator();
- if (!iterator.hasNext()) {// not initialized
- CallbackHandler callbackHandler = new KeyringCallbackHandler();
- try {
- LoginContext loginContext = new LoginContext(loginContextName,
- subject, callbackHandler);
- loginContext.login();
- // FIXME will login even if password is wrong
- iterator = subject.getPrivateCredentials(SecretKey.class)
- .iterator();
- return iterator.next();
- } catch (LoginException e) {
- throw new ArgeoException("Keyring login failed", e);
- }
-
- } else {
- SecretKey secretKey = iterator.next();
- if (iterator.hasNext())
- throw new ArgeoException(
- "More than one secret key in private credentials");
- return secretKey;
- }
- }
-
- public InputStream getAsStream(String path) {
- return decrypt(path);
- }
-
- public void set(String path, InputStream in) {
- encrypt(path, in);
- }
-
- public char[] getAsChars(String path) {
- InputStream in = getAsStream(path);
- CharArrayWriter writer = null;
- Reader reader = null;
- try {
- writer = new CharArrayWriter();
- reader = new InputStreamReader(in, charset);
- StreamUtils.copy(reader, writer);
- return writer.toCharArray();
- } catch (IOException e) {
- throw new ArgeoException("Cannot decrypt to char array", e);
- } finally {
- StreamUtils.closeQuietly(reader);
- StreamUtils.closeQuietly(in);
- StreamUtils.closeQuietly(writer);
- }
- }
-
- public void set(String path, char[] arr) {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = null;
- Writer writer = null;
- try {
- writer = new OutputStreamWriter(out, charset);
- writer.write(arr);
- writer.flush();
- in = new ByteArrayInputStream(out.toByteArray());
- set(path, in);
- } catch (IOException e) {
- throw new ArgeoException("Cannot encrypt to char array", e);
- } finally {
- StreamUtils.closeQuietly(writer);
- StreamUtils.closeQuietly(out);
- StreamUtils.closeQuietly(in);
- }
- }
-
- protected Provider getSecurityProvider() {
- return Security.getProvider(securityProviderName);
- }
-
- public void setLoginContextName(String loginContextName) {
- this.loginContextName = loginContextName;
- }
-
- public void setDefaultCallbackHandler(CallbackHandler defaultCallbackHandler) {
- this.defaultCallbackHandler = defaultCallbackHandler;
- }
-
- public void setCharset(String charset) {
- this.charset = charset;
- }
-
- public void setSecurityProviderName(String securityProviderName) {
- this.securityProviderName = securityProviderName;
- }
-
- @Deprecated
- protected static byte[] hash(char[] password, byte[] salt,
- Integer iterationCount) {
- ByteArrayOutputStream out = null;
- OutputStreamWriter writer = null;
- try {
- out = new ByteArrayOutputStream();
- writer = new OutputStreamWriter(out, "UTF-8");
- writer.write(password);
- MessageDigest pwDigest = MessageDigest.getInstance("SHA-256");
- pwDigest.reset();
- pwDigest.update(salt);
- byte[] btPass = pwDigest.digest(out.toByteArray());
- for (int i = 0; i < iterationCount; i++) {
- pwDigest.reset();
- btPass = pwDigest.digest(btPass);
- }
- return btPass;
- } catch (Exception e) {
- throw new ArgeoException("Cannot hash", e);
- } finally {
- StreamUtils.closeQuietly(out);
- StreamUtils.closeQuietly(writer);
- }
-
- }
-
- /**
- * Convenience method using the underlying callback to ask for a password
- * (typically used when the password is not saved in the keyring)
- */
- protected char[] ask() {
- PasswordCallback passwordCb = new PasswordCallback("Password", false);
- Callback[] dialogCbs = new Callback[] { passwordCb };
- try {
- defaultCallbackHandler.handle(dialogCbs);
- char[] password = passwordCb.getPassword();
- return password;
- } catch (Exception e) {
- throw new ArgeoException("Cannot ask for a password", e);
- }
-
- }
-
- class KeyringCallbackHandler implements CallbackHandler {
- public void handle(Callback[] callbacks) throws IOException,
- UnsupportedCallbackException {
- // checks
- if (callbacks.length != 2)
- throw new IllegalArgumentException(
- "Keyring required 2 and only 2 callbacks: {PasswordCallback,PBEKeySpecCallback}");
- if (!(callbacks[0] instanceof PasswordCallback))
- throw new UnsupportedCallbackException(callbacks[0]);
- if (!(callbacks[1] instanceof PBEKeySpecCallback))
- throw new UnsupportedCallbackException(callbacks[0]);
-
- PasswordCallback passwordCb = (PasswordCallback) callbacks[0];
- PBEKeySpecCallback pbeCb = (PBEKeySpecCallback) callbacks[1];
-
- if (isSetup()) {
- Callback[] dialogCbs = new Callback[] { passwordCb };
- defaultCallbackHandler.handle(dialogCbs);
- } else {// setup keyring
- TextOutputCallback textCb1 = new TextOutputCallback(
- TextOutputCallback.INFORMATION,
- "Enter a master password which will protect your private data");
- TextOutputCallback textCb2 = new TextOutputCallback(
- TextOutputCallback.INFORMATION,
- "(for example your credentials to third-party services)");
- TextOutputCallback textCb3 = new TextOutputCallback(
- TextOutputCallback.INFORMATION,
- "Don't forget this password since the data cannot be read without it");
- PasswordCallback confirmPasswordCb = new PasswordCallback(
- "Confirm password", false);
- // first try
- Callback[] dialogCbs = new Callback[] { textCb1, textCb2,
- textCb3, passwordCb, confirmPasswordCb };
- defaultCallbackHandler.handle(dialogCbs);
-
- // if passwords different, retry (except if cancelled)
- while (passwordCb.getPassword() != null
- && !Arrays.equals(passwordCb.getPassword(),
- confirmPasswordCb.getPassword())) {
- TextOutputCallback textCb = new TextOutputCallback(
- TextOutputCallback.ERROR,
- "The passwords do not match");
- dialogCbs = new Callback[] { textCb, passwordCb,
- confirmPasswordCb };
- defaultCallbackHandler.handle(dialogCbs);
- }
-
- if (passwordCb.getPassword() != null) {// not cancelled
- setup(passwordCb.getPassword());
- }
- }
-
- if (passwordCb.getPassword() != null)
- handleKeySpecCallback(pbeCb);
- }
-
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.crypto;
-
-import org.argeo.util.security.Keyring;
-
-/**
- * Advanced keyring based on cryptography that can easily be centralized and
- * coordinated with {@link KeyringLoginModule} (since they ar ein the same
- * package)
- */
-public interface CryptoKeyring extends Keyring {
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.crypto;
-
-import java.security.AccessController;
-import java.util.Map;
-import java.util.Set;
-
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.SecretKeySpec;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.spi.LoginModule;
-
-/** Adds a secret key to the private credentials */
-public class KeyringLoginModule implements LoginModule {
- private Subject subject;
- private CallbackHandler callbackHandler;
- private SecretKey secretKey;
-
- public void initialize(Subject subject, CallbackHandler callbackHandler,
- Map<String, ?> sharedState, Map<String, ?> options) {
- this.subject = subject;
- if (subject == null) {
- subject = Subject.getSubject(AccessController.getContext());
- }
- this.callbackHandler = callbackHandler;
- }
-
- public boolean login() throws LoginException {
- Set<SecretKey> pbes = subject.getPrivateCredentials(SecretKey.class);
- if (pbes.size() > 0)
- return true;
- PasswordCallback pc = new PasswordCallback("Master password", false);
- PBEKeySpecCallback pbeCb = new PBEKeySpecCallback();
- Callback[] callbacks = { pc, pbeCb };
- try {
- callbackHandler.handle(callbacks);
- char[] password = pc.getPassword();
-
- SecretKeyFactory keyFac = SecretKeyFactory.getInstance(pbeCb
- .getSecretKeyFactory());
- PBEKeySpec keySpec;
- if (pbeCb.getKeyLength() != null)
- keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
- pbeCb.getIterationCount(), pbeCb.getKeyLength());
- else
- keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
- pbeCb.getIterationCount());
-
- String secKeyEncryption = pbeCb.getSecretKeyEncryption();
- if (secKeyEncryption != null) {
- SecretKey tmp = keyFac.generateSecret(keySpec);
- secretKey = new SecretKeySpec(tmp.getEncoded(),
- secKeyEncryption);
- } else {
- secretKey = keyFac.generateSecret(keySpec);
- }
- } catch (Exception e) {
- LoginException le = new LoginException("Cannot login keyring");
- le.initCause(e);
- throw le;
- }
- return true;
- }
-
- public boolean commit() throws LoginException {
- if (secretKey != null)
- subject.getPrivateCredentials().add(secretKey);
- return true;
- }
-
- public boolean abort() throws LoginException {
- return true;
- }
-
- public boolean logout() throws LoginException {
- Set<PasswordBasedEncryption> pbes = subject
- .getPrivateCredentials(PasswordBasedEncryption.class);
- pbes.clear();
- return true;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.crypto;
-
-import javax.crypto.spec.PBEKeySpec;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.PasswordCallback;
-
-/**
- * All information required to set up a {@link PBEKeySpec} bar the password
- * itself (use a {@link PasswordCallback})
- */
-public class PBEKeySpecCallback implements Callback {
- private String secretKeyFactory;
- private byte[] salt;
- private Integer iterationCount;
- /** Can be null for some algorithms */
- private Integer keyLength;
- /** Can be null, will trigger secret key encryption if not */
- private String secretKeyEncryption;
-
- private String encryptedPasswordHashCipher;
- private byte[] encryptedPasswordHash;
-
- public void set(String secretKeyFactory, byte[] salt,
- Integer iterationCount, Integer keyLength,
- String secretKeyEncryption) {
- this.secretKeyFactory = secretKeyFactory;
- this.salt = salt;
- this.iterationCount = iterationCount;
- this.keyLength = keyLength;
- this.secretKeyEncryption = secretKeyEncryption;
-// this.encryptedPasswordHashCipher = encryptedPasswordHashCipher;
-// this.encryptedPasswordHash = encryptedPasswordHash;
- }
-
- public String getSecretKeyFactory() {
- return secretKeyFactory;
- }
-
- public byte[] getSalt() {
- return salt;
- }
-
- public Integer getIterationCount() {
- return iterationCount;
- }
-
- public Integer getKeyLength() {
- return keyLength;
- }
-
- public String getSecretKeyEncryption() {
- return secretKeyEncryption;
- }
-
- public String getEncryptedPasswordHashCipher() {
- return encryptedPasswordHashCipher;
- }
-
- public byte[] getEncryptedPasswordHash() {
- return encryptedPasswordHash;
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.crypto;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.Security;
-
-import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
-import org.argeo.StreamUtils;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-/** Simple password based encryption / decryption */
-public class PasswordBasedEncryption {
- private final static Log log = LogFactory
- .getLog(PasswordBasedEncryption.class);
-
- static {
- Security.addProvider(new BouncyCastleProvider());
- }
-
- public final static Integer DEFAULT_ITERATION_COUNT = 1024;
- /** Stronger with 256, but causes problem with Oracle JVM */
- public final static Integer DEFAULT_SECRETE_KEY_LENGTH = 256;
- public final static Integer DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED = 128;
- public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
- public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
- public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
- public final static String DEFAULT_CHARSET = "UTF-8";
-
- private Integer iterationCount = DEFAULT_ITERATION_COUNT;
- private Integer secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
- private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
- private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
- private String cipherName = DEFAULT_CIPHER_NAME;
-
- private static byte[] DEFAULT_SALT_8 = { (byte) 0xA9, (byte) 0x9B,
- (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
- (byte) 0x03 };
- private static byte[] DEFAULT_IV_16 = { (byte) 0xA9, (byte) 0x9B,
- (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
- (byte) 0x03, (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
- (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03 };
-
- private Key key;
- private Cipher ecipher;
- private Cipher dcipher;
-
- /**
- * Default provider is bouncy castle, in order to have consistent behaviour
- * across implementations
- */
- private String securityProviderName = "BC";
-
- /**
- * This is up to the caller to clear the passed array. Neither copy of nor
- * reference to the passed array is kept
- */
- public PasswordBasedEncryption(char[] password) {
- this(password, DEFAULT_SALT_8, DEFAULT_IV_16);
- }
-
- /**
- * This is up to the caller to clear the passed array. Neither copies of nor
- * references to the passed arrays are kept
- */
- public PasswordBasedEncryption(char[] password, byte[] passwordSalt,
- byte[] initializationVector) {
- try {
- initKeyAndCiphers(password, passwordSalt, initializationVector);
- } catch (InvalidKeyException e) {
- Integer previousSecreteKeyLength = secreteKeyLength;
- secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED;
- log.warn("'" + e.getMessage() + "', will use " + secreteKeyLength
- + " secrete key length instead of "
- + previousSecreteKeyLength);
- try {
- initKeyAndCiphers(password, passwordSalt, initializationVector);
- } catch (Exception e1) {
- throw new ArgeoException(
- "Cannot get secret key (with restricted length)", e1);
- }
- } catch (Exception e) {
- throw new ArgeoException("Cannot get secret key", e);
- }
- }
-
- protected void initKeyAndCiphers(char[] password, byte[] passwordSalt,
- byte[] initializationVector) throws GeneralSecurityException {
- byte[] salt = new byte[8];
- System.arraycopy(passwordSalt, 0, salt, 0, salt.length);
- // for (int i = 0; i < password.length && i < salt.length; i++)
- // salt[i] = (byte) password[i];
- byte[] iv = new byte[16];
- System.arraycopy(initializationVector, 0, iv, 0, iv.length);
-
- SecretKeyFactory keyFac = SecretKeyFactory
- .getInstance(getSecretKeyFactoryName());
- PBEKeySpec keySpec = new PBEKeySpec(password, salt,
- getIterationCount(), getKeyLength());
- String secKeyEncryption = getSecretKeyEncryption();
- if (secKeyEncryption != null) {
- SecretKey tmp = keyFac.generateSecret(keySpec);
- key = new SecretKeySpec(tmp.getEncoded(), getSecretKeyEncryption());
- } else {
- key = keyFac.generateSecret(keySpec);
- }
- ecipher = Cipher.getInstance(getCipherName(), securityProviderName);
- ecipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
- dcipher = Cipher.getInstance(getCipherName());
- dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
- }
-
- public void encrypt(InputStream decryptedIn, OutputStream encryptedOut)
- throws IOException {
- try {
- CipherOutputStream out = new CipherOutputStream(encryptedOut,
- ecipher);
- StreamUtils.copy(decryptedIn, out);
- StreamUtils.closeQuietly(out);
- } catch (IOException e) {
- throw e;
- } catch (Exception e) {
- throw new ArgeoException("Cannot encrypt", e);
- } finally {
- StreamUtils.closeQuietly(decryptedIn);
- }
- }
-
- public void decrypt(InputStream encryptedIn, OutputStream decryptedOut)
- throws IOException {
- try {
- CipherInputStream decryptedIn = new CipherInputStream(encryptedIn,
- dcipher);
- StreamUtils.copy(decryptedIn, decryptedOut);
- } catch (IOException e) {
- throw e;
- } catch (Exception e) {
- throw new ArgeoException("Cannot decrypt", e);
- } finally {
- StreamUtils.closeQuietly(encryptedIn);
- }
- }
-
- public byte[] encryptString(String str) {
- ByteArrayOutputStream out = null;
- ByteArrayInputStream in = null;
- try {
- out = new ByteArrayOutputStream();
- in = new ByteArrayInputStream(str.getBytes(DEFAULT_CHARSET));
- encrypt(in, out);
- return out.toByteArray();
- } catch (Exception e) {
- throw new ArgeoException("Cannot encrypt", e);
- } finally {
- StreamUtils.closeQuietly(out);
- }
- }
-
- /** Closes the input stream */
- public String decryptAsString(InputStream in) {
- ByteArrayOutputStream out = null;
- try {
- out = new ByteArrayOutputStream();
- decrypt(in, out);
- return new String(out.toByteArray(), DEFAULT_CHARSET);
- } catch (Exception e) {
- throw new ArgeoException("Cannot decrypt", e);
- } finally {
- StreamUtils.closeQuietly(out);
- }
- }
-
- protected Key getKey() {
- return key;
- }
-
- protected Cipher getEcipher() {
- return ecipher;
- }
-
- protected Cipher getDcipher() {
- return dcipher;
- }
-
- protected Integer getIterationCount() {
- return iterationCount;
- }
-
- protected Integer getKeyLength() {
- return secreteKeyLength;
- }
-
- protected String getSecretKeyFactoryName() {
- return secreteKeyFactoryName;
- }
-
- protected String getSecretKeyEncryption() {
- return secreteKeyEncryption;
- }
-
- protected String getCipherName() {
- return cipherName;
- }
-
- public void setIterationCount(Integer iterationCount) {
- this.iterationCount = iterationCount;
- }
-
- public void setSecreteKeyLength(Integer keyLength) {
- this.secreteKeyLength = keyLength;
- }
-
- public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
- this.secreteKeyFactoryName = secreteKeyFactoryName;
- }
-
- public void setSecreteKeyEncryption(String secreteKeyEncryption) {
- this.secreteKeyEncryption = secreteKeyEncryption;
- }
-
- public void setCipherName(String cipherName) {
- this.cipherName = cipherName;
- }
-
- public void setSecurityProviderName(String securityProviderName) {
- this.securityProviderName = securityProviderName;
- }
-}
+++ /dev/null
-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.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.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);
- }
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.jcr;
-
-import java.io.ByteArrayInputStream;
-import java.io.CharArrayReader;
-import java.io.InputStream;
-import java.io.Reader;
-import java.security.SecureRandom;
-
-import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.ArgeoTypes;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.jcr.UserJcrUtils;
-import org.argeo.security.crypto.AbstractKeyring;
-import org.argeo.security.crypto.PBEKeySpecCallback;
-
-/** JCR based implementation of a keyring */
-public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
- /**
- * Stronger with 256, but causes problem with Oracle JVM, force 128 in this
- * case
- */
- public final static Long DEFAULT_SECRETE_KEY_LENGTH = 256l;
- public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
- public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
- public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
-
- private Integer iterationCountFactor = 200;
- private Long secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
- private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
- private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
- private String cipherName = DEFAULT_CIPHER_NAME;
-
- private Session session;
-
- /**
- * When setup is called the session has not yet been saved and we don't want
- * to save it since there maybe other data which would be inconsistent. So
- * we keep a reference to this node which will then be used (an reset to
- * null) when handling the PBE callback. We keep one per thread in case
- * multiple users are accessing the same instance of a keyring.
- */
- private ThreadLocal<Node> notYetSavedKeyring = new ThreadLocal<Node>() {
-
- @Override
- protected Node initialValue() {
- return null;
- }
- };
-
- @Override
- protected Boolean isSetup() {
- try {
- if (notYetSavedKeyring.get() != null)
- return true;
-
- Node userHome = UserJcrUtils.getUserHome(session);
- return userHome.hasNode(ARGEO_KEYRING);
- } catch (RepositoryException e) {
- throw new ArgeoException("Cannot check whether keyring is setup", e);
- }
- }
-
- @Override
- protected void setup(char[] password) {
- Binary binary = null;
- InputStream in = null;
- try {
- Node userHome = UserJcrUtils.getUserHome(session);
- if (userHome.hasNode(ARGEO_KEYRING))
- throw new ArgeoException("Keyring already setup");
- Node keyring = userHome.addNode(ARGEO_KEYRING);
- keyring.addMixin(ArgeoTypes.ARGEO_PBE_SPEC);
-
- // deterministic salt and iteration count based on username
- String username = session.getUserID();
- byte[] salt = new byte[8];
- byte[] usernameBytes = username.getBytes();
- for (int i = 0; i < salt.length; i++) {
- if (i < usernameBytes.length)
- salt[i] = usernameBytes[i];
- else
- salt[i] = 0;
- }
- in = new ByteArrayInputStream(salt);
- binary = session.getValueFactory().createBinary(in);
- keyring.setProperty(ARGEO_SALT, binary);
-
- Integer iterationCount = username.length() * iterationCountFactor;
- keyring.setProperty(ARGEO_ITERATION_COUNT, iterationCount);
-
- // default algo
- // TODO check if algo and key length are available, use DES if not
- keyring.setProperty(ARGEO_SECRET_KEY_FACTORY, secreteKeyFactoryName);
- keyring.setProperty(ARGEO_KEY_LENGTH, secreteKeyLength);
- keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION,
- secreteKeyEncryption);
- keyring.setProperty(ARGEO_CIPHER, cipherName);
-
- // encrypted password hash
- // IOUtils.closeQuietly(in);
- // JcrUtils.closeQuietly(binary);
- // byte[] btPass = hash(password, salt, iterationCount);
- // in = new ByteArrayInputStream(btPass);
- // binary = session.getValueFactory().createBinary(in);
- // keyring.setProperty(ARGEO_PASSWORD, binary);
-
- notYetSavedKeyring.set(keyring);
- } catch (Exception e) {
- throw new ArgeoException("Cannot setup keyring", e);
- } finally {
- JcrUtils.closeQuietly(binary);
- IOUtils.closeQuietly(in);
- // JcrUtils.discardQuietly(session);
- }
- }
-
- @Override
- protected void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) {
- try {
- Node userHome = UserJcrUtils.getUserHome(session);
- Node keyring;
- if (userHome.hasNode(ARGEO_KEYRING))
- keyring = userHome.getNode(ARGEO_KEYRING);
- else if (notYetSavedKeyring.get() != null)
- keyring = notYetSavedKeyring.get();
- else
- throw new ArgeoException("Keyring not setup");
-
- pbeCallback.set(keyring.getProperty(ARGEO_SECRET_KEY_FACTORY)
- .getString(), JcrUtils.getBinaryAsBytes(keyring
- .getProperty(ARGEO_SALT)),
- (int) keyring.getProperty(ARGEO_ITERATION_COUNT).getLong(),
- (int) keyring.getProperty(ARGEO_KEY_LENGTH).getLong(),
- keyring.getProperty(ARGEO_SECRET_KEY_ENCRYPTION)
- .getString());
-
- if (notYetSavedKeyring.get() != null)
- notYetSavedKeyring.remove();
- } catch (RepositoryException e) {
- throw new ArgeoException("Cannot handle key spec callback", e);
- }
- }
-
- /** The parent node must already exist at this path. */
- @Override
- protected synchronized void encrypt(String path, InputStream unencrypted) {
- // should be called first for lazy initialization
- SecretKey secretKey = getSecretKey();
-
- Binary binary = null;
- InputStream in = null;
- try {
- Cipher cipher = createCipher();
- Node node;
- if (!session.nodeExists(path)) {
- String parentPath = JcrUtils.parentPath(path);
- if (!session.nodeExists(parentPath))
- throw new ArgeoException("No parent node of " + path);
- Node parentNode = session.getNode(parentPath);
- node = parentNode.addNode(JcrUtils.nodeNameFromPath(path));
- } else {
- node = session.getNode(path);
- }
- node.addMixin(ArgeoTypes.ARGEO_ENCRYPTED);
- SecureRandom random = new SecureRandom();
- byte[] iv = new byte[16];
- random.nextBytes(iv);
- cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
- JcrUtils.setBinaryAsBytes(node, ARGEO_IV, iv);
-
- in = new CipherInputStream(unencrypted, cipher);
- binary = session.getValueFactory().createBinary(in);
- node.setProperty(Property.JCR_DATA, binary);
- session.save();
- } catch (Exception e) {
- throw new ArgeoException("Cannot encrypt", e);
- } finally {
- IOUtils.closeQuietly(unencrypted);
- IOUtils.closeQuietly(in);
- JcrUtils.closeQuietly(binary);
- }
- }
-
- @Override
- protected synchronized InputStream decrypt(String path) {
- Binary binary = null;
- InputStream encrypted = null;
- Reader reader = null;
- try {
- if (!session.nodeExists(path)) {
- char[] password = ask();
- reader = new CharArrayReader(password);
- return new ByteArrayInputStream(IOUtils.toByteArray(reader));
- } else {
- // should be called first for lazy initialisation
- SecretKey secretKey = getSecretKey();
-
- Cipher cipher = createCipher();
-
- Node node = session.getNode(path);
- if (node.hasProperty(ARGEO_IV)) {
- byte[] iv = JcrUtils.getBinaryAsBytes(node
- .getProperty(ARGEO_IV));
- cipher.init(Cipher.DECRYPT_MODE, secretKey,
- new IvParameterSpec(iv));
- } else {
- cipher.init(Cipher.DECRYPT_MODE, secretKey);
- }
-
- binary = node.getProperty(Property.JCR_DATA).getBinary();
- encrypted = binary.getStream();
- return new CipherInputStream(encrypted, cipher);
- }
- } catch (Exception e) {
- throw new ArgeoException("Cannot decrypt", e);
- } finally {
- IOUtils.closeQuietly(encrypted);
- IOUtils.closeQuietly(reader);
- JcrUtils.closeQuietly(binary);
- }
- }
-
- protected Cipher createCipher() {
- try {
- Node userHome = UserJcrUtils.getUserHome(session);
- if (!userHome.hasNode(ARGEO_KEYRING))
- throw new ArgeoException("Keyring not setup");
- Node keyring = userHome.getNode(ARGEO_KEYRING);
- Cipher cipher = Cipher.getInstance(keyring
- .getProperty(ARGEO_CIPHER).getString(),
- getSecurityProvider());
- return cipher;
- } catch (Exception e) {
- throw new ArgeoException("Cannot get cipher", e);
- }
- }
-
- public synchronized void changePassword(char[] oldPassword,
- char[] newPassword) {
- // TODO decrypt with old pw / encrypt with new pw all argeo:encrypted
- }
-
- public synchronized void setSession(Session session) {
- this.session = session;
- }
-
- public void setIterationCountFactor(Integer iterationCountFactor) {
- this.iterationCountFactor = iterationCountFactor;
- }
-
- public void setSecreteKeyLength(Long keyLength) {
- this.secreteKeyLength = keyLength;
- }
-
- public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
- this.secreteKeyFactoryName = secreteKeyFactoryName;
- }
-
- public void setSecreteKeyEncryption(String secreteKeyEncryption) {
- this.secreteKeyEncryption = secreteKeyEncryption;
- }
-
- public void setCipherName(String cipherName) {
- this.cipherName = cipherName;
- }
-
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.log4j;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Properties;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-import org.apache.log4j.AppenderSkeleton;
-import org.apache.log4j.Level;
-import org.apache.log4j.LogManager;
-import org.apache.log4j.Logger;
-import org.apache.log4j.PropertyConfigurator;
-import org.apache.log4j.spi.LoggingEvent;
-import org.argeo.ArgeoException;
-import org.argeo.ArgeoLogListener;
-import org.argeo.ArgeoLogger;
-import org.argeo.security.SecurityUtils;
-
-/** Not meant to be used directly in standard log4j config */
-public class SecureLogger implements ArgeoLogger {
-
- private Boolean disabled = false;
-
- private String level = null;
-
- private Level log4jLevel = null;
- // private Layout layout;
-
- private Properties configuration;
-
- private AppenderImpl appender;
-
- private final List<ArgeoLogListener> everythingListeners = Collections
- .synchronizedList(new ArrayList<ArgeoLogListener>());
- private final List<ArgeoLogListener> allUsersListeners = Collections
- .synchronizedList(new ArrayList<ArgeoLogListener>());
- private final Map<String, List<ArgeoLogListener>> userListeners = Collections
- .synchronizedMap(new HashMap<String, List<ArgeoLogListener>>());
-
- private BlockingQueue<LogEvent> events;
- private LogDispatcherThread logDispatcherThread = new LogDispatcherThread();
-
- private Integer maxLastEventsCount = 10 * 1000;
-
- /** Marker to prevent stack overflow */
- private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
-
- @Override
- protected Boolean initialValue() {
- return false;
- }
- };
-
- public void init() {
- try {
- events = new LinkedBlockingQueue<LogEvent>();
-
- // if (layout != null)
- // setLayout(layout);
- // else
- // setLayout(new PatternLayout(pattern));
- appender = new AppenderImpl();
- reloadConfiguration();
- Logger.getRootLogger().addAppender(appender);
-
- logDispatcherThread = new LogDispatcherThread();
- logDispatcherThread.start();
- } catch (Exception e) {
- throw new ArgeoException("Cannot initialize log4j");
- }
- }
-
- public void destroy() throws Exception {
- Logger.getRootLogger().removeAppender(appender);
- allUsersListeners.clear();
- for (List<ArgeoLogListener> lst : userListeners.values())
- lst.clear();
- userListeners.clear();
-
- events.clear();
- events = null;
- logDispatcherThread.interrupt();
- }
-
- // public void setLayout(Layout layout) {
- // this.layout = layout;
- // }
-
- public synchronized void register(ArgeoLogListener listener,
- Integer numberOfPreviousEvents) {
- String username = SecurityUtils.getCurrentThreadUsername();
- if (username == null)
- throw new ArgeoException(
- "Only authenticated users can register a log listener");
-
- if (!userListeners.containsKey(username)) {
- List<ArgeoLogListener> lst = Collections
- .synchronizedList(new ArrayList<ArgeoLogListener>());
- userListeners.put(username, lst);
- }
- userListeners.get(username).add(listener);
- List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username,
- numberOfPreviousEvents);
- for (LogEvent evt : lastEvents)
- dispatchEvent(listener, evt);
- }
-
- public synchronized void registerForAll(ArgeoLogListener listener,
- Integer numberOfPreviousEvents, boolean everything) {
- if (everything)
- everythingListeners.add(listener);
- else
- allUsersListeners.add(listener);
- List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(null,
- numberOfPreviousEvents);
- for (LogEvent evt : lastEvents)
- if (everything || evt.getUsername() != null)
- dispatchEvent(listener, evt);
- }
-
- public synchronized void unregister(ArgeoLogListener listener) {
- String username = SecurityUtils.getCurrentThreadUsername();
- if (!userListeners.containsKey(username))
- throw new ArgeoException("No user listeners " + listener
- + " registered for user " + username);
- if (!userListeners.get(username).contains(listener))
- throw new ArgeoException("No user listeners " + listener
- + " registered for user " + username);
- userListeners.get(username).remove(listener);
- if (userListeners.get(username).isEmpty())
- userListeners.remove(username);
-
- }
-
- public synchronized void unregisterForAll(ArgeoLogListener listener) {
- everythingListeners.remove(listener);
- allUsersListeners.remove(listener);
- }
-
- /** For development purpose, since using regular logging is not easy here */
- static void stdOut(Object obj) {
- System.out.println(obj);
- }
-
- // public void setPattern(String pattern) {
- // this.pattern = pattern;
- // }
-
- public void setDisabled(Boolean disabled) {
- this.disabled = disabled;
- }
-
- public void setLevel(String level) {
- this.level = level;
- }
-
- public void setConfiguration(Properties configuration) {
- this.configuration = configuration;
- }
-
- public void updateConfiguration(Properties configuration) {
- setConfiguration(configuration);
- reloadConfiguration();
- }
-
- public Properties getConfiguration() {
- return configuration;
- }
-
- /** Reloads configuration (if the configuration {@link Properties} is set) */
- protected void reloadConfiguration() {
- if (configuration != null) {
- LogManager.resetConfiguration();
- PropertyConfigurator.configure(configuration);
- }
- }
-
- protected synchronized void processLoggingEvent(LogEvent event) {
- if (disabled)
- return;
-
- if (dispatching.get())
- return;
-
- if (level != null && !level.trim().equals("")) {
- if (log4jLevel == null || !log4jLevel.toString().equals(level))
- try {
- log4jLevel = Level.toLevel(level);
- } catch (Exception e) {
- System.err
- .println("Log4j level could not be set for level '"
- + level + "', resetting it to null.");
- e.printStackTrace();
- level = null;
- }
-
- if (log4jLevel != null
- && !event.getLoggingEvent().getLevel()
- .isGreaterOrEqual(log4jLevel)) {
- return;
- }
- }
-
- try {
- // admin listeners
- Iterator<ArgeoLogListener> everythingIt = everythingListeners
- .iterator();
- while (everythingIt.hasNext())
- dispatchEvent(everythingIt.next(), event);
-
- if (event.getUsername() != null) {
- Iterator<ArgeoLogListener> allUsersIt = allUsersListeners
- .iterator();
- while (allUsersIt.hasNext())
- dispatchEvent(allUsersIt.next(), event);
-
- if (userListeners.containsKey(event.getUsername())) {
- Iterator<ArgeoLogListener> userIt = userListeners.get(
- event.getUsername()).iterator();
- while (userIt.hasNext())
- dispatchEvent(userIt.next(), event);
- }
- }
- } catch (Exception e) {
- stdOut("Cannot process logging event");
- e.printStackTrace();
- }
- }
-
- protected void dispatchEvent(ArgeoLogListener logListener, LogEvent evt) {
- LoggingEvent event = evt.getLoggingEvent();
- logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event
- .getLevel().toString(), event.getLoggerName(), event
- .getThreadName(), event.getMessage(), event
- .getThrowableStrRep());
- }
-
- private class AppenderImpl extends AppenderSkeleton {
- public boolean requiresLayout() {
- return false;
- }
-
- public void close() {
- }
-
- @Override
- protected void append(LoggingEvent event) {
- if (events != null) {
- try {
- String username = SecurityUtils.getCurrentThreadUsername();
- events.put(new LogEvent(username, event));
- } catch (InterruptedException e) {
- // silent
- }
- }
- }
-
- }
-
- private class LogDispatcherThread extends Thread {
- /** encapsulated in order to simplify concurrency management */
- private LinkedList<LogEvent> lastEvents = new LinkedList<LogEvent>();
-
- public LogDispatcherThread() {
- super("Argeo Logging Dispatcher Thread");
- }
-
- public void run() {
- while (events != null) {
- try {
- LogEvent loggingEvent = events.take();
- processLoggingEvent(loggingEvent);
- addLastEvent(loggingEvent);
- } catch (InterruptedException e) {
- if (events == null)
- return;
- }
- }
- }
-
- protected synchronized void addLastEvent(LogEvent loggingEvent) {
- if (lastEvents.size() >= maxLastEventsCount)
- lastEvents.poll();
- lastEvents.add(loggingEvent);
- }
-
- public synchronized List<LogEvent> getLastEvents(String username,
- Integer maxCount) {
- LinkedList<LogEvent> evts = new LinkedList<LogEvent>();
- ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents
- .size());
- int count = 0;
- while (it.hasPrevious() && (count < maxCount)) {
- LogEvent evt = it.previous();
- if (username == null || username.equals(evt.getUsername())) {
- evts.push(evt);
- count++;
- }
- }
- return evts;
- }
- }
-
- private class LogEvent {
- private final String username;
- private final LoggingEvent loggingEvent;
-
- public LogEvent(String username, LoggingEvent loggingEvent) {
- super();
- this.username = username;
- this.loggingEvent = loggingEvent;
- }
-
- @Override
- public int hashCode() {
- return loggingEvent.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- return loggingEvent.equals(obj);
- }
-
- @Override
- public String toString() {
- return username + "@ " + loggingEvent.toString();
- }
-
- public String getUsername() {
- return username;
- }
-
- public LoggingEvent getLoggingEvent() {
- return loggingEvent;
- }
-
- }
-}
<property name="repository" ref="nodeRepository" />
</bean>
- <bean id="keyring" class="org.argeo.security.jcr.JcrKeyring">
+ <bean id="keyring" class="org.argeo.jcr.security.JcrKeyring">
<property name="session" ref="nodeSession" />
<property name="defaultCallbackHandler" ref="defaultCallbackHandler" />
<property name="secreteKeyLength" value="${argeo.keyring.secreteKeyLength}" />
<reference id="defaultCallbackHandler" interface="javax.security.auth.callback.CallbackHandler" />\r
\r
<!-- SERVICES -->\r
- <service interface="org.argeo.security.crypto.CryptoKeyring"\r
+ <service interface="org.argeo.util.security.CryptoKeyring"\r
ref="keyring" />\r
</beans:beans>
\ No newline at end of file
import java.util.Map;
import java.util.Set;
-import org.argeo.security.ui.internal.CurrentUser;
+import org.argeo.cms.auth.CurrentUser;
import org.eclipse.ui.AbstractSourceProvider;
/**
+++ /dev/null
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.internal;
-
-import java.util.Set;
-
-import org.argeo.security.SecurityUtils;
-
-/**
- * Retrieves information about the current user. Not an API, can change without
- * notice.
- */
-public class CurrentUser {
- public final static String getUsername() {
- return SecurityUtils.getCurrentThreadUsername();
- }
-
- public final static Set<String> roles() {
- return SecurityUtils.roles();
- }
-}
import java.util.TreeSet;
+import org.argeo.cms.auth.CurrentUser;
import org.argeo.eclipse.ui.EclipseUiUtils;
import org.argeo.security.ui.SecurityUiPlugin;
-import org.argeo.security.ui.internal.CurrentUser;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
public void createPartControl(Composite parent) {
parent.setLayout(new GridLayout(2, false));
-// Authentication authentication = CurrentUser.getAuthentication();
-// EclipseUiUtils.createGridLL(parent, "Name", authentication
-// .getPrincipal().toString());
+ // Authentication authentication = CurrentUser.getAuthentication();
+ // EclipseUiUtils.createGridLL(parent, "Name", authentication
+ // .getPrincipal().toString());
EclipseUiUtils.createGridLL(parent, "User ID",
CurrentUser.getUsername());
/** Registers a service, typically called when OSGi services are bound. */
@SuppressWarnings("rawtypes")
public synchronized void register(Repository repository, Map properties) {
- // TODO: also check bean name?
String alias;
if (properties == null || !properties.containsKey(JCR_REPOSITORY_ALIAS)) {
log.warn("Cannot register a repository if no "
String alias = properties.get(JCR_REPOSITORY_ALIAS).toString();
Map<String, Repository> map = new TreeMap<String, Repository>(
repositories);
- map.put(alias, repository);
+ if (map.remove(alias) == null) {
+ log.warn("No repository was registered with alias " + alias);
+ return;
+ }
repositories = Collections.unmodifiableMap(map);
setChanged();
notifyObservers(alias);
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr.security;
+
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayReader;
+import java.io.InputStream;
+import java.io.Reader;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.util.security.AbstractKeyring;
+import org.argeo.util.security.PBEKeySpecCallback;
+
+/** JCR based implementation of a keyring */
+public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
+ /**
+ * Stronger with 256, but causes problem with Oracle JVM, force 128 in this
+ * case
+ */
+ public final static Long DEFAULT_SECRETE_KEY_LENGTH = 256l;
+ public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
+ public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
+ public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
+
+ private Integer iterationCountFactor = 200;
+ private Long secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
+ private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
+ private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
+ private String cipherName = DEFAULT_CIPHER_NAME;
+
+ private Session session;
+
+ /**
+ * When setup is called the session has not yet been saved and we don't want
+ * to save it since there maybe other data which would be inconsistent. So
+ * we keep a reference to this node which will then be used (an reset to
+ * null) when handling the PBE callback. We keep one per thread in case
+ * multiple users are accessing the same instance of a keyring.
+ */
+ private ThreadLocal<Node> notYetSavedKeyring = new ThreadLocal<Node>() {
+
+ @Override
+ protected Node initialValue() {
+ return null;
+ }
+ };
+
+ @Override
+ protected Boolean isSetup() {
+ try {
+ if (notYetSavedKeyring.get() != null)
+ return true;
+
+ Node userHome = UserJcrUtils.getUserHome(session);
+ return userHome.hasNode(ARGEO_KEYRING);
+ } catch (RepositoryException e) {
+ throw new ArgeoException("Cannot check whether keyring is setup", e);
+ }
+ }
+
+ @Override
+ protected void setup(char[] password) {
+ Binary binary = null;
+ InputStream in = null;
+ try {
+ Node userHome = UserJcrUtils.getUserHome(session);
+ if (userHome.hasNode(ARGEO_KEYRING))
+ throw new ArgeoException("Keyring already setup");
+ Node keyring = userHome.addNode(ARGEO_KEYRING);
+ keyring.addMixin(ArgeoTypes.ARGEO_PBE_SPEC);
+
+ // deterministic salt and iteration count based on username
+ String username = session.getUserID();
+ byte[] salt = new byte[8];
+ byte[] usernameBytes = username.getBytes();
+ for (int i = 0; i < salt.length; i++) {
+ if (i < usernameBytes.length)
+ salt[i] = usernameBytes[i];
+ else
+ salt[i] = 0;
+ }
+ in = new ByteArrayInputStream(salt);
+ binary = session.getValueFactory().createBinary(in);
+ keyring.setProperty(ARGEO_SALT, binary);
+
+ Integer iterationCount = username.length() * iterationCountFactor;
+ keyring.setProperty(ARGEO_ITERATION_COUNT, iterationCount);
+
+ // default algo
+ // TODO check if algo and key length are available, use DES if not
+ keyring.setProperty(ARGEO_SECRET_KEY_FACTORY, secreteKeyFactoryName);
+ keyring.setProperty(ARGEO_KEY_LENGTH, secreteKeyLength);
+ keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION,
+ secreteKeyEncryption);
+ keyring.setProperty(ARGEO_CIPHER, cipherName);
+
+ // encrypted password hash
+ // IOUtils.closeQuietly(in);
+ // JcrUtils.closeQuietly(binary);
+ // byte[] btPass = hash(password, salt, iterationCount);
+ // in = new ByteArrayInputStream(btPass);
+ // binary = session.getValueFactory().createBinary(in);
+ // keyring.setProperty(ARGEO_PASSWORD, binary);
+
+ notYetSavedKeyring.set(keyring);
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot setup keyring", e);
+ } finally {
+ JcrUtils.closeQuietly(binary);
+ IOUtils.closeQuietly(in);
+ // JcrUtils.discardQuietly(session);
+ }
+ }
+
+ @Override
+ protected void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) {
+ try {
+ Node userHome = UserJcrUtils.getUserHome(session);
+ Node keyring;
+ if (userHome.hasNode(ARGEO_KEYRING))
+ keyring = userHome.getNode(ARGEO_KEYRING);
+ else if (notYetSavedKeyring.get() != null)
+ keyring = notYetSavedKeyring.get();
+ else
+ throw new ArgeoException("Keyring not setup");
+
+ pbeCallback.set(keyring.getProperty(ARGEO_SECRET_KEY_FACTORY)
+ .getString(), JcrUtils.getBinaryAsBytes(keyring
+ .getProperty(ARGEO_SALT)),
+ (int) keyring.getProperty(ARGEO_ITERATION_COUNT).getLong(),
+ (int) keyring.getProperty(ARGEO_KEY_LENGTH).getLong(),
+ keyring.getProperty(ARGEO_SECRET_KEY_ENCRYPTION)
+ .getString());
+
+ if (notYetSavedKeyring.get() != null)
+ notYetSavedKeyring.remove();
+ } catch (RepositoryException e) {
+ throw new ArgeoException("Cannot handle key spec callback", e);
+ }
+ }
+
+ /** The parent node must already exist at this path. */
+ @Override
+ protected synchronized void encrypt(String path, InputStream unencrypted) {
+ // should be called first for lazy initialization
+ SecretKey secretKey = getSecretKey();
+
+ Binary binary = null;
+ InputStream in = null;
+ try {
+ Cipher cipher = createCipher();
+ Node node;
+ if (!session.nodeExists(path)) {
+ String parentPath = JcrUtils.parentPath(path);
+ if (!session.nodeExists(parentPath))
+ throw new ArgeoException("No parent node of " + path);
+ Node parentNode = session.getNode(parentPath);
+ node = parentNode.addNode(JcrUtils.nodeNameFromPath(path));
+ } else {
+ node = session.getNode(path);
+ }
+ node.addMixin(ArgeoTypes.ARGEO_ENCRYPTED);
+ SecureRandom random = new SecureRandom();
+ byte[] iv = new byte[16];
+ random.nextBytes(iv);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
+ JcrUtils.setBinaryAsBytes(node, ARGEO_IV, iv);
+
+ in = new CipherInputStream(unencrypted, cipher);
+ binary = session.getValueFactory().createBinary(in);
+ node.setProperty(Property.JCR_DATA, binary);
+ session.save();
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot encrypt", e);
+ } finally {
+ IOUtils.closeQuietly(unencrypted);
+ IOUtils.closeQuietly(in);
+ JcrUtils.closeQuietly(binary);
+ }
+ }
+
+ @Override
+ protected synchronized InputStream decrypt(String path) {
+ Binary binary = null;
+ InputStream encrypted = null;
+ Reader reader = null;
+ try {
+ if (!session.nodeExists(path)) {
+ char[] password = ask();
+ reader = new CharArrayReader(password);
+ return new ByteArrayInputStream(IOUtils.toByteArray(reader));
+ } else {
+ // should be called first for lazy initialisation
+ SecretKey secretKey = getSecretKey();
+
+ Cipher cipher = createCipher();
+
+ Node node = session.getNode(path);
+ if (node.hasProperty(ARGEO_IV)) {
+ byte[] iv = JcrUtils.getBinaryAsBytes(node
+ .getProperty(ARGEO_IV));
+ cipher.init(Cipher.DECRYPT_MODE, secretKey,
+ new IvParameterSpec(iv));
+ } else {
+ cipher.init(Cipher.DECRYPT_MODE, secretKey);
+ }
+
+ binary = node.getProperty(Property.JCR_DATA).getBinary();
+ encrypted = binary.getStream();
+ return new CipherInputStream(encrypted, cipher);
+ }
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot decrypt", e);
+ } finally {
+ IOUtils.closeQuietly(encrypted);
+ IOUtils.closeQuietly(reader);
+ JcrUtils.closeQuietly(binary);
+ }
+ }
+
+ protected Cipher createCipher() {
+ try {
+ Node userHome = UserJcrUtils.getUserHome(session);
+ if (!userHome.hasNode(ARGEO_KEYRING))
+ throw new ArgeoException("Keyring not setup");
+ Node keyring = userHome.getNode(ARGEO_KEYRING);
+ Cipher cipher = Cipher.getInstance(keyring
+ .getProperty(ARGEO_CIPHER).getString(),
+ getSecurityProvider());
+ return cipher;
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot get cipher", e);
+ }
+ }
+
+ public synchronized void changePassword(char[] oldPassword,
+ char[] newPassword) {
+ // TODO decrypt with old pw / encrypt with new pw all argeo:encrypted
+ }
+
+ public synchronized void setSession(Session session) {
+ this.session = session;
+ }
+
+ public void setIterationCountFactor(Integer iterationCountFactor) {
+ this.iterationCountFactor = iterationCountFactor;
+ }
+
+ public void setSecreteKeyLength(Long keyLength) {
+ this.secreteKeyLength = keyLength;
+ }
+
+ public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
+ this.secreteKeyFactoryName = secreteKeyFactoryName;
+ }
+
+ public void setSecreteKeyEncryption(String secreteKeyEncryption) {
+ this.secreteKeyEncryption = secreteKeyEncryption;
+ }
+
+ public void setCipherName(String cipherName) {
+ this.cipherName = cipherName;
+ }
+
+}
\ No newline at end of file
source.. = src/,\
ext/test/
output.. = bin/
-additional.bundles = org.junit
+additional.bundles = org.junit,\
+ org.slf4j.commons.logging,\
+ org.apache.log4j
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.util.security;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.bind.DatatypeConverter;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.StreamUtils;
+import org.argeo.util.security.PasswordBasedEncryption;
+
+public class PasswordBasedEncryptionTest extends TestCase {
+ private final static Log log = LogFactory
+ .getLog(PasswordBasedEncryptionTest.class);
+
+ public void testEncryptDecrypt() {
+ final String password = "test long password since they are safer";
+ PasswordBasedEncryption pbeEnc = new PasswordBasedEncryption(
+ password.toCharArray());
+ String message = "Hello World!";
+ log.info("Password:\t'" + password + "'");
+ log.info("Message:\t'" + message + "'");
+ byte[] encrypted = pbeEnc.encryptString(message);
+ log.info("Encrypted:\t'"
+ + DatatypeConverter.printBase64Binary(encrypted) + "'");
+ PasswordBasedEncryption pbeDec = new PasswordBasedEncryption(
+ password.toCharArray());
+ InputStream in = null;
+ in = new ByteArrayInputStream(encrypted);
+ String decrypted = pbeDec.decryptAsString(in);
+ log.info("Decrypted:\t'" + decrypted + "'");
+ StreamUtils.closeQuietly(in);
+ assertEquals(message, decrypted);
+ }
+
+ public void testPBEWithMD5AndDES() throws Exception {
+ String password = "test";
+ String message = "Hello World!";
+
+ byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+ (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
+
+ int count = 1024;
+
+ String cipherAlgorithm = "PBEWithMD5AndDES";
+ String secretKeyAlgorithm = "PBEWithMD5AndDES";
+ SecretKeyFactory keyFac = SecretKeyFactory
+ .getInstance(secretKeyAlgorithm);
+ PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
+ PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
+ SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
+ Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
+ ecipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
+ Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
+ dcipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
+
+ byte[] encrypted = ecipher.doFinal(message.getBytes());
+ byte[] decrypted = dcipher.doFinal(encrypted);
+ assertEquals(message, new String(decrypted));
+
+ }
+
+ public void testPBEWithSHA1AndAES() throws Exception {
+ String password = "test";
+ String message = "Hello World!";
+
+ byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+ (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
+ byte[] iv = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+ (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99,
+ (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+ (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
+
+ int count = 1024;
+ // int keyLength = 256;
+ int keyLength = 128;
+
+ String cipherAlgorithm = "AES/CBC/PKCS5Padding";
+ String secretKeyAlgorithm = "PBKDF2WithHmacSHA1";
+ SecretKeyFactory keyFac = SecretKeyFactory
+ .getInstance(secretKeyAlgorithm);
+ PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt,
+ count, keyLength);
+ SecretKey tmp = keyFac.generateSecret(pbeKeySpec);
+ SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
+ Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
+ ecipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
+
+ // decrypt
+ keyFac = SecretKeyFactory.getInstance(secretKeyAlgorithm);
+ pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, count,
+ keyLength);
+ tmp = keyFac.generateSecret(pbeKeySpec);
+ secret = new SecretKeySpec(tmp.getEncoded(), "AES");
+ // AlgorithmParameters params = ecipher.getParameters();
+ // byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
+ Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
+ dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
+
+ byte[] encrypted = ecipher.doFinal(message.getBytes());
+ byte[] decrypted = dcipher.doFinal(encrypted);
+ assertEquals(message, new String(decrypted));
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ CipherOutputStream cipherOut = new CipherOutputStream(out, ecipher);
+ cipherOut.write(message.getBytes());
+ StreamUtils.closeQuietly(cipherOut);
+ byte[] enc = out.toByteArray();
+
+ ByteArrayInputStream in = new ByteArrayInputStream(enc);
+ CipherInputStream cipherIn = new CipherInputStream(in, dcipher);
+ ByteArrayOutputStream dec = new ByteArrayOutputStream();
+ StreamUtils.copy(cipherIn, dec);
+ assertEquals(message, new String(dec.toByteArray()));
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.util.security;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.security.AccessController;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import javax.crypto.SecretKey;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.TextOutputCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.ArgeoException;
+import org.argeo.StreamUtils;
+
+/** username / password based keyring. TODO internationalize */
+public abstract class AbstractKeyring implements Keyring, CryptoKeyring {
+ public final static String DEFAULT_KEYRING_LOGIN_CONTEXT = "KEYRING";
+
+ private String loginContextName = DEFAULT_KEYRING_LOGIN_CONTEXT;
+ private CallbackHandler defaultCallbackHandler;
+
+ private String charset = "UTF-8";
+
+ /**
+ * Default provider is bouncy castle, in order to have consistent behaviour
+ * across implementations
+ */
+ private String securityProviderName = "BC";
+
+ /**
+ * Whether the keyring has already been created in the past with a master
+ * password
+ */
+ protected abstract Boolean isSetup();
+
+ /**
+ * Setup the keyring persistently, {@link #isSetup()} must return true
+ * afterwards
+ */
+ protected abstract void setup(char[] password);
+
+ /** Populates the key spec callback */
+ protected abstract void handleKeySpecCallback(PBEKeySpecCallback pbeCallback);
+
+ protected abstract void encrypt(String path, InputStream unencrypted);
+
+ protected abstract InputStream decrypt(String path);
+
+ /** Triggers lazy initialization */
+ protected SecretKey getSecretKey() {
+ Subject subject = Subject.getSubject(AccessController.getContext());
+ // we assume only one secrete key is available
+ Iterator<SecretKey> iterator = subject.getPrivateCredentials(
+ SecretKey.class).iterator();
+ if (!iterator.hasNext()) {// not initialized
+ CallbackHandler callbackHandler = new KeyringCallbackHandler();
+ try {
+ LoginContext loginContext = new LoginContext(loginContextName,
+ subject, callbackHandler);
+ loginContext.login();
+ // FIXME will login even if password is wrong
+ iterator = subject.getPrivateCredentials(SecretKey.class)
+ .iterator();
+ return iterator.next();
+ } catch (LoginException e) {
+ throw new ArgeoException("Keyring login failed", e);
+ }
+
+ } else {
+ SecretKey secretKey = iterator.next();
+ if (iterator.hasNext())
+ throw new ArgeoException(
+ "More than one secret key in private credentials");
+ return secretKey;
+ }
+ }
+
+ public InputStream getAsStream(String path) {
+ return decrypt(path);
+ }
+
+ public void set(String path, InputStream in) {
+ encrypt(path, in);
+ }
+
+ public char[] getAsChars(String path) {
+ InputStream in = getAsStream(path);
+ CharArrayWriter writer = null;
+ Reader reader = null;
+ try {
+ writer = new CharArrayWriter();
+ reader = new InputStreamReader(in, charset);
+ StreamUtils.copy(reader, writer);
+ return writer.toCharArray();
+ } catch (IOException e) {
+ throw new ArgeoException("Cannot decrypt to char array", e);
+ } finally {
+ StreamUtils.closeQuietly(reader);
+ StreamUtils.closeQuietly(in);
+ StreamUtils.closeQuietly(writer);
+ }
+ }
+
+ public void set(String path, char[] arr) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = null;
+ Writer writer = null;
+ try {
+ writer = new OutputStreamWriter(out, charset);
+ writer.write(arr);
+ writer.flush();
+ in = new ByteArrayInputStream(out.toByteArray());
+ set(path, in);
+ } catch (IOException e) {
+ throw new ArgeoException("Cannot encrypt to char array", e);
+ } finally {
+ StreamUtils.closeQuietly(writer);
+ StreamUtils.closeQuietly(out);
+ StreamUtils.closeQuietly(in);
+ }
+ }
+
+ protected Provider getSecurityProvider() {
+ return Security.getProvider(securityProviderName);
+ }
+
+ public void setLoginContextName(String loginContextName) {
+ this.loginContextName = loginContextName;
+ }
+
+ public void setDefaultCallbackHandler(CallbackHandler defaultCallbackHandler) {
+ this.defaultCallbackHandler = defaultCallbackHandler;
+ }
+
+ public void setCharset(String charset) {
+ this.charset = charset;
+ }
+
+ public void setSecurityProviderName(String securityProviderName) {
+ this.securityProviderName = securityProviderName;
+ }
+
+ @Deprecated
+ protected static byte[] hash(char[] password, byte[] salt,
+ Integer iterationCount) {
+ ByteArrayOutputStream out = null;
+ OutputStreamWriter writer = null;
+ try {
+ out = new ByteArrayOutputStream();
+ writer = new OutputStreamWriter(out, "UTF-8");
+ writer.write(password);
+ MessageDigest pwDigest = MessageDigest.getInstance("SHA-256");
+ pwDigest.reset();
+ pwDigest.update(salt);
+ byte[] btPass = pwDigest.digest(out.toByteArray());
+ for (int i = 0; i < iterationCount; i++) {
+ pwDigest.reset();
+ btPass = pwDigest.digest(btPass);
+ }
+ return btPass;
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot hash", e);
+ } finally {
+ StreamUtils.closeQuietly(out);
+ StreamUtils.closeQuietly(writer);
+ }
+
+ }
+
+ /**
+ * Convenience method using the underlying callback to ask for a password
+ * (typically used when the password is not saved in the keyring)
+ */
+ protected char[] ask() {
+ PasswordCallback passwordCb = new PasswordCallback("Password", false);
+ Callback[] dialogCbs = new Callback[] { passwordCb };
+ try {
+ defaultCallbackHandler.handle(dialogCbs);
+ char[] password = passwordCb.getPassword();
+ return password;
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot ask for a password", e);
+ }
+
+ }
+
+ class KeyringCallbackHandler implements CallbackHandler {
+ public void handle(Callback[] callbacks) throws IOException,
+ UnsupportedCallbackException {
+ // checks
+ if (callbacks.length != 2)
+ throw new IllegalArgumentException(
+ "Keyring required 2 and only 2 callbacks: {PasswordCallback,PBEKeySpecCallback}");
+ if (!(callbacks[0] instanceof PasswordCallback))
+ throw new UnsupportedCallbackException(callbacks[0]);
+ if (!(callbacks[1] instanceof PBEKeySpecCallback))
+ throw new UnsupportedCallbackException(callbacks[0]);
+
+ PasswordCallback passwordCb = (PasswordCallback) callbacks[0];
+ PBEKeySpecCallback pbeCb = (PBEKeySpecCallback) callbacks[1];
+
+ if (isSetup()) {
+ Callback[] dialogCbs = new Callback[] { passwordCb };
+ defaultCallbackHandler.handle(dialogCbs);
+ } else {// setup keyring
+ TextOutputCallback textCb1 = new TextOutputCallback(
+ TextOutputCallback.INFORMATION,
+ "Enter a master password which will protect your private data");
+ TextOutputCallback textCb2 = new TextOutputCallback(
+ TextOutputCallback.INFORMATION,
+ "(for example your credentials to third-party services)");
+ TextOutputCallback textCb3 = new TextOutputCallback(
+ TextOutputCallback.INFORMATION,
+ "Don't forget this password since the data cannot be read without it");
+ PasswordCallback confirmPasswordCb = new PasswordCallback(
+ "Confirm password", false);
+ // first try
+ Callback[] dialogCbs = new Callback[] { textCb1, textCb2,
+ textCb3, passwordCb, confirmPasswordCb };
+ defaultCallbackHandler.handle(dialogCbs);
+
+ // if passwords different, retry (except if cancelled)
+ while (passwordCb.getPassword() != null
+ && !Arrays.equals(passwordCb.getPassword(),
+ confirmPasswordCb.getPassword())) {
+ TextOutputCallback textCb = new TextOutputCallback(
+ TextOutputCallback.ERROR,
+ "The passwords do not match");
+ dialogCbs = new Callback[] { textCb, passwordCb,
+ confirmPasswordCb };
+ defaultCallbackHandler.handle(dialogCbs);
+ }
+
+ if (passwordCb.getPassword() != null) {// not cancelled
+ setup(passwordCb.getPassword());
+ }
+ }
+
+ if (passwordCb.getPassword() != null)
+ handleKeySpecCallback(pbeCb);
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.util.security;
+
+
+/**
+ * Advanced keyring based on cryptography that can easily be centralized and
+ * coordinated with {@link KeyringLoginModule} (since they ar ein the same
+ * package)
+ */
+public interface CryptoKeyring extends Keyring {
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.util.security;
+
+import java.security.AccessController;
+import java.util.Map;
+import java.util.Set;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+/** Adds a secret key to the private credentials */
+public class KeyringLoginModule implements LoginModule {
+ private Subject subject;
+ private CallbackHandler callbackHandler;
+ private SecretKey secretKey;
+
+ public void initialize(Subject subject, CallbackHandler callbackHandler,
+ Map<String, ?> sharedState, Map<String, ?> options) {
+ this.subject = subject;
+ if (subject == null) {
+ subject = Subject.getSubject(AccessController.getContext());
+ }
+ this.callbackHandler = callbackHandler;
+ }
+
+ public boolean login() throws LoginException {
+ Set<SecretKey> pbes = subject.getPrivateCredentials(SecretKey.class);
+ if (pbes.size() > 0)
+ return true;
+ PasswordCallback pc = new PasswordCallback("Master password", false);
+ PBEKeySpecCallback pbeCb = new PBEKeySpecCallback();
+ Callback[] callbacks = { pc, pbeCb };
+ try {
+ callbackHandler.handle(callbacks);
+ char[] password = pc.getPassword();
+
+ SecretKeyFactory keyFac = SecretKeyFactory.getInstance(pbeCb
+ .getSecretKeyFactory());
+ PBEKeySpec keySpec;
+ if (pbeCb.getKeyLength() != null)
+ keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
+ pbeCb.getIterationCount(), pbeCb.getKeyLength());
+ else
+ keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
+ pbeCb.getIterationCount());
+
+ String secKeyEncryption = pbeCb.getSecretKeyEncryption();
+ if (secKeyEncryption != null) {
+ SecretKey tmp = keyFac.generateSecret(keySpec);
+ secretKey = new SecretKeySpec(tmp.getEncoded(),
+ secKeyEncryption);
+ } else {
+ secretKey = keyFac.generateSecret(keySpec);
+ }
+ } catch (Exception e) {
+ LoginException le = new LoginException("Cannot login keyring");
+ le.initCause(e);
+ throw le;
+ }
+ return true;
+ }
+
+ public boolean commit() throws LoginException {
+ if (secretKey != null)
+ subject.getPrivateCredentials().add(secretKey);
+ return true;
+ }
+
+ public boolean abort() throws LoginException {
+ return true;
+ }
+
+ public boolean logout() throws LoginException {
+ Set<PasswordBasedEncryption> pbes = subject
+ .getPrivateCredentials(PasswordBasedEncryption.class);
+ pbes.clear();
+ return true;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.util.security;
+
+import javax.crypto.spec.PBEKeySpec;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.PasswordCallback;
+
+/**
+ * All information required to set up a {@link PBEKeySpec} bar the password
+ * itself (use a {@link PasswordCallback})
+ */
+public class PBEKeySpecCallback implements Callback {
+ private String secretKeyFactory;
+ private byte[] salt;
+ private Integer iterationCount;
+ /** Can be null for some algorithms */
+ private Integer keyLength;
+ /** Can be null, will trigger secret key encryption if not */
+ private String secretKeyEncryption;
+
+ private String encryptedPasswordHashCipher;
+ private byte[] encryptedPasswordHash;
+
+ public void set(String secretKeyFactory, byte[] salt,
+ Integer iterationCount, Integer keyLength,
+ String secretKeyEncryption) {
+ this.secretKeyFactory = secretKeyFactory;
+ this.salt = salt;
+ this.iterationCount = iterationCount;
+ this.keyLength = keyLength;
+ this.secretKeyEncryption = secretKeyEncryption;
+// this.encryptedPasswordHashCipher = encryptedPasswordHashCipher;
+// this.encryptedPasswordHash = encryptedPasswordHash;
+ }
+
+ public String getSecretKeyFactory() {
+ return secretKeyFactory;
+ }
+
+ public byte[] getSalt() {
+ return salt;
+ }
+
+ public Integer getIterationCount() {
+ return iterationCount;
+ }
+
+ public Integer getKeyLength() {
+ return keyLength;
+ }
+
+ public String getSecretKeyEncryption() {
+ return secretKeyEncryption;
+ }
+
+ public String getEncryptedPasswordHashCipher() {
+ return encryptedPasswordHashCipher;
+ }
+
+ public byte[] getEncryptedPasswordHash() {
+ return encryptedPasswordHash;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.util.security;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.argeo.ArgeoException;
+import org.argeo.StreamUtils;
+
+/** Simple password based encryption / decryption */
+public class PasswordBasedEncryption {
+ public final static Integer DEFAULT_ITERATION_COUNT = 1024;
+ /** Stronger with 256, but causes problem with Oracle JVM */
+ public final static Integer DEFAULT_SECRETE_KEY_LENGTH = 256;
+ public final static Integer DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED = 128;
+ public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
+ public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
+ public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
+ public final static String DEFAULT_CHARSET = "UTF-8";
+
+ private Integer iterationCount = DEFAULT_ITERATION_COUNT;
+ private Integer secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
+ private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
+ private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
+ private String cipherName = DEFAULT_CIPHER_NAME;
+
+ private static byte[] DEFAULT_SALT_8 = { (byte) 0xA9, (byte) 0x9B,
+ (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
+ (byte) 0x03 };
+ private static byte[] DEFAULT_IV_16 = { (byte) 0xA9, (byte) 0x9B,
+ (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
+ (byte) 0x03, (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
+ (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03 };
+
+ private Key key;
+ private Cipher ecipher;
+ private Cipher dcipher;
+
+ private String securityProviderName = null;
+
+ /**
+ * This is up to the caller to clear the passed array. Neither copy of nor
+ * reference to the passed array is kept
+ */
+ public PasswordBasedEncryption(char[] password) {
+ this(password, DEFAULT_SALT_8, DEFAULT_IV_16);
+ }
+
+ /**
+ * This is up to the caller to clear the passed array. Neither copies of nor
+ * references to the passed arrays are kept
+ */
+ public PasswordBasedEncryption(char[] password, byte[] passwordSalt,
+ byte[] initializationVector) {
+ try {
+ initKeyAndCiphers(password, passwordSalt, initializationVector);
+ } catch (InvalidKeyException e) {
+ Integer previousSecreteKeyLength = secreteKeyLength;
+ secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED;
+ System.err.println("'" + e.getMessage() + "', will use "
+ + secreteKeyLength + " secrete key length instead of "
+ + previousSecreteKeyLength);
+ try {
+ initKeyAndCiphers(password, passwordSalt, initializationVector);
+ } catch (Exception e1) {
+ throw new ArgeoException(
+ "Cannot get secret key (with restricted length)", e1);
+ }
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot get secret key", e);
+ }
+ }
+
+ protected void initKeyAndCiphers(char[] password, byte[] passwordSalt,
+ byte[] initializationVector) throws GeneralSecurityException {
+ byte[] salt = new byte[8];
+ System.arraycopy(passwordSalt, 0, salt, 0, salt.length);
+ // for (int i = 0; i < password.length && i < salt.length; i++)
+ // salt[i] = (byte) password[i];
+ byte[] iv = new byte[16];
+ System.arraycopy(initializationVector, 0, iv, 0, iv.length);
+
+ SecretKeyFactory keyFac = SecretKeyFactory
+ .getInstance(getSecretKeyFactoryName());
+ PBEKeySpec keySpec = new PBEKeySpec(password, salt,
+ getIterationCount(), getKeyLength());
+ String secKeyEncryption = getSecretKeyEncryption();
+ if (secKeyEncryption != null) {
+ SecretKey tmp = keyFac.generateSecret(keySpec);
+ key = new SecretKeySpec(tmp.getEncoded(), getSecretKeyEncryption());
+ } else {
+ key = keyFac.generateSecret(keySpec);
+ }
+ if (securityProviderName != null)
+ ecipher = Cipher.getInstance(getCipherName(), securityProviderName);
+ else
+ ecipher = Cipher.getInstance(getCipherName());
+ ecipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
+ dcipher = Cipher.getInstance(getCipherName());
+ dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
+ }
+
+ public void encrypt(InputStream decryptedIn, OutputStream encryptedOut)
+ throws IOException {
+ try {
+ CipherOutputStream out = new CipherOutputStream(encryptedOut,
+ ecipher);
+ StreamUtils.copy(decryptedIn, out);
+ StreamUtils.closeQuietly(out);
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot encrypt", e);
+ } finally {
+ StreamUtils.closeQuietly(decryptedIn);
+ }
+ }
+
+ public void decrypt(InputStream encryptedIn, OutputStream decryptedOut)
+ throws IOException {
+ try {
+ CipherInputStream decryptedIn = new CipherInputStream(encryptedIn,
+ dcipher);
+ StreamUtils.copy(decryptedIn, decryptedOut);
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot decrypt", e);
+ } finally {
+ StreamUtils.closeQuietly(encryptedIn);
+ }
+ }
+
+ public byte[] encryptString(String str) {
+ ByteArrayOutputStream out = null;
+ ByteArrayInputStream in = null;
+ try {
+ out = new ByteArrayOutputStream();
+ in = new ByteArrayInputStream(str.getBytes(DEFAULT_CHARSET));
+ encrypt(in, out);
+ return out.toByteArray();
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot encrypt", e);
+ } finally {
+ StreamUtils.closeQuietly(out);
+ }
+ }
+
+ /** Closes the input stream */
+ public String decryptAsString(InputStream in) {
+ ByteArrayOutputStream out = null;
+ try {
+ out = new ByteArrayOutputStream();
+ decrypt(in, out);
+ return new String(out.toByteArray(), DEFAULT_CHARSET);
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot decrypt", e);
+ } finally {
+ StreamUtils.closeQuietly(out);
+ }
+ }
+
+ protected Key getKey() {
+ return key;
+ }
+
+ protected Cipher getEcipher() {
+ return ecipher;
+ }
+
+ protected Cipher getDcipher() {
+ return dcipher;
+ }
+
+ protected Integer getIterationCount() {
+ return iterationCount;
+ }
+
+ protected Integer getKeyLength() {
+ return secreteKeyLength;
+ }
+
+ protected String getSecretKeyFactoryName() {
+ return secreteKeyFactoryName;
+ }
+
+ protected String getSecretKeyEncryption() {
+ return secreteKeyEncryption;
+ }
+
+ protected String getCipherName() {
+ return cipherName;
+ }
+
+ public void setIterationCount(Integer iterationCount) {
+ this.iterationCount = iterationCount;
+ }
+
+ public void setSecreteKeyLength(Integer keyLength) {
+ this.secreteKeyLength = keyLength;
+ }
+
+ public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
+ this.secreteKeyFactoryName = secreteKeyFactoryName;
+ }
+
+ public void setSecreteKeyEncryption(String secreteKeyEncryption) {
+ this.secreteKeyEncryption = secreteKeyEncryption;
+ }
+
+ public void setCipherName(String cipherName) {
+ this.cipherName = cipherName;
+ }
+
+ public void setSecurityProviderName(String securityProviderName) {
+ this.securityProviderName = securityProviderName;
+ }
+}