X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Fsecurity%2FAbstractKeyring.java;h=3de2e1451d89a5a7b5cafb16d2cbe54194d2e2aa;hb=3c1cdc594d954520b14646102b366290bdad58c7;hp=7f4e960cda0a9ce70e27dc411ccfddad4c848ab1;hpb=25f7fd3b4a96e527c7d723181aaa01c289a0880f;p=lgpl%2Fargeo-commons.git 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..3de2e1451 100644 --- a/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java +++ b/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java @@ -1,18 +1,3 @@ -/* - * 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; @@ -24,8 +9,6 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; -import java.security.AccessController; -import java.security.MessageDigest; import java.security.Provider; import java.security.Security; import java.util.Arrays; @@ -41,17 +24,15 @@ import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; -import org.apache.commons.io.IOUtils; -import org.argeo.cms.CmsException; -import org.argeo.node.security.CryptoKeyring; -import org.argeo.node.security.Keyring; -import org.argeo.node.security.PBEKeySpecCallback; +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"; + // 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,22 +63,25 @@ public abstract class AbstractKeyring implements Keyring, CryptoKeyring { protected abstract InputStream decrypt(String path); /** Triggers lazy initialization */ - protected SecretKey getSecretKey() { - Subject subject = Subject.getSubject(AccessController.getContext()); + 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 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(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 CmsException("Keyring login failed", e); + throw new IllegalStateException("Keyring login failed", e); } finally { Thread.currentThread().setContextClassLoader(currentContextClassLoader); } @@ -105,7 +89,7 @@ public abstract class AbstractKeyring implements Keyring, CryptoKeyring { } else { SecretKey secretKey = iterator.next(); if (iterator.hasNext()) - throw new CmsException("More than one secret key in private credentials"); + throw new IllegalStateException("More than one secret key in private credentials"); return secretKey; } } @@ -119,48 +103,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); - IOUtils.copy(reader, writer); + // 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 CmsException("Cannot decrypt to char array", e); + throw new IllegalStateException("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); + throw new IllegalStateException("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 IllegalStateException("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 +166,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 @@ -213,7 +205,7 @@ public abstract class AbstractKeyring implements Keyring, CryptoKeyring { char[] password = passwordCb.getPassword(); return password; } catch (Exception e) { - throw new CmsException("Cannot ask for a password", e); + throw new IllegalStateException("Cannot ask for a password", e); } } @@ -223,7 +215,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 +258,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); + } + + } }