scope="prototype">
<property name="userAdmin" ref="userAdmin" />
<property name="userTransaction" ref="userTransaction" />
+ <property name="keyring" ref="keyring" />
</bean>
<!-- RAP Specific command and corresponding service to enable open file -->
\r
<reference id="userAdmin" interface="org.osgi.service.useradmin.UserAdmin" />\r
<reference id="userTransaction" interface="javax.transaction.UserTransaction" />\r
+ <reference id="keyring" interface="org.argeo.node.security.CryptoKeyring" />\r
</beans:beans>\r
import org.apache.commons.logging.LogFactory;
import org.argeo.cms.CmsException;
import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.argeo.node.security.CryptoKeyring;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
private final static Log log = LogFactory.getLog(OpenChangePasswordDialog.class);
private UserAdmin userAdmin;
private UserTransaction userTransaction;
+ private CryptoKeyring keyring = null;
public Object execute(ExecutionEvent event) throws ExecutionException {
ChangePasswordDialog dialog = new ChangePasswordDialog(HandlerUtil.getActiveShell(event), userAdmin);
try {
userTransaction.begin();
user.getCredentials().put(null, newPassword);
+ if (keyring != null) {
+ keyring.changePassword(oldPassword, newPassword);
+ // TODO change secret keys in the CMS session
+ }
userTransaction.commit();
} catch (Exception e) {
try {
public void setUserTransaction(UserTransaction userTransaction) {
this.userTransaction = userTransaction;
}
+
+ public void setKeyring(CryptoKeyring keyring) {
+ this.keyring = keyring;
+ }
+
}
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.security.PasswordBasedEncryption;
+import org.argeo.util.PasswordEncryption;
public class PasswordBasedEncryptionTest extends TestCase {
private final static Log log = LogFactory
public void testEncryptDecrypt() {
final String password = "test long password since they are safer";
- PasswordBasedEncryption pbeEnc = new PasswordBasedEncryption(
+ PasswordEncryption pbeEnc = new PasswordEncryption(
password.toCharArray());
String message = "Hello World!";
log.info("Password:\t'" + password + "'");
byte[] encrypted = pbeEnc.encryptString(message);
log.info("Encrypted:\t'"
+ DatatypeConverter.printBase64Binary(encrypted) + "'");
- PasswordBasedEncryption pbeDec = new PasswordBasedEncryption(
+ PasswordEncryption pbeDec = new PasswordEncryption(
password.toCharArray());
InputStream in = null;
in = new ByteArrayInputStream(encrypted);
if (request != null)
locale = request.getLocale();
CmsAuthUtils.addAuthorization(subject, authorization, locale, request);
+ CmsAuthUtils.registerSessionAuthorization(request, subject, authorization, locale);
if (log.isTraceEnabled())
log.trace("Anonymous logged in to CMS: " + subject);
return true;
throw new CmsException("Cannot commit", e);
}
- registerSessionAuthorization(request, subject, authorization, locale);
+ // registerSessionAuthorization(request, subject, authorization, locale);
}
private static void checkSubjectEmpty(Subject subject) {
// subject.getPrincipals().removeAll(subject.getPrincipals(AnonymousPrincipal.class));
}
- private synchronized static void registerSessionAuthorization(HttpServletRequest request, Subject subject,
+ synchronized static void registerSessionAuthorization(HttpServletRequest request, Subject subject,
Authorization authorization, Locale locale) {
// synchronized in order to avoid multiple registrations
// TODO move it to a service in order to avoid static synchronization
throw new CmsException("Inconsistent user " + authorization.getName()
+ " for existing CMS session " + cmsSession);
}
+ // keyring
+ subject.getPrivateCredentials().addAll(cmsSession.getSecretKeys());
} else {// anonymous
if (cmsSession.getAuthorization().getName() != null) {
cmsSession.close();
cmsSession = null;
}
}
- }
-
- if (cmsSession == null)
+ } else if (cmsSession == null) {
cmsSession = new WebCmsSessionImpl(subject, authorization, locale, request);
+ }
// request.setAttribute(CmsSession.class.getName(), cmsSession);
CmsSessionId nodeSessionId = new CmsSessionId(cmsSession.getUuid());
if (subject.getPrivateCredentials(CmsSessionId.class).size() == 0)
throw new CmsException(
"Subject already logged with session " + storedSessionId + " (not " + nodeSessionId + ")");
}
- } else {
+ } else
+
+ {
// TODO desktop, CLI
}
}
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Collection;
+import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
}
if (authorization != null) {
- CmsAuthUtils.addAuthorization(subject, authorization, request.getLocale(), request);
+ Locale locale = request.getLocale();
+ CmsAuthUtils.addAuthorization(subject, authorization,locale , request);
+ CmsAuthUtils.registerSessionAuthorization(request, subject, authorization, locale);
cleanUp();
return true;
} else {
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
-import org.argeo.cms.security.PasswordBasedEncryption;
import org.argeo.node.security.PBEKeySpecCallback;
+import org.argeo.util.PasswordEncryption;
/** Adds a secret key to the private credentials */
public class KeyringLoginModule implements LoginModule {
private CallbackHandler callbackHandler;
private SecretKey secretKey;
- public void initialize(Subject subject, CallbackHandler callbackHandler,
- Map<String, ?> sharedState, Map<String, ?> options) {
+ public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
+ Map<String, ?> options) {
this.subject = subject;
if (subject == null) {
subject = Subject.getSubject(AccessController.getContext());
}
public boolean login() throws LoginException {
- Set<SecretKey> pbes = subject.getPrivateCredentials(SecretKey.class);
- if (pbes.size() > 0)
- return true;
+// 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 };
callbackHandler.handle(callbacks);
char[] password = pc.getPassword();
- SecretKeyFactory keyFac = SecretKeyFactory.getInstance(pbeCb
- .getSecretKeyFactory());
+ SecretKeyFactory keyFac = SecretKeyFactory.getInstance(pbeCb.getSecretKeyFactory());
PBEKeySpec keySpec;
if (pbeCb.getKeyLength() != null)
- keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
- pbeCb.getIterationCount(), pbeCb.getKeyLength());
+ keySpec = new PBEKeySpec(password, pbeCb.getSalt(), pbeCb.getIterationCount(), pbeCb.getKeyLength());
else
- keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
- pbeCb.getIterationCount());
+ 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);
+ secretKey = new SecretKeySpec(tmp.getEncoded(), secKeyEncryption);
} else {
secretKey = keyFac.generateSecret(keySpec);
}
}
public boolean commit() throws LoginException {
- if (secretKey != null)
+ if (secretKey != null) {
+ subject.getPrivateCredentials().removeAll(subject.getPrivateCredentials(SecretKey.class));
subject.getPrivateCredentials().add(secretKey);
+ }
return true;
}
}
public boolean logout() throws LoginException {
- Set<PasswordBasedEncryption> pbes = subject
- .getPrivateCredentials(PasswordBasedEncryption.class);
+ Set<PasswordEncryption> pbes = subject.getPrivateCredentials(PasswordEncryption.class);
pbes.clear();
return true;
}
import org.argeo.cms.CmsException;
import org.argeo.cms.internal.kernel.Activator;
import org.argeo.naming.LdapAttrs;
+import org.argeo.node.security.CryptoKeyring;
import org.argeo.osgi.useradmin.AuthenticatingUser;
import org.argeo.osgi.useradmin.IpaUtils;
import org.argeo.osgi.useradmin.OsUserUtils;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
import org.osgi.service.useradmin.Authorization;
import org.osgi.service.useradmin.User;
import org.osgi.service.useradmin.UserAdmin;
password = passwordCallback.getPassword();
else
throw new CredentialNotFoundException("No credentials provided");
+ sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, username);
+ sharedState.put(CmsAuthUtils.SHARED_STATE_PWD, password);
}
User user = searchForUser(userAdmin, username);
if (user == null)
throw new LoginException(
"User admin found no authorization for authenticated user " + authenticatingUser.getName());
}
+
// Log and monitor new login
- CmsAuthUtils.addAuthorization(subject, authorization, locale,
- (HttpServletRequest) sharedState.get(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST));
+ HttpServletRequest request = (HttpServletRequest) sharedState.get(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST);
+ CmsAuthUtils.addAuthorization(subject, authorization, locale, request);
+
+ // Unlock keyring (underlying login to the JCR repository)
+ char[] password = (char[]) sharedState.get(CmsAuthUtils.SHARED_STATE_PWD);
+ if (password != null) {
+ ServiceReference<CryptoKeyring> keyringSr = bc.getServiceReference(CryptoKeyring.class);
+ if (keyringSr != null) {
+ CryptoKeyring keyring = bc.getService(keyringSr);
+ Subject.doAs(subject, new PrivilegedAction<Void>() {
+
+ @Override
+ public Void run() {
+ try {
+ keyring.unlock(password);
+ } catch (Exception e) {
+ e.printStackTrace();
+ log.warn("Could not unlock keyring with the password provided by " + authorization.getName()
+ + ": " + e.getMessage());
+ }
+ return null;
+ }
+
+ });
+ }
+ }
+
+ // Register CmsSession with initial subject
+ CmsAuthUtils.registerSessionAuthorization(request, subject, authorization, locale);
+
if (log.isDebugEnabled())
log.debug("Logged in to CMS: " + subject);
return true;
import java.util.Set;
import java.util.UUID;
+import javax.crypto.SecretKey;
import javax.jcr.Repository;
import javax.jcr.Session;
import javax.naming.InvalidNameException;
private Subject getSubject() {
return Subject.getSubject(initialContext);
}
+
+ public Set<SecretKey> getSecretKeys() {
+ return getSubject().getPrivateCredentials(SecretKey.class);
+ }
public synchronized Session getDataSession(String cn, String workspace, Repository repository) {
// FIXME make it more robust
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 org.apache.commons.io.IOUtils;
import org.argeo.cms.CmsException;
+import org.argeo.node.NodeConstants;
import org.argeo.node.security.CryptoKeyring;
import org.argeo.node.security.Keyring;
import org.argeo.node.security.PBEKeySpecCallback;
/** username / password based keyring. TODO internationalize */
public abstract class AbstractKeyring implements Keyring, CryptoKeyring {
- public final static String DEFAULT_KEYRING_LOGIN_CONTEXT = "KEYRING";
+ // public final static String DEFAULT_KEYRING_LOGIN_CONTEXT = "KEYRING";
- private String loginContextName = DEFAULT_KEYRING_LOGIN_CONTEXT;
+ // private String loginContextName = DEFAULT_KEYRING_LOGIN_CONTEXT;
private CallbackHandler defaultCallbackHandler;
private String charset = "UTF-8";
protected abstract InputStream decrypt(String path);
/** Triggers lazy initialization */
- protected SecretKey getSecretKey() {
+ protected SecretKey getSecretKey(char[] password) {
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();
+ if (!iterator.hasNext() || password!=null) {// not initialized
+ CallbackHandler callbackHandler = password == null ? new KeyringCallbackHandler()
+ : new PasswordProvidedCallBackHandler(password);
ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
try {
- LoginContext loginContext = new LoginContext(loginContextName, subject, callbackHandler);
+ LoginContext loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_KEYRING, subject,
+ callbackHandler);
loginContext.login();
// FIXME will login even if password is wrong
iterator = subject.getPrivateCredentials(SecretKey.class).iterator();
}
public char[] getAsChars(String path) {
- InputStream in = getAsStream(path);
- CharArrayWriter writer = null;
- Reader reader = null;
- try {
- writer = new CharArrayWriter();
- reader = new InputStreamReader(in, charset);
+ // InputStream in = getAsStream(path);
+ // CharArrayWriter writer = null;
+ // Reader reader = null;
+ try (InputStream in = getAsStream(path);
+ CharArrayWriter writer = new CharArrayWriter();
+ Reader reader = new InputStreamReader(in, charset);) {
IOUtils.copy(reader, writer);
return writer.toCharArray();
} catch (IOException e) {
throw new CmsException("Cannot decrypt to char array", e);
} finally {
- IOUtils.closeQuietly(reader);
- IOUtils.closeQuietly(in);
- IOUtils.closeQuietly(writer);
+ // IOUtils.closeQuietly(reader);
+ // IOUtils.closeQuietly(in);
+ // IOUtils.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);
+ // ByteArrayOutputStream out = new ByteArrayOutputStream();
+ // ByteArrayInputStream in = null;
+ // Writer writer = null;
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream();
+ Writer writer = new OutputStreamWriter(out, charset);) {
+ // writer = new OutputStreamWriter(out, charset);
writer.write(arr);
writer.flush();
- in = new ByteArrayInputStream(out.toByteArray());
- set(path, in);
+ // in = new ByteArrayInputStream(out.toByteArray());
+ try (ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());) {
+ set(path, in);
+ }
} catch (IOException e) {
throw new CmsException("Cannot encrypt to char array", e);
} finally {
- IOUtils.closeQuietly(writer);
- IOUtils.closeQuietly(out);
- IOUtils.closeQuietly(in);
+ // IOUtils.closeQuietly(writer);
+ // IOUtils.closeQuietly(out);
+ // IOUtils.closeQuietly(in);
}
}
- protected Provider getSecurityProvider() {
- return Security.getProvider(securityProviderName);
+ public void unlock(char[] password) {
+ if (!isSetup())
+ setup(password);
+ SecretKey secretKey = getSecretKey(password);
+ if (secretKey == null)
+ throw new CmsException("Could not unlock keyring");
}
- public void setLoginContextName(String loginContextName) {
- this.loginContextName = loginContextName;
+ protected Provider getSecurityProvider() {
+ return Security.getProvider(securityProviderName);
}
public void setDefaultCallbackHandler(CallbackHandler defaultCallbackHandler) {
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 CmsException("Cannot hash", e);
- } finally {
- IOUtils.closeQuietly(out);
- IOUtils.closeQuietly(writer);
- }
-
- }
+ // @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 CmsException("Cannot hash", e);
+ // } finally {
+ // IOUtils.closeQuietly(out);
+ // IOUtils.closeQuietly(writer);
+ // }
+ //
+ // }
/**
* Convenience method using the underlying callback to ask for a password
// checks
if (callbacks.length != 2)
throw new IllegalArgumentException(
- "Keyring required 2 and only 2 callbacks: {PasswordCallback,PBEKeySpecCallback}");
+ "Keyring requires 2 and only 2 callbacks: {PasswordCallback,PBEKeySpecCallback}");
if (!(callbacks[0] instanceof PasswordCallback))
throw new UnsupportedCallbackException(callbacks[0]);
if (!(callbacks[1] instanceof PBEKeySpecCallback))
}
}
+
+ class PasswordProvidedCallBackHandler implements CallbackHandler {
+ private final char[] password;
+
+ public PasswordProvidedCallBackHandler(char[] password) {
+ this.password = password;
+ }
+
+ @Override
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ // checks
+ if (callbacks.length != 2)
+ throw new IllegalArgumentException(
+ "Keyring requires 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];
+ passwordCb.setPassword(password);
+ PBEKeySpecCallback pbeCb = (PBEKeySpecCallback) callbacks[1];
+ handleKeySpecCallback(pbeCb);
+ }
+
+ }
}
import java.io.ByteArrayInputStream;
import java.io.CharArrayReader;
+import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
import java.security.Provider;
import java.security.SecureRandom;
import javax.crypto.spec.IvParameterSpec;
import javax.jcr.Binary;
import javax.jcr.Node;
+import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
+import javax.jcr.query.Query;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.argeo.cms.ArgeoNames;
import org.argeo.cms.ArgeoTypes;
import org.argeo.cms.CmsException;
/** JCR based implementation of a keyring */
public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
+ private final static Log log = LogFactory.getLog(JcrKeyring.class);
/**
- * Stronger with 256, but causes problem with Oracle JVM, force 128 in this
- * case
+ * 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_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 Long secretKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
+ private String secretKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
+ private String secretKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
private String cipherName = DEFAULT_CIPHER_NAME;
private final Repository repository;
// FIXME is it really still needed?
/**
- * 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.
+ * 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;
- }
- };
+// private ThreadLocal<Node> notYetSavedKeyring = new ThreadLocal<Node>() {
+//
+// @Override
+// protected Node initialValue() {
+// return null;
+// }
+// };
public JcrKeyring(Repository repository) {
this.repository = repository;
}
@Override
- protected Boolean isSetup() {
+ protected synchronized Boolean isSetup() {
try {
- if (notYetSavedKeyring.get() != null)
- return true;
+// if (notYetSavedKeyring.get() != null)
+// return true;
+ session().refresh(true);
Node userHome = NodeUtils.getUserHome(session());
return userHome.hasNode(ARGEO_KEYRING);
} catch (RepositoryException e) {
}
@Override
- protected void setup(char[] password) {
+ protected synchronized void setup(char[] password) {
Binary binary = null;
- InputStream in = null;
+ // InputStream in = null;
try {
+ session().refresh(true);
Node userHome = NodeUtils.getUserHome(session());
- if (userHome.hasNode(ARGEO_KEYRING))
- throw new ArgeoJcrException("Keyring already setup");
- Node keyring = userHome.addNode(ARGEO_KEYRING);
+ Node keyring;
+ if (userHome.hasNode(ARGEO_KEYRING)) {
+ throw new CmsException("Keyring already set up");
+ } else {
+ 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();
+ byte[] usernameBytes = username.getBytes(StandardCharsets.UTF_8);
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);
+ try (InputStream in = new ByteArrayInputStream(salt);) {
+ binary = session().getValueFactory().createBinary(in);
+ }
keyring.setProperty(ARGEO_SALT, binary);
Integer iterationCount = username.length() * iterationCountFactor;
// 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_SECRET_KEY_FACTORY, secretKeyFactoryName);
+ keyring.setProperty(ARGEO_KEY_LENGTH, secretKeyLength);
+ keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION, secretKeyEncryption);
keyring.setProperty(ARGEO_CIPHER, cipherName);
- // keyring.getSession().save();
+ keyring.getSession().save();
// encrypted password hash
// IOUtils.closeQuietly(in);
// binary = session().getValueFactory().createBinary(in);
// keyring.setProperty(ARGEO_PASSWORD, binary);
- notYetSavedKeyring.set(keyring);
+// notYetSavedKeyring.set(keyring);
} catch (Exception e) {
throw new ArgeoJcrException("Cannot setup keyring", e);
} finally {
JcrUtils.closeQuietly(binary);
- IOUtils.closeQuietly(in);
+ // IOUtils.closeQuietly(in);
// JcrUtils.discardQuietly(session());
}
}
@Override
- protected void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) {
+ protected synchronized void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) {
try {
+ session().refresh(true);
Node userHome = NodeUtils.getUserHome(session());
Node keyring;
if (userHome.hasNode(ARGEO_KEYRING))
keyring = userHome.getNode(ARGEO_KEYRING);
- else if (notYetSavedKeyring.get() != null)
- keyring = notYetSavedKeyring.get();
+// else if (notYetSavedKeyring.get() != null)
+// keyring = notYetSavedKeyring.get();
else
throw new ArgeoJcrException("Keyring not setup");
(int) keyring.getProperty(ARGEO_KEY_LENGTH).getLong(),
keyring.getProperty(ARGEO_SECRET_KEY_ENCRYPTION).getString());
- if (notYetSavedKeyring.get() != null)
- notYetSavedKeyring.remove();
+// if (notYetSavedKeyring.get() != null)
+// notYetSavedKeyring.remove();
} catch (RepositoryException e) {
throw new ArgeoJcrException("Cannot handle key spec callback", e);
}
@Override
protected synchronized void encrypt(String path, InputStream unencrypted) {
// should be called first for lazy initialization
- SecretKey secretKey = getSecretKey();
+ SecretKey secretKey = getSecretKey(null);
+ Cipher cipher = createCipher();
- Binary binary = null;
- InputStream in = null;
+ // Binary binary = null;
+ // InputStream in = null;
try {
- Cipher cipher = createCipher();
+ session().refresh(true);
Node node;
if (!session().nodeExists(path)) {
String parentPath = JcrUtils.parentPath(path);
} else {
node = session().getNode(path);
}
+ encrypt(secretKey, cipher, node, unencrypted);
+ // 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);
+ //
+ // try (InputStream in = new CipherInputStream(unencrypted, cipher);) {
+ // binary = session().getValueFactory().createBinary(in);
+ // node.setProperty(Property.JCR_DATA, binary);
+ // session().save();
+ // }
+ } catch (RepositoryException e) {
+ throw new ArgeoJcrException("Cannot encrypt", e);
+ } finally {
+ try {
+ unencrypted.close();
+ } catch (IOException e) {
+ // silent
+ }
+ // IOUtils.closeQuietly(unencrypted);
+ // IOUtils.closeQuietly(in);
+ // JcrUtils.closeQuietly(binary);
+ JcrUtils.logoutQuietly(session());
+ }
+ }
+
+ protected synchronized void encrypt(SecretKey secretKey, Cipher cipher, Node node, InputStream unencrypted) {
+ try {
node.addMixin(ArgeoTypes.ARGEO_ENCRYPTED);
SecureRandom random = new SecureRandom();
byte[] iv = new byte[16];
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();
+ Binary binary = null;
+ try (InputStream in = new CipherInputStream(unencrypted, cipher);) {
+ binary = session().getValueFactory().createBinary(in);
+ node.setProperty(Property.JCR_DATA, binary);
+ session().save();
+ } finally {
+ JcrUtils.closeQuietly(binary);
+ }
} catch (Exception e) {
throw new ArgeoJcrException("Cannot encrypt", e);
} finally {
- IOUtils.closeQuietly(unencrypted);
- IOUtils.closeQuietly(in);
- JcrUtils.closeQuietly(binary);
- JcrUtils.logoutQuietly(session());
+ try {
+ unencrypted.close();
+ } catch (IOException e) {
+ // silent
+ }
+ // IOUtils.closeQuietly(unencrypted);
+ // IOUtils.closeQuietly(in);
+ // JcrUtils.closeQuietly(binary);
+ // JcrUtils.logoutQuietly(session());
}
}
@Override
protected synchronized InputStream decrypt(String path) {
Binary binary = null;
- InputStream encrypted = null;
- Reader reader = null;
+ // InputStream encrypted = null;
try {
+ session().refresh(true);
if (!session().nodeExists(path)) {
char[] password = ask();
- reader = new CharArrayReader(password);
- return new ByteArrayInputStream(IOUtils.toByteArray(reader));
+ Reader reader = new CharArrayReader(password);
+ return new ByteArrayInputStream(IOUtils.toByteArray(reader, StandardCharsets.UTF_8));
} else {
// should be called first for lazy initialisation
- SecretKey secretKey = getSecretKey();
-
+ SecretKey secretKey = getSecretKey(null);
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);
+ return decrypt(secretKey, cipher, node);
}
} catch (Exception e) {
throw new ArgeoJcrException("Cannot decrypt", e);
} finally {
- IOUtils.closeQuietly(encrypted);
- IOUtils.closeQuietly(reader);
+ // IOUtils.closeQuietly(encrypted);
+ // IOUtils.closeQuietly(reader);
JcrUtils.closeQuietly(binary);
JcrUtils.logoutQuietly(session());
}
}
+ protected synchronized InputStream decrypt(SecretKey secretKey, Cipher cipher, Node node)
+ throws RepositoryException, GeneralSecurityException {
+ 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 binary = node.getProperty(Property.JCR_DATA).getBinary();
+ InputStream encrypted = binary.getStream();
+ return new CipherInputStream(encrypted, cipher);
+ }
+
protected Cipher createCipher() {
try {
Node userHome = NodeUtils.getUserHome(session());
}
public synchronized void changePassword(char[] oldPassword, char[] newPassword) {
- // TODO decrypt with old pw / encrypt with new pw all argeo:encrypted
+ // TODO make it XA compatible
+ SecretKey oldSecretKey = getSecretKey(oldPassword);
+ SecretKey newSecretKey = getSecretKey(newPassword);
+ Session session = session();
+ try {
+ NodeIterator encryptedNodes = session.getWorkspace().getQueryManager()
+ .createQuery("select * from [argeo:encrypted]", Query.JCR_SQL2).execute().getNodes();
+ while (encryptedNodes.hasNext()) {
+ Node node = encryptedNodes.nextNode();
+ InputStream in = decrypt(oldSecretKey, createCipher(), node);
+ encrypt(newSecretKey, createCipher(), node, in);
+ if (log.isDebugEnabled())
+ log.debug("Converted keyring encrypted value of " + node.getPath());
+ }
+ } catch (RepositoryException | GeneralSecurityException e) {
+ throw new CmsException("Cannot change JCR keyring password", e);
+ } finally {
+ JcrUtils.logoutQuietly(session);
+ }
}
// public synchronized void setSession(Session session) {
this.iterationCountFactor = iterationCountFactor;
}
- public void setSecreteKeyLength(Long keyLength) {
- this.secreteKeyLength = keyLength;
+ public void setSecretKeyLength(Long keyLength) {
+ this.secretKeyLength = keyLength;
}
- public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
- this.secreteKeyFactoryName = secreteKeyFactoryName;
+ public void setSecretKeyFactoryName(String secreteKeyFactoryName) {
+ this.secretKeyFactoryName = secreteKeyFactoryName;
}
- public void setSecreteKeyEncryption(String secreteKeyEncryption) {
- this.secreteKeyEncryption = secreteKeyEncryption;
+ public void setSecretKeyEncryption(String secreteKeyEncryption) {
+ this.secretKeyEncryption = secreteKeyEncryption;
}
public void setCipherName(String cipherName) {
+++ /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.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.apache.commons.io.IOUtils;
-import org.argeo.cms.CmsException;
-
-/** 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 CmsException(
- "Cannot get secret key (with restricted length)", e1);
- }
- } catch (Exception e) {
- throw new CmsException("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);
- IOUtils.copy(decryptedIn, out);
- IOUtils.closeQuietly(out);
- } catch (IOException e) {
- throw e;
- } catch (Exception e) {
- throw new CmsException("Cannot encrypt", e);
- } finally {
- IOUtils.closeQuietly(decryptedIn);
- }
- }
-
- public void decrypt(InputStream encryptedIn, OutputStream decryptedOut)
- throws IOException {
- try {
- CipherInputStream decryptedIn = new CipherInputStream(encryptedIn,
- dcipher);
- IOUtils.copy(decryptedIn, decryptedOut);
- } catch (IOException e) {
- throw e;
- } catch (Exception e) {
- throw new CmsException("Cannot decrypt", e);
- } finally {
- IOUtils.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 CmsException("Cannot encrypt", e);
- } finally {
- IOUtils.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 CmsException("Cannot decrypt", e);
- } finally {
- IOUtils.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;
- }
-}
package org.argeo.node;
public interface NodeConstants {
- /*
- * PIDs
- */
- String NODE_STATE_PID = "org.argeo.node.state";
- String NODE_DEPLOYMENT_PID = "org.argeo.node.deployment";
- String NODE_INSTANCE_PID = "org.argeo.node.instance";
-
- String NODE_KEYRING_PID = "org.argeo.node.keyring";
- String NODE_FS_PROVIDER_PID = "org.argeo.node.fsProvider";
-
- /*
- * FACTORY PIDs
- */
- String NODE_REPOS_FACTORY_PID = "org.argeo.node.repos";
- String NODE_USER_ADMIN_PID = "org.argeo.node.userAdmin";
-
/*
* DN ATTRIBUTES (RFC 4514)
*/
String LOGIN_CONTEXT_ANONYMOUS = "ANONYMOUS";
String LOGIN_CONTEXT_DATA_ADMIN = "DATA_ADMIN";
String LOGIN_CONTEXT_SINGLE_USER = "SINGLE_USER";
+ String LOGIN_CONTEXT_KEYRING = "KEYRING";
/*
* PATHS
// HTTP
String HTTP_PORT = "org.osgi.service.http.port";
String HTTP_PORT_SECURE = "org.osgi.service.http.port.secure";
+
+ /*
+ * PIDs
+ */
+ String NODE_STATE_PID = "org.argeo.node.state";
+ String NODE_DEPLOYMENT_PID = "org.argeo.node.deployment";
+ String NODE_INSTANCE_PID = "org.argeo.node.instance";
+
+ String NODE_KEYRING_PID = "org.argeo.node.keyring";
+ String NODE_FS_PROVIDER_PID = "org.argeo.node.fsProvider";
+
+ /*
+ * FACTORY PIDs
+ */
+ String NODE_REPOS_FACTORY_PID = "org.argeo.node.repos";
+ String NODE_USER_ADMIN_PID = "org.argeo.node.userAdmin";
}
* Marker interface for an advanced keyring based on cryptography.
*/
public interface CryptoKeyring extends Keyring {
+ public void changePassword(char[] oldPassword, char[] newPassword);
+ public void unlock(char[] password);
}
* change.</b>
*/
public interface Keyring {
- public void changePassword(char[] oldPassword, char[] newPassword);
-
/**
* Returns the confidential information as chars. Must ask for it if it is
* not stored.
--- /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;
+
+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;
+
+public class PasswordEncryption {
+ 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 PasswordEncryption(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 PasswordEncryption(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 UtilsException("Cannot get secret key (with restricted length)", e1);
+ }
+ } catch (Exception e) {
+ throw new UtilsException("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 UtilsException("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 UtilsException("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 UtilsException("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 UtilsException("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;
+ }
+}