newRoles = new HashSet<>(currentRoles);
newRoles.remove(roleName);
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
- existingUser.getFirstname(), existingUser.getLastname(), existingUser.getUserState(), newRoles,
- existingUser.getLocale(), existingUser.getProperties());
+ existingUser.getSalt(), existingUser.getFirstname(), existingUser.getLastname(),
+ existingUser.getUserState(), newRoles, existingUser.getLocale(), existingUser.getProperties());
// delegate user replacement to persistence handler
this.persistenceHandler.replaceUser(newUser);
@@ -725,8 +735,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
// create new user
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
- existingUser.getFirstname(), existingUser.getLastname(), existingUser.getUserState(),
- existingUser.getRoles(), locale, existingUser.getProperties());
+ existingUser.getSalt(), existingUser.getFirstname(), existingUser.getLastname(),
+ existingUser.getUserState(), existingUser.getRoles(), locale, existingUser.getProperties());
// if the user is not setting their own locale, then make sure this user may set this user's locale
if (!certificate.getUsername().equals(username)) {
@@ -747,7 +757,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
}
@Override
- public void setUserPassword(Certificate certificate, String username, byte[] password) {
+ public void setUserPassword(Certificate certificate, String username, char[] password) {
try {
// validate user actually has this type of privilege
@@ -760,18 +770,22 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
throw new PrivilegeException(MessageFormat.format("User {0} does not exist!", username)); //$NON-NLS-1$
}
- String passwordHash = null;
+ byte[] passwordHash = null;
+ byte[] salt = null;
if (password != null) {
// validate password meets basic requirements
validatePassword(password);
+ // get new salt for user
+ salt = this.encryptionHandler.nextSalt();
+
// hash password
- passwordHash = this.encryptionHandler.convertToHash(password);
+ passwordHash = this.encryptionHandler.hashPassword(password, salt);
}
// create new user
- User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), passwordHash,
+ User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), passwordHash, salt,
existingUser.getFirstname(), existingUser.getLastname(), existingUser.getUserState(),
existingUser.getRoles(), existingUser.getLocale(), existingUser.getProperties());
@@ -815,8 +829,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
// create new user
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
- existingUser.getFirstname(), existingUser.getLastname(), state, existingUser.getRoles(),
- existingUser.getLocale(), existingUser.getProperties());
+ existingUser.getSalt(), existingUser.getFirstname(), existingUser.getLastname(), state,
+ existingUser.getRoles(), existingUser.getLocale(), existingUser.getProperties());
// validate that this user may modify this user's state
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_SET_USER_STATE, new Tuple(existingUser, newUser)));
@@ -1059,7 +1073,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
// validate the response
UserChallenge userChallenge = this.userChallengeHandler.validateResponse(user, challenge);
- String authToken = this.encryptionHandler.convertToHash(this.encryptionHandler.nextToken());
+ String authToken = this.encryptionHandler.nextToken();
String sessionId = UUID.randomUUID().toString();
// create a new certificate, with details of the user
@@ -1077,7 +1091,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
}
@Override
- public Certificate authenticate(String username, byte[] password) {
+ public Certificate authenticate(String username, char[] password) {
try {
// username must be at least 2 characters in length
@@ -1097,7 +1111,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
}
// get 2 auth tokens
- String authToken = this.encryptionHandler.convertToHash(this.encryptionHandler.nextToken());
+ String authToken = this.encryptionHandler.nextToken();
// get next session id
String sessionId = UUID.randomUUID().toString();
@@ -1232,15 +1246,12 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
* @throws InvalidCredentialsException
* if the given credentials are invalid, the user does not exist, or has no password set
*/
- private User checkCredentialsAndUserState(String username, byte[] password)
+ private User checkCredentialsAndUserState(String username, char[] password)
throws InvalidCredentialsException, AccessDeniedException {
// and validate the password
validatePassword(password);
- // we only work with hashed passwords
- String passwordHash = this.encryptionHandler.convertToHash(password);
-
// get user object
User user = this.persistenceHandler.getUser(username);
// no user means no authentication
@@ -1256,14 +1267,6 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
throw new InvalidCredentialsException(msg);
}
- // validate password
- String pwHash = user.getPassword();
- if (pwHash == null)
- throw new AccessDeniedException(
- MessageFormat.format("User {0} has no password and may not login!", username)); //$NON-NLS-1$
- if (!pwHash.equals(passwordHash))
- throw new InvalidCredentialsException(MessageFormat.format("Password is incorrect for {0}", username)); //$NON-NLS-1$
-
// validate if user is allowed to login
// this also capture the trying to login of SYSTEM user
if (user.getUserState() != UserState.ENABLED) {
@@ -1272,6 +1275,25 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
throw new AccessDeniedException(msg);
}
+ byte[] pwHash = user.getPassword();
+ if (pwHash == null)
+ throw new AccessDeniedException(
+ MessageFormat.format("User {0} has no password and may not login!", username)); //$NON-NLS-1$
+ byte[] salt = user.getSalt();
+ if (salt == null)
+ throw new AccessDeniedException(MessageFormat.format("User {0} has no salt and may not login!", salt)); //$NON-NLS-1$
+
+ // we only work with hashed passwords
+ byte[] passwordHash = this.encryptionHandler.hashPassword(password, salt);
+
+ logger.info("New hash: " + StringHelper.getHexString(passwordHash));
+ logger.info("User hash: " + StringHelper.getHexString(pwHash));
+ logger.info("User salt: " + StringHelper.getHexString(salt));
+
+ // validate password
+ if (!Arrays.equals(passwordHash, pwHash))
+ throw new InvalidCredentialsException(MessageFormat.format("Password is incorrect for {0}", username)); //$NON-NLS-1$
+
return user;
}
@@ -1426,10 +1448,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
/**
* This simple implementation validates that the password is not null, and that the password string is not empty
*
- * @see li.strolch.privilege.handler.PrivilegeHandler#validatePassword(byte[])
+ * @see li.strolch.privilege.handler.PrivilegeHandler#validatePassword(char[])
*/
@Override
- public void validatePassword(byte[] password) throws PrivilegeException {
+ public void validatePassword(char[] password) throws PrivilegeException {
if (password == null || password.length == 0) {
throw new PrivilegeException("A password may not be empty!"); //$NON-NLS-1$
@@ -1708,13 +1730,13 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
}
/**
- * Passwords should not be kept as strings, as string are immutable, this method thus clears the byte array so that
+ * Passwords should not be kept as strings, as string are immutable, this method thus clears the char array so that
* the password is not in memory anymore
*
* @param password
- * the byte array containing the passwort which is to be set to zeroes
+ * the char array containing the passwort which is to be set to zeroes
*/
- private void clearPassword(byte[] password) {
+ private void clearPassword(char[] password) {
if (password != null) {
for (int i = 0; i < password.length; i++) {
password[i] = 0;
@@ -1797,7 +1819,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
}
// validate password
- String pwHash = user.getPassword();
+ byte[] pwHash = user.getPassword();
if (pwHash != null) {
String msg = MessageFormat.format("System users must not have a password: {0}", systemUsername); //$NON-NLS-1$
throw new AccessDeniedException(msg);
diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/EncryptionHandler.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/EncryptionHandler.java
index 238772be8..aa7feb63d 100644
--- a/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/EncryptionHandler.java
+++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/EncryptionHandler.java
@@ -26,29 +26,30 @@ import java.util.Map;
public interface EncryptionHandler {
/**
- * Calculates or generates a token which can be used to identify certificates and so forth
+ * Generates a token which can be used to identify certificates and so forth
*
- * @return the secure token
+ * @return a new token
*/
public String nextToken();
/**
- * Converts a given string, e.g. a password to a hash which is defined by the concrete implementation
+ * Generates a token which can be used to identify certificates and so forth
*
- * @param string
- * the string to convert
- * @return the hash of the string after converting
+ * @return a new token
*/
- public String convertToHash(String string);
+ public byte[] nextSalt();
/**
- * Converts a given byte array, e.g. a password to a hash which is defined by the concrete implementation
+ * Hashes the given password with the given salt with the configured algorithm
*
- * @param bytes
- * the bytes to convert
- * @return the hash of the string after converting
+ * @param password
+ * the password
+ * @param salt
+ * the salt
+ *
+ * @return the hashed password
*/
- public String convertToHash(byte[] bytes);
+ public byte[] hashPassword(final char[] password, final byte[] salt);
/**
* Initialize the concrete {@link EncryptionHandler}. The passed parameter map contains any configuration the
diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/PrivilegeHandler.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/PrivilegeHandler.java
index aff0d2ca6..a3acc15ff 100644
--- a/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/PrivilegeHandler.java
+++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/PrivilegeHandler.java
@@ -331,7 +331,7 @@ public interface PrivilegeHandler {
*
*
* If the password given is null, then the user is created, but can not not login! Otherwise the password must meet
- * the requirements of the implementation under {@link PrivilegeHandler#validatePassword(byte[])}
+ * the requirements of the implementation under {@link PrivilegeHandler#validatePassword(char[])}
*
*
* @param certificate
@@ -341,14 +341,14 @@ public interface PrivilegeHandler {
* @param password
* the password of the new user. If the password is null, then this is accepted but the user can not
* login, otherwise the password must be validated against
- * {@link PrivilegeHandler#validatePassword(byte[])}
+ * {@link PrivilegeHandler#validatePassword(char[])}
*
* @throws AccessDeniedException
* if the user for this certificate may not perform the action
* @throws PrivilegeException
* if there is anything wrong with this certificate or the user already exists
*/
- public UserRep addUser(Certificate certificate, UserRep userRep, byte[] password)
+ public UserRep addUser(Certificate certificate, UserRep userRep, char[] password)
throws AccessDeniedException, PrivilegeException;
/**
@@ -390,7 +390,7 @@ public interface PrivilegeHandler {
*
*
* If the password given is null, then the user is created, but can not not login! Otherwise the password must meet
- * the requirements of the implementation under {@link PrivilegeHandler#validatePassword(byte[])}
+ * the requirements of the implementation under {@link PrivilegeHandler#validatePassword(char[])}
*
*
* @param certificate
@@ -400,14 +400,14 @@ public interface PrivilegeHandler {
* @param password
* the password of the new user. If the password is null, then this is accepted but the user can not
* login, otherwise the password must be validated against
- * {@link PrivilegeHandler#validatePassword(byte[])}
+ * {@link PrivilegeHandler#validatePassword(char[])}
*
* @throws AccessDeniedException
* if the user for this certificate may not perform the action
* @throws PrivilegeException
* if there is anything wrong with this certificate or if the user does not exist
*/
- public UserRep replaceUser(Certificate certificate, UserRep userRep, byte[] password)
+ public UserRep replaceUser(Certificate certificate, UserRep userRep, char[] password)
throws AccessDeniedException, PrivilegeException;
/**
@@ -481,7 +481,7 @@ public interface PrivilegeHandler {
*
* Changes the password for the {@link User} with the given username. If the password is null, then the {@link User}
* can not login anymore. Otherwise the password must meet the requirements of the implementation under
- * {@link PrivilegeHandler#validatePassword(byte[])}
+ * {@link PrivilegeHandler#validatePassword(char[])}
*
*
*
@@ -495,14 +495,14 @@ public interface PrivilegeHandler {
* @param password
* the new password for this user. If the password is null, then the {@link User} can not login anymore.
* Otherwise the password must meet the requirements of the implementation under
- * {@link PrivilegeHandler#validatePassword(byte[])}
+ * {@link PrivilegeHandler#validatePassword(char[])}
*
* @throws AccessDeniedException
* if the user for this certificate may not perform the action
* @throws PrivilegeException
* if there is anything wrong with this certificate
*/
- public void setUserPassword(Certificate certificate, String username, byte[] password)
+ public void setUserPassword(Certificate certificate, String username, char[] password)
throws AccessDeniedException, PrivilegeException;
/**
@@ -575,14 +575,14 @@ public interface PrivilegeHandler {
* the username of the {@link User} which is registered in the {@link PersistenceHandler}
* @param password
* the password with which this user is to be authenticated. Null passwords are not accepted and they
- * must meet the requirements of the {@link #validatePassword(byte[])}-method
+ * must meet the requirements of the {@link #validatePassword(char[])}-method
*
* @return a {@link Certificate} with which this user may then perform actions
*
* @throws AccessDeniedException
* if the user credentials are not valid
*/
- public Certificate authenticate(String username, byte[] password) throws AccessDeniedException;
+ public Certificate authenticate(String username, char[] password) throws AccessDeniedException;
/**
* Invalidates the session for the given {@link Certificate}, effectively logging out the user who was authenticated
@@ -630,7 +630,7 @@ public interface PrivilegeHandler {
* @throws PrivilegeException
* if the password does not implement the requirement of the concrete implementation
*/
- public void validatePassword(byte[] password) throws PrivilegeException;
+ public void validatePassword(char[] password) throws PrivilegeException;
/**
*
diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/PasswordCreaterUI.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/PasswordCreaterUI.java
deleted file mode 100644
index 40e68f95d..000000000
--- a/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/PasswordCreaterUI.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2013 Robert von Burg
- *
- * 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 li.strolch.privilege.helper;
-
-import java.awt.Dimension;
-import java.awt.GridLayout;
-import java.awt.Toolkit;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyEvent;
-import java.awt.event.KeyListener;
-
-import javax.swing.JButton;
-import javax.swing.JComboBox;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JPasswordField;
-import javax.swing.JTextField;
-import javax.swing.SwingConstants;
-
-import li.strolch.utils.helper.StringHelper;
-
-/**
- * Simple Swing UI to create passwords
- *
- * @author Robert von Burg
- */
-@SuppressWarnings("nls")
-public class PasswordCreaterUI {
-
- /**
- * Launches the UI
- *
- * @param args
- * not used
- */
- public static void main(String[] args) {
-
- JFrame.setDefaultLookAndFeelDecorated(true);
-
- JFrame frame = new JFrame();
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- frame.setTitle("Password creator");
- frame.setLayout(new GridLayout(4, 2));
-
- JLabel digest = new JLabel("Digest:", SwingConstants.RIGHT);
- JLabel password = new JLabel("Password:", SwingConstants.RIGHT);
- JLabel hash = new JLabel("Hash:", SwingConstants.RIGHT);
-
- String[] digests = new String[] { "MD2", "MD5", "SHA-1", "SHA-256", "SHA-384", "SHA-512" };
- final JComboBox digestCombo = new JComboBox<>(digests);
- digestCombo.setSelectedIndex(3);
- final JPasswordField passwordField = new JPasswordField();
- final JTextField hashField = new JTextField(150);
-
- JButton digestBtn = new JButton("Digest");
-
- passwordField.addKeyListener(new KeyListener() {
-
- @Override
- public void keyTyped(KeyEvent e) {
- //
- }
-
- @Override
- public void keyReleased(KeyEvent e) {
- //
- }
-
- @Override
- public void keyPressed(KeyEvent e) {
- hashField.setText("");
- }
- });
- digestBtn.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
-
- try {
- String digest = (String) digestCombo.getSelectedItem();
- char[] passwordChar = passwordField.getPassword();
- String password = new String(passwordChar);
- String hash = StringHelper.hashAsHex(digest, password);
- hashField.setText(hash);
- } catch (Exception e1) {
- e1.printStackTrace();
- hashField.setText("Failed: " + e1.getLocalizedMessage());
- }
- }
- });
-
- frame.add(digest);
- frame.add(digestCombo);
- frame.add(password);
- frame.add(passwordField);
- frame.add(hash);
- frame.add(hashField);
- frame.add(new JLabel());
- frame.add(digestBtn);
-
- Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
- int width = 500;
- int height = 160;
- frame.setSize(width, height);
- frame.setLocation(screenSize.width / 2 - width, screenSize.height / 2 - height);
-
- frame.setVisible(true);
- }
-}
\ No newline at end of file
diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/PasswordCreator.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/PasswordCreator.java
index 2898e12e8..d8847454d 100644
--- a/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/PasswordCreator.java
+++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/PasswordCreator.java
@@ -17,8 +17,12 @@ package li.strolch.privilege.helper;
import java.io.BufferedReader;
import java.io.InputStreamReader;
-import java.security.MessageDigest;
+import java.util.HashMap;
+import java.util.Map;
+import javax.crypto.SecretKeyFactory;
+
+import li.strolch.privilege.handler.DefaultEncryptionHandler;
import li.strolch.utils.helper.StringHelper;
/**
@@ -26,10 +30,6 @@ import li.strolch.utils.helper.StringHelper;
* Simple main class which can be used to create a hash from a password which the user must type in at the command line
*
*
- *
- * TODO: Note: currently the password input is echoed which is a security risk
- *
- *
* @author Robert von Burg
*/
public class PasswordCreator {
@@ -47,26 +47,82 @@ public class PasswordCreator {
String hashAlgorithm = null;
while (hashAlgorithm == null) {
- System.out.print("Hash Algorithm [SHA-256]: ");
+ System.out.print("Hash Algorithm [PBKDF2WithHmacSHA512]: ");
String readLine = r.readLine().trim();
if (readLine.isEmpty()) {
- hashAlgorithm = "SHA-256";
+ hashAlgorithm = "PBKDF2WithHmacSHA512";
} else {
try {
- MessageDigest.getInstance(readLine);
+ SecretKeyFactory.getInstance(readLine);
hashAlgorithm = readLine;
} catch (Exception e) {
- System.out.println(e.getLocalizedMessage());
+ System.err.println(e.getLocalizedMessage());
hashAlgorithm = null;
}
}
}
- System.out.print("Password: ");
- String password = r.readLine().trim();
- System.out.print("Hash is: " + StringHelper.hashAsHex(hashAlgorithm, password));
- }
+ int iterations = -1;
+ while (iterations == -1) {
+ System.out.print("Hash iterations [200000]: ");
+ String readLine = r.readLine().trim();
+ if (readLine.isEmpty()) {
+ iterations = 200000;
+ } else {
+
+ try {
+ iterations = Integer.parseInt(readLine);
+ } catch (Exception e) {
+ System.err.println(e.getLocalizedMessage());
+ iterations = -1;
+ }
+ }
+ }
+
+ int keyLength = -1;
+ while (keyLength == -1) {
+ System.out.print("Hash keyLength [256]: ");
+ String readLine = r.readLine().trim();
+
+ if (readLine.isEmpty()) {
+ keyLength = 256;
+ } else {
+
+ try {
+ keyLength = Integer.parseInt(readLine);
+ if (keyLength <= 0)
+ throw new IllegalArgumentException("KeyLength must be > 0");
+ } catch (Exception e) {
+ System.err.println(e.getLocalizedMessage());
+ keyLength = -1;
+ }
+ }
+ }
+
+ System.out.print("Password: ");
+ char[] password = r.readLine().trim().toCharArray();
+ System.out.print("Salt: ");
+ String saltS = StringHelper.getHexString(r.readLine().trim().getBytes());
+ byte[] salt = StringHelper.fromHexString(saltS);
+
+ Map parameterMap = new HashMap<>();
+ parameterMap.put(XmlConstants.XML_PARAM_HASH_ALGORITHM, hashAlgorithm);
+ parameterMap.put(XmlConstants.XML_PARAM_HASH_ITERATIONS, "" + iterations);
+ parameterMap.put(XmlConstants.XML_PARAM_HASH_KEY_LENGTH, "" + keyLength);
+
+ DefaultEncryptionHandler encryptionHandler = new DefaultEncryptionHandler();
+ encryptionHandler.initialize(parameterMap);
+
+ byte[] passwordHash = encryptionHandler.hashPassword(password, salt);
+ String passwordHashS = StringHelper.getHexString(passwordHash);
+ System.out.println("Hash is: " + passwordHashS);
+ System.out.println("Salt is: " + saltS);
+ System.out.println();
+
+ System.out.println(XmlConstants.XML_ATTR_PASSWORD + "=\"" + passwordHashS + "\" " + XmlConstants.XML_ATTR_SALT
+ + "=\"" + saltS + "\"");
+ }
}
diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/XmlConstants.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/XmlConstants.java
index d5c8aa6dc..cad9ab76e 100644
--- a/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/XmlConstants.java
+++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/XmlConstants.java
@@ -233,11 +233,26 @@ public class XmlConstants {
*/
public static final String XML_ATTR_PASSWORD = "password";
+ /**
+ * XML_ATTR_SALT = "salt" :
+ */
+ public static final String XML_ATTR_SALT = "salt";
+
/**
* XML_PARAM_HASH_ALGORITHM = "hashAlgorithm" :
*/
public static final String XML_PARAM_HASH_ALGORITHM = "hashAlgorithm";
+ /**
+ * XML_PARAM_HASH_ALGORITHM = "hashAlgorithm" :
+ */
+ public static final String XML_PARAM_HASH_ITERATIONS = "hashIterations";
+
+ /**
+ * XML_PARAM_HASH_ALGORITHM = "hashAlgorithm" :
+ */
+ public static final String XML_PARAM_HASH_KEY_LENGTH = "hashKeyLength";
+
/**
* XML_PARAM_USERS_FILE = "usersXmlFile" :
*/
diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/model/internal/User.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/model/internal/User.java
index 70cd57b5b..aec9bae8e 100644
--- a/li.strolch.privilege/src/main/java/li/strolch/privilege/model/internal/User.java
+++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/model/internal/User.java
@@ -43,7 +43,8 @@ public final class User {
private final String userId;
private final String username;
- private final String password;
+ private final byte[] password;
+ private final byte[] salt;
private final String firstname;
private final String lastname;
@@ -78,7 +79,7 @@ public final class User {
* @param propertyMap
* a {@link Map} containing string value pairs of properties for this user
*/
- public User(String userId, String username, String password, String firstname, String lastname,
+ public User(String userId, String username, byte[] password, byte[] salt, String firstname, String lastname,
UserState userState, Set roles, Locale locale, Map propertyMap) {
if (StringHelper.isEmpty(userId)) {
@@ -107,7 +108,8 @@ public final class User {
this.userId = userId;
this.username = username;
- this.password = StringHelper.isEmpty(password) ? null : password;
+ this.password = password;
+ this.salt = salt;
this.userState = userState;
this.firstname = firstname;
@@ -148,10 +150,19 @@ public final class User {
*
* @return the hashed password for this {@link User}
*/
- public String getPassword() {
+ public byte[] getPassword() {
return this.password;
}
+ /**
+ * Return the salt for this {@link User}
+ *
+ * @return the salt for this {@link User}
+ */
+ public byte[] getSalt() {
+ return this.salt;
+ }
+
/**
* @return the first name
*/
diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeUsersDomWriter.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeUsersDomWriter.java
index dec448fce..7eacc5ba9 100644
--- a/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeUsersDomWriter.java
+++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeUsersDomWriter.java
@@ -59,8 +59,10 @@ public class PrivilegeUsersDomWriter {
userElement.setAttribute(XmlConstants.XML_ATTR_USER_ID, user.getUserId());
userElement.setAttribute(XmlConstants.XML_ATTR_USERNAME, user.getUsername());
- if (StringHelper.isNotEmpty(user.getPassword()))
- userElement.setAttribute(XmlConstants.XML_ATTR_PASSWORD, user.getPassword());
+ if (user.getPassword() != null)
+ userElement.setAttribute(XmlConstants.XML_ATTR_PASSWORD, StringHelper.getHexString(user.getPassword()));
+ if (user.getSalt() != null)
+ userElement.setAttribute(XmlConstants.XML_ATTR_SALT, StringHelper.getHexString(user.getSalt()));
// add first name element
if (StringHelper.isNotEmpty(user.getFirstname())) {
diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeUsersSaxReader.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeUsersSaxReader.java
index e79efe676..221f9e306 100644
--- a/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeUsersSaxReader.java
+++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeUsersSaxReader.java
@@ -35,6 +35,7 @@ import org.xml.sax.helpers.DefaultHandler;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.UserState;
import li.strolch.privilege.model.internal.User;
+import li.strolch.utils.helper.StringHelper;
/**
* @author Robert von Burg
@@ -114,7 +115,8 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
String userId;
String username;
- String password;
+ byte[] password;
+ byte[] salt;
String firstName;
String lastname;
UserState userState;
@@ -135,7 +137,12 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
if (qName.equals(XmlConstants.XML_USER)) {
this.userId = attributes.getValue(XmlConstants.XML_ATTR_USER_ID);
this.username = attributes.getValue(XmlConstants.XML_ATTR_USERNAME);
- this.password = attributes.getValue(XmlConstants.XML_ATTR_PASSWORD);
+ String passwordS = attributes.getValue(XmlConstants.XML_ATTR_PASSWORD);
+ if (!StringHelper.isEmpty(passwordS))
+ this.password = StringHelper.fromHexString(passwordS);
+ String saltS = attributes.getValue(XmlConstants.XML_ATTR_SALT);
+ if (!StringHelper.isEmpty(saltS))
+ this.salt = StringHelper.fromHexString(saltS);
}
}
@@ -165,8 +172,8 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
// NO-OP
} else if (qName.equals(XmlConstants.XML_USER)) {
- User user = new User(this.userId, this.username, this.password, this.firstName, this.lastname,
- this.userState, this.userRoles, this.locale, this.parameters);
+ User user = new User(this.userId, this.username, this.password, this.salt, this.firstName,
+ this.lastname, this.userState, this.userRoles, this.locale, this.parameters);
logger.info(MessageFormat.format("New User: {0}", user)); //$NON-NLS-1$
getUsers().add(user);
} else {
diff --git a/li.strolch.privilege/src/test/java/li/strolch/privilege/test/AbstractPrivilegeTest.java b/li.strolch.privilege/src/test/java/li/strolch/privilege/test/AbstractPrivilegeTest.java
index af4af356e..fb5b13f28 100644
--- a/li.strolch.privilege/src/test/java/li/strolch/privilege/test/AbstractPrivilegeTest.java
+++ b/li.strolch.privilege/src/test/java/li/strolch/privilege/test/AbstractPrivilegeTest.java
@@ -22,7 +22,7 @@ public class AbstractPrivilegeTest {
protected PrivilegeHandler privilegeHandler;
protected PrivilegeContext ctx;
- protected void login(String username, byte[] password) {
+ protected void login(String username, char[] password) {
Certificate certificate = privilegeHandler.authenticate(username, password);
assertTrue("Certificate is null!", certificate != null);
PrivilegeContext privilegeContext = privilegeHandler.getPrivilegeContext(certificate);
diff --git a/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PersistSessionsTest.java b/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PersistSessionsTest.java
index e79ddbb57..d5c6d02ef 100644
--- a/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PersistSessionsTest.java
+++ b/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PersistSessionsTest.java
@@ -37,7 +37,7 @@ public class PersistSessionsTest extends AbstractPrivilegeTest {
assertFalse("Sessions File should no yet exist", sessionsFile.exists());
// login and assert sessions file was written
- login("admin", "admin".getBytes());
+ login("admin", "admin".toCharArray());
this.privilegeHandler.isCertificateValid(ctx.getCertificate());
assertTrue("Sessions File should have been created!", sessionsFile.isFile());
diff --git a/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PrivilegeConflictMergeTest.java b/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PrivilegeConflictMergeTest.java
index 8943c1c5f..dc52a45cb 100644
--- a/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PrivilegeConflictMergeTest.java
+++ b/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PrivilegeConflictMergeTest.java
@@ -51,7 +51,7 @@ public class PrivilegeConflictMergeTest extends AbstractPrivilegeTest {
@Test
public void shouldMergePrivileges1() {
try {
- login("userA", "admin".getBytes());
+ login("userA", "admin".toCharArray());
IPrivilege privilege = this.ctx.getPrivilege("Foo");
assertTrue(privilege.isAllAllowed());
assertTrue(privilege.getAllowList().isEmpty());
@@ -65,7 +65,7 @@ public class PrivilegeConflictMergeTest extends AbstractPrivilegeTest {
@Test
public void shouldMergePrivileges2() {
try {
- login("userB", "admin".getBytes());
+ login("userB", "admin".toCharArray());
IPrivilege privilege = this.ctx.getPrivilege("Bar");
assertFalse(privilege.isAllAllowed());
assertEquals(2, privilege.getAllowList().size());
diff --git a/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PrivilegeTest.java b/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PrivilegeTest.java
index ec5b1f699..9c684a42b 100644
--- a/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PrivilegeTest.java
+++ b/li.strolch.privilege/src/test/java/li/strolch/privilege/test/PrivilegeTest.java
@@ -70,21 +70,21 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
private static final String ROLE_PRIVILEGE_ADMIN = "PrivilegeAdmin";
private static final String PRIVILEGE_USER_ACCESS = "UserAccessPrivilege";
private static final String ADMIN = "admin";
- private static final byte[] PASS_ADMIN = "admin".getBytes();
+ private static final char[] PASS_ADMIN = "admin".toCharArray();
private static final String BOB = "bob";
private static final String TED = "ted";
private static final String SYSTEM_USER_ADMIN = "system_admin";
private static final String SYSTEM_USER_ADMIN2 = "system_admin2";
- private static final byte[] PASS_BOB = "admin1".getBytes();
+ private static final char[] PASS_BOB = "admin1".toCharArray();
private static final String ROLE_APP_USER = "AppUser";
private static final String ROLE_MY = "MyRole";
private static final String ROLE_MY2 = "MyRole2";
private static final String ROLE_CHANGE_PW = "changePw";
private static final String ROLE_TEMP = "temp";
private static final String ROLE_USER = "user";
- private static final byte[] PASS_DEF = "def".getBytes();
- private static final byte[] PASS_BAD = "123".getBytes();
- private static final byte[] PASS_TED = "12345".getBytes();
+ private static final char[] PASS_DEF = "def".toCharArray();
+ private static final char[] PASS_BAD = "123".toCharArray();
+ private static final char[] PASS_TED = "12345".toCharArray();
private static final Logger logger = LoggerFactory.getLogger(PrivilegeTest.class);
@@ -223,7 +223,7 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
this.exception.expect(AccessDeniedException.class);
this.exception.expectMessage("User system_admin is a system user and may not login!");
try {
- login(SYSTEM_USER_ADMIN, SYSTEM_USER_ADMIN.getBytes());
+ login(SYSTEM_USER_ADMIN, SYSTEM_USER_ADMIN.toCharArray());
} finally {
logout();
}
diff --git a/li.strolch.privilege/src/test/java/li/strolch/privilege/test/XmlTest.java b/li.strolch.privilege/src/test/java/li/strolch/privilege/test/XmlTest.java
index 52ef1c8ee..59e743fc9 100644
--- a/li.strolch.privilege/src/test/java/li/strolch/privilege/test/XmlTest.java
+++ b/li.strolch.privilege/src/test/java/li/strolch/privilege/test/XmlTest.java
@@ -129,7 +129,7 @@ public class XmlTest {
assertEquals(6, containerModel.getParameterMap().size());
assertEquals(3, containerModel.getPolicies().size());
- assertEquals(1, containerModel.getEncryptionHandlerParameterMap().size());
+ assertEquals(3, containerModel.getEncryptionHandlerParameterMap().size());
assertEquals(3, containerModel.getPersistenceHandlerParameterMap().size());
// TODO extend assertions to actual model
@@ -184,7 +184,9 @@ public class XmlTest {
User admin = findUser("admin", users);
assertEquals("1", admin.getUserId());
assertEquals("admin", admin.getUsername());
- assertEquals("8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", admin.getPassword());
+ assertEquals("cb69962946617da006a2f95776d78b49e5ec7941d2bdb2d25cdb05f957f64344",
+ StringHelper.getHexString(admin.getPassword()));
+ assertEquals("61646d696e", StringHelper.getHexString(admin.getSalt()));
assertEquals("Application", admin.getFirstname());
assertEquals("Administrator", admin.getLastname());
assertEquals(UserState.ENABLED, admin.getUserState());
@@ -200,6 +202,7 @@ public class XmlTest {
assertEquals("2", systemAdmin.getUserId());
assertEquals("system_admin", systemAdmin.getUsername());
assertEquals(null, systemAdmin.getPassword());
+ assertEquals(null, systemAdmin.getSalt());
assertEquals("System User", systemAdmin.getFirstname());
assertEquals("Administrator", systemAdmin.getLastname());
assertEquals(UserState.SYSTEM, systemAdmin.getUserState());
@@ -264,9 +267,8 @@ public class XmlTest {
Role systemAdminPrivileges = findRole("system_admin_privileges", roles);
assertEquals("system_admin_privileges", systemAdminPrivileges.getName());
assertEquals(2, systemAdminPrivileges.getPrivilegeNames().size());
- assertThat(systemAdminPrivileges.getPrivilegeNames(),
- containsInAnyOrder("li.strolch.privilege.handler.SystemAction",
- "li.strolch.privilege.test.model.TestSystemRestrictable"));
+ assertThat(systemAdminPrivileges.getPrivilegeNames(), containsInAnyOrder(
+ "li.strolch.privilege.handler.SystemAction", "li.strolch.privilege.test.model.TestSystemRestrictable"));
IPrivilege testSystemUserAction = systemAdminPrivileges
.getPrivilege("li.strolch.privilege.handler.SystemAction");
@@ -288,11 +290,9 @@ public class XmlTest {
Role restrictedRole = findRole("restrictedRole", roles);
assertEquals("restrictedRole", restrictedRole.getName());
assertEquals(1, restrictedRole.getPrivilegeNames().size());
- assertThat(restrictedRole.getPrivilegeNames(),
- containsInAnyOrder("li.strolch.privilege.handler.SystemAction"));
+ assertThat(restrictedRole.getPrivilegeNames(), containsInAnyOrder("li.strolch.privilege.handler.SystemAction"));
- IPrivilege testSystemUserAction2 = restrictedRole
- .getPrivilege("li.strolch.privilege.handler.SystemAction");
+ IPrivilege testSystemUserAction2 = restrictedRole.getPrivilege("li.strolch.privilege.handler.SystemAction");
assertEquals("li.strolch.privilege.handler.SystemAction", testSystemUserAction2.getName());
assertEquals("DefaultPrivilege", testSystemUserAction2.getPolicy());
assertFalse(testSystemUserAction2.isAllAllowed());
@@ -341,16 +341,16 @@ public class XmlTest {
propertyMap.put("prop1", "value1");
userRoles = new HashSet<>();
userRoles.add("role1");
- User user1 = new User("1", "user1", "blabla", "Bob", "White", UserState.DISABLED, userRoles, Locale.ENGLISH,
- propertyMap);
+ User user1 = new User("1", "user1", "blabla".getBytes(), "blabla".getBytes(), "Bob", "White",
+ UserState.DISABLED, userRoles, Locale.ENGLISH, propertyMap);
users.add(user1);
propertyMap = new HashMap<>();
propertyMap.put("prop2", "value2");
userRoles = new HashSet<>();
userRoles.add("role2");
- User user2 = new User("2", "user2", "haha", "Leonard", "Sheldon", UserState.ENABLED, userRoles, Locale.ENGLISH,
- propertyMap);
+ User user2 = new User("2", "user2", "haha".getBytes(), "haha".getBytes(), "Leonard", "Sheldon",
+ UserState.ENABLED, userRoles, Locale.ENGLISH, propertyMap);
users.add(user2);
File modelFile = new File("./target/test/PrivilegeUsersTest.xml");
@@ -370,7 +370,8 @@ public class XmlTest {
assertEquals(user1.getFirstname(), parsedUser1.getFirstname());
assertEquals(user1.getLastname(), parsedUser1.getLastname());
assertEquals(user1.getLocale(), parsedUser1.getLocale());
- assertEquals(user1.getPassword(), parsedUser1.getPassword());
+ assertTrue(Arrays.equals(user1.getPassword(), parsedUser1.getPassword()));
+ assertTrue(Arrays.equals(user1.getSalt(), parsedUser1.getSalt()));
assertEquals(user1.getProperties(), parsedUser1.getProperties());
assertEquals(user1.getUserId(), parsedUser1.getUserId());
assertEquals(user1.getUserState(), parsedUser1.getUserState());
@@ -379,7 +380,8 @@ public class XmlTest {
assertEquals(user2.getFirstname(), parsedUser2.getFirstname());
assertEquals(user2.getLastname(), parsedUser2.getLastname());
assertEquals(user2.getLocale(), parsedUser2.getLocale());
- assertEquals(user2.getPassword(), parsedUser2.getPassword());
+ assertTrue(Arrays.equals(user2.getPassword(), parsedUser2.getPassword()));
+ assertTrue(Arrays.equals(user2.getSalt(), parsedUser2.getSalt()));
assertEquals(user2.getProperties(), parsedUser2.getProperties());
assertEquals(user2.getUserId(), parsedUser2.getUserId());
assertEquals(user2.getUserState(), parsedUser2.getUserState());
diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/DefaultStrolchSessionHandler.java b/li.strolch.rest/src/main/java/li/strolch/rest/DefaultStrolchSessionHandler.java
index 252284c5d..ba0a845a3 100644
--- a/li.strolch.rest/src/main/java/li/strolch/rest/DefaultStrolchSessionHandler.java
+++ b/li.strolch.rest/src/main/java/li/strolch/rest/DefaultStrolchSessionHandler.java
@@ -138,7 +138,7 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
}
@Override
- public Certificate authenticate(String username, byte[] password) {
+ public Certificate authenticate(String username, char[] password) {
DBC.PRE.assertNotEmpty("Username must be set!", username); //$NON-NLS-1$
DBC.PRE.assertNotNull("Passwort must be set", password); //$NON-NLS-1$
diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/StrolchSessionHandler.java b/li.strolch.rest/src/main/java/li/strolch/rest/StrolchSessionHandler.java
index af54aa23d..2678b7519 100644
--- a/li.strolch.rest/src/main/java/li/strolch/rest/StrolchSessionHandler.java
+++ b/li.strolch.rest/src/main/java/li/strolch/rest/StrolchSessionHandler.java
@@ -28,7 +28,7 @@ import li.strolch.rest.model.UserSession;
*/
public interface StrolchSessionHandler {
- public Certificate authenticate(String username, byte[] password);
+ public Certificate authenticate(String username, char[] password);
public Certificate validate(String authToken);
diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/AuthenticationService.java b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/AuthenticationService.java
index 7beec3587..a396c94ef 100644
--- a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/AuthenticationService.java
+++ b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/AuthenticationService.java
@@ -92,7 +92,8 @@ public class AuthenticationService {
sb.append("Password was not given!"); //$NON-NLS-1$
}
- byte[] password = passwordE == null ? new byte[] {} : Base64.getDecoder().decode(passwordE.getAsString());
+ char[] password = passwordE == null ? new char[] {}
+ : new String(Base64.getDecoder().decode(passwordE.getAsString())).toCharArray();
if (password.length < 3) {
if (sb.length() > 0)
sb.append("\n");
@@ -295,7 +296,7 @@ public class AuthenticationService {
String msg = "Authorization cookie is secure, but connection is not secure! Cookie won't be passed to client!";
logger.warn(msg);
}
-
+
NewCookie cookie = new NewCookie(StrolchRestfulConstants.STROLCH_AUTHORIZATION, certificate.getAuthToken(),
"/", null, "Authorization header", (int) TimeUnit.DAYS.toSeconds(1), secureCookie);
diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/PrivilegeUsersService.java b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/PrivilegeUsersService.java
index 0df168ca4..af6d585cf 100644
--- a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/PrivilegeUsersService.java
+++ b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/PrivilegeUsersService.java
@@ -360,7 +360,7 @@ public class PrivilegeUsersService {
PrivilegeSetUserPasswordService svc = new PrivilegeSetUserPasswordService();
PrivilegeSetUserPasswordArgument arg = new PrivilegeSetUserPasswordArgument();
arg.username = username;
- arg.password = Base64.getDecoder().decode(password);
+ arg.password = new String(Base64.getDecoder().decode(password)).toCharArray();
ServiceResult svcResult = svcHandler.doService(cert, svc, arg);
if (svcResult.isOk()) {
diff --git a/li.strolch.rest/src/test/resources/withPrivilegeRuntime/config/PrivilegeConfig.xml b/li.strolch.rest/src/test/resources/withPrivilegeRuntime/config/PrivilegeConfig.xml
index 5ccaf8559..4b73ec32b 100644
--- a/li.strolch.rest/src/test/resources/withPrivilegeRuntime/config/PrivilegeConfig.xml
+++ b/li.strolch.rest/src/test/resources/withPrivilegeRuntime/config/PrivilegeConfig.xml
@@ -10,7 +10,13 @@
-
+
+
+
+
+
+
+
diff --git a/li.strolch.rest/src/test/resources/withPrivilegeRuntime/config/PrivilegeUsers.xml b/li.strolch.rest/src/test/resources/withPrivilegeRuntime/config/PrivilegeUsers.xml
index 7ff78a5fc..94b3a7b23 100644
--- a/li.strolch.rest/src/test/resources/withPrivilegeRuntime/config/PrivilegeUsers.xml
+++ b/li.strolch.rest/src/test/resources/withPrivilegeRuntime/config/PrivilegeUsers.xml
@@ -6,7 +6,7 @@
agent