From 6d206b9052689ffa880cd4593bfefa704dc0dd46 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Mon, 5 Feb 2018 14:42:55 +0100 Subject: [PATCH] Implement keyring change password --- .../META-INF/spring/commands.xml | 1 + .../META-INF/spring/osgi.xml | 1 + .../commands/OpenChangePasswordDialog.java | 11 + .../security/PasswordBasedEncryptionTest.java | 6 +- .../argeo/cms/auth/AnonymousLoginModule.java | 1 + .../src/org/argeo/cms/auth/CmsAuthUtils.java | 15 +- .../cms/auth/HttpSessionLoginModule.java | 5 +- .../argeo/cms/auth/KeyringLoginModule.java | 31 ++- .../argeo/cms/auth/UserAdminLoginModule.java | 37 ++- .../cms/internal/auth/CmsSessionImpl.java | 5 + .../argeo/cms/security/AbstractKeyring.java | 148 ++++++----- .../org/argeo/cms/security/JcrKeyring.java | 229 ++++++++++++------ .../src/org/argeo/node/NodeConstants.java | 33 +-- .../argeo/node/security/CryptoKeyring.java | 2 + .../src/org/argeo/node/security/Keyring.java | 2 - .../org/argeo/util/PasswordEncryption.java | 79 +++--- 16 files changed, 379 insertions(+), 227 deletions(-) rename org.argeo.cms/src/org/argeo/cms/security/PasswordBasedEncryption.java => org.argeo.util/src/org/argeo/util/PasswordEncryption.java (78%) diff --git a/org.argeo.cms.ui.workbench.rap/META-INF/spring/commands.xml b/org.argeo.cms.ui.workbench.rap/META-INF/spring/commands.xml index d9c72b419..2bfa179a4 100644 --- a/org.argeo.cms.ui.workbench.rap/META-INF/spring/commands.xml +++ b/org.argeo.cms.ui.workbench.rap/META-INF/spring/commands.xml @@ -9,6 +9,7 @@ scope="prototype"> + diff --git a/org.argeo.cms.ui.workbench.rap/META-INF/spring/osgi.xml b/org.argeo.cms.ui.workbench.rap/META-INF/spring/osgi.xml index 6e0f3b332..231b7d883 100644 --- a/org.argeo.cms.ui.workbench.rap/META-INF/spring/osgi.xml +++ b/org.argeo.cms.ui.workbench.rap/META-INF/spring/osgi.xml @@ -10,4 +10,5 @@ + diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/commands/OpenChangePasswordDialog.java b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/commands/OpenChangePasswordDialog.java index 64f4ff9b8..30836b948 100644 --- a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/commands/OpenChangePasswordDialog.java +++ b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/commands/OpenChangePasswordDialog.java @@ -35,6 +35,7 @@ import org.apache.commons.logging.Log; 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; @@ -59,6 +60,7 @@ public class OpenChangePasswordDialog extends AbstractHandler { 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); @@ -87,6 +89,10 @@ public class OpenChangePasswordDialog extends AbstractHandler { 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 { @@ -162,4 +168,9 @@ public class OpenChangePasswordDialog extends AbstractHandler { public void setUserTransaction(UserTransaction userTransaction) { this.userTransaction = userTransaction; } + + public void setKeyring(CryptoKeyring keyring) { + this.keyring = keyring; + } + } diff --git a/org.argeo.cms/ext/test/org/argeo/cms/security/PasswordBasedEncryptionTest.java b/org.argeo.cms/ext/test/org/argeo/cms/security/PasswordBasedEncryptionTest.java index 49319f15a..5c43e34f3 100644 --- a/org.argeo.cms/ext/test/org/argeo/cms/security/PasswordBasedEncryptionTest.java +++ b/org.argeo.cms/ext/test/org/argeo/cms/security/PasswordBasedEncryptionTest.java @@ -35,7 +35,7 @@ import junit.framework.TestCase; 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 @@ -43,7 +43,7 @@ public class PasswordBasedEncryptionTest extends TestCase { 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 + "'"); @@ -51,7 +51,7 @@ public class PasswordBasedEncryptionTest extends TestCase { 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); diff --git a/org.argeo.cms/src/org/argeo/cms/auth/AnonymousLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/AnonymousLoginModule.java index eca28e8b9..19c0d60ed 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/AnonymousLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/AnonymousLoginModule.java @@ -55,6 +55,7 @@ public class AnonymousLoginModule implements LoginModule { 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; diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java b/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java index 661cc6905..aa313ee0a 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java @@ -93,7 +93,7 @@ class CmsAuthUtils { throw new CmsException("Cannot commit", e); } - registerSessionAuthorization(request, subject, authorization, locale); + // registerSessionAuthorization(request, subject, authorization, locale); } private static void checkSubjectEmpty(Subject subject) { @@ -121,7 +121,7 @@ class CmsAuthUtils { // 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 @@ -144,6 +144,8 @@ class CmsAuthUtils { 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(); @@ -151,10 +153,9 @@ class CmsAuthUtils { 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) @@ -165,7 +166,9 @@ class CmsAuthUtils { throw new CmsException( "Subject already logged with session " + storedSessionId + " (not " + nodeSessionId + ")"); } - } else { + } else + + { // TODO desktop, CLI } } diff --git a/org.argeo.cms/src/org/argeo/cms/auth/HttpSessionLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/HttpSessionLoginModule.java index ccd02b5b2..81ca5baf3 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/HttpSessionLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/HttpSessionLoginModule.java @@ -4,6 +4,7 @@ import java.io.IOException; 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; @@ -119,7 +120,9 @@ public class HttpSessionLoginModule implements LoginModule { } 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 { diff --git a/org.argeo.cms/src/org/argeo/cms/auth/KeyringLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/KeyringLoginModule.java index 2c4958254..09fece03a 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/KeyringLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/KeyringLoginModule.java @@ -30,8 +30,8 @@ import javax.security.auth.callback.PasswordCallback; 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 { @@ -39,8 +39,8 @@ public class KeyringLoginModule implements LoginModule { private CallbackHandler callbackHandler; private SecretKey secretKey; - public void initialize(Subject subject, CallbackHandler callbackHandler, - Map sharedState, Map options) { + public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, + Map options) { this.subject = subject; if (subject == null) { subject = Subject.getSubject(AccessController.getContext()); @@ -49,9 +49,9 @@ public class KeyringLoginModule implements LoginModule { } public boolean login() throws LoginException { - Set pbes = subject.getPrivateCredentials(SecretKey.class); - if (pbes.size() > 0) - return true; +// Set 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 }; @@ -59,21 +59,17 @@ public class KeyringLoginModule implements LoginModule { 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); } @@ -86,8 +82,10 @@ public class KeyringLoginModule implements LoginModule { } public boolean commit() throws LoginException { - if (secretKey != null) + if (secretKey != null) { + subject.getPrivateCredentials().removeAll(subject.getPrivateCredentials(SecretKey.class)); subject.getPrivateCredentials().add(secretKey); + } return true; } @@ -96,8 +94,7 @@ public class KeyringLoginModule implements LoginModule { } public boolean logout() throws LoginException { - Set pbes = subject - .getPrivateCredentials(PasswordBasedEncryption.class); + Set pbes = subject.getPrivateCredentials(PasswordEncryption.class); pbes.clear(); return true; } diff --git a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java index e39918e40..683d13b21 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java @@ -29,11 +29,13 @@ import org.apache.commons.logging.LogFactory; 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; @@ -122,6 +124,8 @@ public class UserAdminLoginModule implements LoginModule { 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) @@ -204,9 +208,38 @@ public class UserAdminLoginModule implements LoginModule { 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 keyringSr = bc.getServiceReference(CryptoKeyring.class); + if (keyringSr != null) { + CryptoKeyring keyring = bc.getService(keyringSr); + Subject.doAs(subject, new PrivilegedAction() { + + @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; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java index 9b667717b..863f7c202 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java @@ -15,6 +15,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; +import javax.crypto.SecretKey; import javax.jcr.Repository; import javax.jcr.Session; import javax.naming.InvalidNameException; @@ -122,6 +123,10 @@ public class CmsSessionImpl implements CmsSession { private Subject getSubject() { return Subject.getSubject(initialContext); } + + public Set getSecretKeys() { + return getSubject().getPrivateCredentials(SecretKey.class); + } public synchronized Session getDataSession(String cn, String workspace, Repository repository) { // FIXME make it more robust diff --git a/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java b/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java index 7f4e960cd..779406a1d 100644 --- a/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java +++ b/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java @@ -25,7 +25,6 @@ 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; @@ -43,15 +42,16 @@ import javax.security.auth.login.LoginException; 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"; @@ -82,16 +82,18 @@ public abstract class AbstractKeyring implements Keyring, CryptoKeyring { 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 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(); @@ -119,48 +121,55 @@ public abstract class AbstractKeyring implements Keyring, CryptoKeyring { } 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) { @@ -175,31 +184,32 @@ public abstract class AbstractKeyring implements Keyring, CryptoKeyring { 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 @@ -223,7 +233,7 @@ public abstract class AbstractKeyring implements Keyring, CryptoKeyring { // 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)) @@ -266,4 +276,30 @@ public abstract class AbstractKeyring implements Keyring, CryptoKeyring { } } + + 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); + } + + } } diff --git a/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java b/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java index 43eab4b3c..04e3eb9db 100644 --- a/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java +++ b/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java @@ -17,8 +17,11 @@ package org.argeo.cms.security; 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; @@ -28,12 +31,16 @@ import javax.crypto.SecretKey; 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; @@ -44,9 +51,9 @@ import org.argeo.node.security.PBEKeySpecCallback; /** 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"; @@ -54,9 +61,9 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { 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; @@ -71,19 +78,19 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { // 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 notYetSavedKeyring = new ThreadLocal() { - - @Override - protected Node initialValue() { - return null; - } - }; +// private ThreadLocal notYetSavedKeyring = new ThreadLocal() { +// +// @Override +// protected Node initialValue() { +// return null; +// } +// }; public JcrKeyring(Repository repository) { this.repository = repository; @@ -107,11 +114,12 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { } @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) { @@ -120,28 +128,33 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { } @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; @@ -149,12 +162,12 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { // 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); @@ -164,25 +177,26 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { // 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"); @@ -192,8 +206,8 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { (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); } @@ -203,12 +217,13 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { @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); @@ -219,6 +234,36 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { } 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]; @@ -226,58 +271,70 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { 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()); @@ -298,7 +355,25 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { } 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) { @@ -309,16 +384,16 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { 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) { diff --git a/org.argeo.node.api/src/org/argeo/node/NodeConstants.java b/org.argeo.node.api/src/org/argeo/node/NodeConstants.java index 2b4c284f6..22afe0065 100644 --- a/org.argeo.node.api/src/org/argeo/node/NodeConstants.java +++ b/org.argeo.node.api/src/org/argeo/node/NodeConstants.java @@ -1,22 +1,6 @@ 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) */ @@ -73,6 +57,7 @@ public interface NodeConstants { 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 @@ -108,4 +93,20 @@ public interface NodeConstants { // 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"; } diff --git a/org.argeo.node.api/src/org/argeo/node/security/CryptoKeyring.java b/org.argeo.node.api/src/org/argeo/node/security/CryptoKeyring.java index 026fcb06d..dd3402277 100644 --- a/org.argeo.node.api/src/org/argeo/node/security/CryptoKeyring.java +++ b/org.argeo.node.api/src/org/argeo/node/security/CryptoKeyring.java @@ -19,5 +19,7 @@ package org.argeo.node.security; * 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); } diff --git a/org.argeo.node.api/src/org/argeo/node/security/Keyring.java b/org.argeo.node.api/src/org/argeo/node/security/Keyring.java index 467d9a8aa..fe054c3cc 100644 --- a/org.argeo.node.api/src/org/argeo/node/security/Keyring.java +++ b/org.argeo.node.api/src/org/argeo/node/security/Keyring.java @@ -23,8 +23,6 @@ import java.io.InputStream; * change. */ 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. diff --git a/org.argeo.cms/src/org/argeo/cms/security/PasswordBasedEncryption.java b/org.argeo.util/src/org/argeo/util/PasswordEncryption.java similarity index 78% rename from org.argeo.cms/src/org/argeo/cms/security/PasswordBasedEncryption.java rename to org.argeo.util/src/org/argeo/util/PasswordEncryption.java index a74cb592e..7269689bc 100644 --- a/org.argeo.cms/src/org/argeo/cms/security/PasswordBasedEncryption.java +++ b/org.argeo.util/src/org/argeo/util/PasswordEncryption.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.argeo.cms.security; +package org.argeo.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -33,11 +33,7 @@ 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 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; @@ -53,13 +49,11 @@ public class PasswordBasedEncryption { 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 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; @@ -71,7 +65,7 @@ public class PasswordBasedEncryption { * 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) { + public PasswordEncryption(char[] password) { this(password, DEFAULT_SALT_8, DEFAULT_IV_16); } @@ -79,29 +73,26 @@ public class PasswordBasedEncryption { * 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) { + 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); + 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); + throw new UtilsException("Cannot get secret key (with restricted length)", e1); } } catch (Exception e) { - throw new CmsException("Cannot get secret key", e); + throw new UtilsException("Cannot get secret key", e); } } - protected void initKeyAndCiphers(char[] password, byte[] passwordSalt, - byte[] initializationVector) throws GeneralSecurityException { + 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++) @@ -109,10 +100,8 @@ public class PasswordBasedEncryption { 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()); + SecretKeyFactory keyFac = SecretKeyFactory.getInstance(getSecretKeyFactoryName()); + PBEKeySpec keySpec = new PBEKeySpec(password, salt, getIterationCount(), getKeyLength()); String secKeyEncryption = getSecretKeyEncryption(); if (secKeyEncryption != null) { SecretKey tmp = keyFac.generateSecret(keySpec); @@ -129,34 +118,30 @@ public class PasswordBasedEncryption { dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); } - public void encrypt(InputStream decryptedIn, OutputStream encryptedOut) - throws IOException { + public void encrypt(InputStream decryptedIn, OutputStream encryptedOut) throws IOException { try { - CipherOutputStream out = new CipherOutputStream(encryptedOut, - ecipher); - IOUtils.copy(decryptedIn, out); - IOUtils.closeQuietly(out); + CipherOutputStream out = new CipherOutputStream(encryptedOut, ecipher); + StreamUtils.copy(decryptedIn, out); + StreamUtils.closeQuietly(out); } catch (IOException e) { throw e; } catch (Exception e) { - throw new CmsException("Cannot encrypt", e); + throw new UtilsException("Cannot encrypt", e); } finally { - IOUtils.closeQuietly(decryptedIn); + StreamUtils.closeQuietly(decryptedIn); } } - public void decrypt(InputStream encryptedIn, OutputStream decryptedOut) - throws IOException { + public void decrypt(InputStream encryptedIn, OutputStream decryptedOut) throws IOException { try { - CipherInputStream decryptedIn = new CipherInputStream(encryptedIn, - dcipher); - IOUtils.copy(decryptedIn, decryptedOut); + CipherInputStream decryptedIn = new CipherInputStream(encryptedIn, dcipher); + StreamUtils.copy(decryptedIn, decryptedOut); } catch (IOException e) { throw e; } catch (Exception e) { - throw new CmsException("Cannot decrypt", e); + throw new UtilsException("Cannot decrypt", e); } finally { - IOUtils.closeQuietly(encryptedIn); + StreamUtils.closeQuietly(encryptedIn); } } @@ -169,9 +154,9 @@ public class PasswordBasedEncryption { encrypt(in, out); return out.toByteArray(); } catch (Exception e) { - throw new CmsException("Cannot encrypt", e); + throw new UtilsException("Cannot encrypt", e); } finally { - IOUtils.closeQuietly(out); + StreamUtils.closeQuietly(out); } } @@ -183,9 +168,9 @@ public class PasswordBasedEncryption { decrypt(in, out); return new String(out.toByteArray(), DEFAULT_CHARSET); } catch (Exception e) { - throw new CmsException("Cannot decrypt", e); + throw new UtilsException("Cannot decrypt", e); } finally { - IOUtils.closeQuietly(out); + StreamUtils.closeQuietly(out); } } -- 2.30.2