Massive package refactoring
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / security / AbstractKeyring.java
diff --git a/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java b/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java
deleted file mode 100644 (file)
index 3de2e14..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-package org.argeo.cms.security;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.CharArrayWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.security.Provider;
-import java.security.Security;
-import java.util.Arrays;
-import java.util.Iterator;
-
-import javax.crypto.SecretKey;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.TextOutputCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.util.CurrentSubject;
-import org.argeo.util.StreamUtils;
-
-/** username / password based keyring. TODO internationalize */
-public abstract class AbstractKeyring implements Keyring, CryptoKeyring {
-       // public final static String DEFAULT_KEYRING_LOGIN_CONTEXT = "KEYRING";
-
-       // private String loginContextName = DEFAULT_KEYRING_LOGIN_CONTEXT;
-       private CallbackHandler defaultCallbackHandler;
-
-       private String charset = "UTF-8";
-
-       /**
-        * Default provider is bouncy castle, in order to have consistent behaviour
-        * across implementations
-        */
-       private String securityProviderName = "BC";
-
-       /**
-        * Whether the keyring has already been created in the past with a master
-        * password
-        */
-       protected abstract Boolean isSetup();
-
-       /**
-        * Setup the keyring persistently, {@link #isSetup()} must return true
-        * afterwards
-        */
-       protected abstract void setup(char[] password);
-
-       /** Populates the key spec callback */
-       protected abstract void handleKeySpecCallback(PBEKeySpecCallback pbeCallback);
-
-       protected abstract void encrypt(String path, InputStream unencrypted);
-
-       protected abstract InputStream decrypt(String path);
-
-       /** Triggers lazy initialization */
-       protected SecretKey getSecretKey(char[] password) {
-               Subject subject = CurrentSubject.current();
-               if (subject == null)
-                       throw new IllegalStateException("Current subject cannot be null");
-               // we assume only one secrete key is available
-               Iterator<SecretKey> iterator = subject.getPrivateCredentials(SecretKey.class).iterator();
-               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(CmsAuth.LOGIN_CONTEXT_KEYRING, subject, callbackHandler);
-                               loginContext.login();
-                               // FIXME will login even if password is wrong
-                               iterator = subject.getPrivateCredentials(SecretKey.class).iterator();
-                               return iterator.next();
-                       } catch (LoginException e) {
-                               throw new IllegalStateException("Keyring login failed", e);
-                       } finally {
-                               Thread.currentThread().setContextClassLoader(currentContextClassLoader);
-                       }
-
-               } else {
-                       SecretKey secretKey = iterator.next();
-                       if (iterator.hasNext())
-                               throw new IllegalStateException("More than one secret key in private credentials");
-                       return secretKey;
-               }
-       }
-
-       public InputStream getAsStream(String path) {
-               return decrypt(path);
-       }
-
-       public void set(String path, InputStream in) {
-               encrypt(path, in);
-       }
-
-       public char[] getAsChars(String path) {
-               // InputStream in = getAsStream(path);
-               // CharArrayWriter writer = null;
-               // Reader reader = null;
-               try (InputStream in = getAsStream(path);
-                               CharArrayWriter writer = new CharArrayWriter();
-                               Reader reader = new InputStreamReader(in, charset);) {
-                       StreamUtils.copy(reader, writer);
-                       return writer.toCharArray();
-               } catch (IOException e) {
-                       throw new IllegalStateException("Cannot decrypt to char array", e);
-               } finally {
-                       // 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 (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());
-                       try (ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());) {
-                               set(path, in);
-                       }
-               } catch (IOException e) {
-                       throw new IllegalStateException("Cannot encrypt to char array", e);
-               } finally {
-                       // IOUtils.closeQuietly(writer);
-                       // IOUtils.closeQuietly(out);
-                       // IOUtils.closeQuietly(in);
-               }
-       }
-
-       public void unlock(char[] password) {
-               if (!isSetup())
-                       setup(password);
-               SecretKey secretKey = getSecretKey(password);
-               if (secretKey == null)
-                       throw new IllegalStateException("Could not unlock keyring");
-       }
-
-       protected Provider getSecurityProvider() {
-               return Security.getProvider(securityProviderName);
-       }
-
-       public void setDefaultCallbackHandler(CallbackHandler defaultCallbackHandler) {
-               this.defaultCallbackHandler = defaultCallbackHandler;
-       }
-
-       public void setCharset(String charset) {
-               this.charset = charset;
-       }
-
-       public void setSecurityProviderName(String securityProviderName) {
-               this.securityProviderName = securityProviderName;
-       }
-
-       // @Deprecated
-       // protected static byte[] hash(char[] password, byte[] salt, Integer
-       // iterationCount) {
-       // ByteArrayOutputStream out = null;
-       // OutputStreamWriter writer = null;
-       // try {
-       // out = new ByteArrayOutputStream();
-       // writer = new OutputStreamWriter(out, "UTF-8");
-       // writer.write(password);
-       // MessageDigest pwDigest = MessageDigest.getInstance("SHA-256");
-       // pwDigest.reset();
-       // pwDigest.update(salt);
-       // byte[] btPass = pwDigest.digest(out.toByteArray());
-       // for (int i = 0; i < iterationCount; i++) {
-       // pwDigest.reset();
-       // btPass = pwDigest.digest(btPass);
-       // }
-       // return btPass;
-       // } catch (Exception e) {
-       // throw new CmsException("Cannot hash", e);
-       // } finally {
-       // IOUtils.closeQuietly(out);
-       // IOUtils.closeQuietly(writer);
-       // }
-       //
-       // }
-
-       /**
-        * Convenience method using the underlying callback to ask for a password
-        * (typically used when the password is not saved in the keyring)
-        */
-       protected char[] ask() {
-               PasswordCallback passwordCb = new PasswordCallback("Password", false);
-               Callback[] dialogCbs = new Callback[] { passwordCb };
-               try {
-                       defaultCallbackHandler.handle(dialogCbs);
-                       char[] password = passwordCb.getPassword();
-                       return password;
-               } catch (Exception e) {
-                       throw new IllegalStateException("Cannot ask for a password", e);
-               }
-
-       }
-
-       class KeyringCallbackHandler implements CallbackHandler {
-               public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
-                       // checks
-                       if (callbacks.length != 2)
-                               throw new IllegalArgumentException(
-                                               "Keyring 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];
-                       PBEKeySpecCallback pbeCb = (PBEKeySpecCallback) callbacks[1];
-
-                       if (isSetup()) {
-                               Callback[] dialogCbs = new Callback[] { passwordCb };
-                               defaultCallbackHandler.handle(dialogCbs);
-                       } else {// setup keyring
-                               TextOutputCallback textCb1 = new TextOutputCallback(TextOutputCallback.INFORMATION,
-                                               "Enter a master password which will protect your private data");
-                               TextOutputCallback textCb2 = new TextOutputCallback(TextOutputCallback.INFORMATION,
-                                               "(for example your credentials to third-party services)");
-                               TextOutputCallback textCb3 = new TextOutputCallback(TextOutputCallback.INFORMATION,
-                                               "Don't forget this password since the data cannot be read without it");
-                               PasswordCallback confirmPasswordCb = new PasswordCallback("Confirm password", false);
-                               // first try
-                               Callback[] dialogCbs = new Callback[] { textCb1, textCb2, textCb3, passwordCb, confirmPasswordCb };
-                               defaultCallbackHandler.handle(dialogCbs);
-
-                               // if passwords different, retry (except if cancelled)
-                               while (passwordCb.getPassword() != null
-                                               && !Arrays.equals(passwordCb.getPassword(), confirmPasswordCb.getPassword())) {
-                                       TextOutputCallback textCb = new TextOutputCallback(TextOutputCallback.ERROR,
-                                                       "The passwords do not match");
-                                       dialogCbs = new Callback[] { textCb, passwordCb, confirmPasswordCb };
-                                       defaultCallbackHandler.handle(dialogCbs);
-                               }
-
-                               if (passwordCb.getPassword() != null) {// not cancelled
-                                       setup(passwordCb.getPassword());
-                               }
-                       }
-
-                       if (passwordCb.getPassword() != null)
-                               handleKeySpecCallback(pbeCb);
-               }
-
-       }
-
-       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);
-               }
-
-       }
-}