realm.authenticationProviders =
# How to store passwords.
-# Valid values are plain, md5, combined-md5 or PBKDF2WithHmacSHA256.
+# Valid values are plain, md5, combined-md5 or pbkdf2.
# md5 is the hash of password.
# combined-md5 is the hash of username.toLowerCase()+password.
-# PBKDF2WithHmacSHA256 is salt+hash(salt+password)
-# Default is PBKDF2WithHmacSHA256.
+# pbkdf2 implements the PBKDF2 algorithm, which is a secure, salted password hashing scheme.
+# Default is pbkdf2. Using plain, md5 or combined-md5 is deprecated, as these are insecure schemes by now.
#
# SINCE 0.5.0
-realm.passwordStorage = PBKDF2WithHmacSHA256
+realm.passwordStorage = pbkdf2
# Minimum valid length for a plain text password.
# Default value is 5. Absolute minimum is 4.
import com.gitblit.models.ServerSettings;\r
import com.gitblit.models.TeamModel;\r
import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.SecurePasswordHashUtils;\r
+import com.gitblit.utils.PasswordHash;\r
import com.gitblit.utils.StringUtils;\r
\r
\r
minLength));\r
return false;\r
}\r
- if (!password.toUpperCase().startsWith(StringUtils.MD5_TYPE)\r
- && !password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)\r
- && !password.startsWith(SecurePasswordHashUtils.PBKDF2WITHHMACSHA256_TYPE)) {\r
+ // What we actually test for here, is if the password has been changed. But this also catches if the password\r
+ // was not changed, but is stored in plain-text. Which is good because then editing the user will hash the\r
+ // password if by now the storage has been changed to a hashed variant.\r
+ if (!PasswordHash.isHashedEntry(password)) {\r
String cpw = new String(confirmPasswordField.getPassword());\r
if (cpw == null || cpw.length() != password.length()) {\r
error("Please confirm the password!");\r
// change the cookie\r
user.cookie = user.createCookie();\r
\r
- String type = settings.get(Keys.realm.passwordStorage).getString(SecurePasswordHashUtils.PBKDF2WITHHMACSHA256);\r
- if (type.equalsIgnoreCase("md5")) {\r
- // store MD5 digest of password\r
- user.password = StringUtils.MD5_TYPE + StringUtils.getMD5(password);\r
- } else if (type.equalsIgnoreCase("combined-md5")) {\r
- // store MD5 digest of username+password\r
- user.password = StringUtils.COMBINED_MD5_TYPE\r
- + StringUtils.getMD5(user.username + password);\r
- } else if (type.equalsIgnoreCase(SecurePasswordHashUtils.PBKDF2WITHHMACSHA256)) {\r
- // store PBKDF2WithHmacSHA256 digest of password\r
- user.password = SecurePasswordHashUtils.get().createStoredPasswordFromPassword(password);\r
+ String type = settings.get(Keys.realm.passwordStorage).getString(PasswordHash.getDefaultType().name());\r
+ PasswordHash pwdHash = PasswordHash.instanceOf(type);\r
+ if (pwdHash != null) {\r
+ user.password = pwdHash.toHashedEntry(password, user.username);\r
} else {\r
- // plain-text password\r
+ // plain-text password.\r
+ // TODO: This is also used when the "realm.passwordStorage" configuration is not a valid type.\r
+ // This is a rather bad default, and should probably caught and changed to a secure default.\r
user.password = password;\r
}\r
- } else if (rename && password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)) {\r
+ } else if (rename && password.toUpperCase().startsWith(PasswordHash.Type.CMD5.name())) {\r
error("Gitblit is configured for combined-md5 password hashing. You must enter a new password on account rename.");\r
return false;\r
} else {\r
import com.gitblit.transport.ssh.SshKey;
import com.gitblit.utils.Base64;
import com.gitblit.utils.HttpUtils;
-import com.gitblit.utils.SecurePasswordHashUtils;
+import com.gitblit.utils.PasswordHash;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.X509Utils.X509Metadata;
import com.google.inject.Inject;
*/
protected UserModel authenticateLocal(UserModel user, char [] password) {
UserModel returnedUser = null;
- boolean strongHashUsed = false;
- if (user.password.startsWith(StringUtils.MD5_TYPE)) {
- // password digest
- String md5 = StringUtils.MD5_TYPE + StringUtils.getMD5(new String(password));
- if (user.password.equalsIgnoreCase(md5)) {
+ PasswordHash pwdHash = PasswordHash.instanceFor(user.password);
+ if (pwdHash != null) {
+ if (pwdHash.matches(user.password, password, user.username)) {
returnedUser = user;
}
- } else if (user.password.startsWith(StringUtils.COMBINED_MD5_TYPE)) {
- // username+password digest
- String md5 = StringUtils.COMBINED_MD5_TYPE
- + StringUtils.getMD5(user.username.toLowerCase() + new String(password));
- if (user.password.equalsIgnoreCase(md5)) {
- returnedUser = user;
- }
- } else if (user.password.startsWith(SecurePasswordHashUtils.PBKDF2WITHHMACSHA256_TYPE)){
- // strong hash
- SecurePasswordHashUtils hashUtils = SecurePasswordHashUtils.get();
- boolean isPasswordValid = hashUtils.isPasswordCorrect(password, user.password);
- if(isPasswordValid){
- returnedUser = user;
- strongHashUsed = true;
- }
} else if (user.password.equals(new String(password))) {
// plain-text password
returnedUser = user;
// validate user
returnedUser = validateAuthentication(returnedUser, AuthenticationType.CREDENTIALS);
- // if no strong hash was used to store the password, try to update it based on the settings
- if(!strongHashUsed){
- updateStoredPassword(returnedUser, password);
- }
-
+ // try to upgrade the stored password hash to a stronger hash, if necessary
+ upgradeStoredPassword(returnedUser, password, pwdHash);
+
return returnedUser;
}
/**
- * Update stored password to a strong hash if configured.
+ * Upgrade stored password to a strong hash if configured.
*
* @param user the user to be updated
* @param password the password
+ * @param pwdHash
+ * Instance of PasswordHash for the stored password entry. If null, no current hashing is assumed.
*/
- protected void updateStoredPassword(UserModel user, char[] password) {
+ private void upgradeStoredPassword(UserModel user, char[] password, PasswordHash pwdHash) {
// check if user has successfully authenticated i.e. is not null
- if(user != null){
- // check if strong hash algorithm is configured
- String algorithm = settings.getString(Keys.realm.passwordStorage, SecurePasswordHashUtils.PBKDF2WITHHMACSHA256);
- if(algorithm.equals(SecurePasswordHashUtils.PBKDF2WITHHMACSHA256)){
- // rehash the provided correct password and update the user model
- user.password = SecurePasswordHashUtils.get().createStoredPasswordFromPassword(password);
+ if (user == null) return;
+
+ // check if strong hash algorithm is configured
+ String algorithm = settings.getString(Keys.realm.passwordStorage, PasswordHash.getDefaultType().name());
+ if (pwdHash == null || pwdHash.needsUpgradeTo(algorithm)) {
+ // rehash the provided correct password and update the user model
+ pwdHash = PasswordHash.instanceOf(algorithm);
+ if (pwdHash != null) { // necessary since the algorithm name could be something not supported.
+ user.password = pwdHash.toHashedEntry(password, user.username);
userManager.updateUserModel(user);
- }
+ }
}
}
+
/**
* Returns the Gitlbit cookie in the request.
*
*/
public abstract class PasswordHash {
-
/**
* The types of implemented password hashing schemes.
*/
- enum Type {
+ public enum Type {
MD5,
CMD5,
- PBKDF2
+ PBKDF2;
+
+ static Type fromName(String name) {
+ if (name == null) return null;
+ for (Type type : Type.values()) {
+ if (type.name().equalsIgnoreCase(name)) return type;
+ }
+ // Compatibility with type id "PBKDF2WITHHMACSHA256", which is also handled by PBKDF2 type.
+ if (name.equalsIgnoreCase("PBKDF2WITHHMACSHA256")) return Type.PBKDF2;
+
+ // Recognise the name used for CMD5 in the settings file.
+ if (name.equalsIgnoreCase("combined-md5")) return Type.CMD5;
+
+ return null;
+ }
}
/**
}
+ /**
+ * When no hash type is specified, this determines the default that should be used.
+ */
+ public static Type getDefaultType() {
+ return Type.PBKDF2;
+ }
+
+
/**
* Create an instance of a password hashing class for the given hash type.
*
* or null if the given hash type is not a valid one.
*/
public static PasswordHash instanceOf(String type) {
- try {
- Type hashType = Type.valueOf(type.toUpperCase());
- switch (hashType) {
- case MD5:
- return new PasswordHashMD5();
- case CMD5:
- return new PasswordHashCombinedMD5();
- case PBKDF2:
- return new PasswordHashPbkdf2();
- default:
- return null;
- }
- }
- catch (Exception e) {
- return null;
+ Type hashType = Type.fromName(type);
+ if (hashType == null) return null;
+ switch (hashType) {
+ case MD5:
+ return new PasswordHashMD5();
+ case CMD5:
+ return new PasswordHashCombinedMD5();
+ case PBKDF2:
+ return new PasswordHashPbkdf2();
+ default:
+ return null;
}
}
}
+ /**
+ * Some hash methods are considered more secure than others. This method determines for a certain type
+ * of password hash if it is inferior than a given other type and stored passwords should be
+ * upgraded to the given hashing method.
+ *
+ * @param algorithm
+ * Password hashing type to be checked if it is superior than the one of this instance.
+ * @return True, if the given type in parameter {@code algorithm} is better and stored passwords should be upgraded to it,
+ * false, otehrwise.
+ */
+ public boolean needsUpgradeTo(String algorithm) {
+ Type hashType = Type.fromName(algorithm);
+ if (hashType == null) return false;
+ if (this.type == hashType) return false;
+
+ // Right now we keep it really simple. With the existing types, only PBKDF2 is considered secure,
+ // everything else is inferior. This will need to be updated once more secure hashing algorithms
+ // are implemented, or the workload/parameters of the PBKDF2 are changed.
+ return hashType == Type.PBKDF2;
+ }
+
/**
* Convert the given password to a hashed password entry to be stored in the user table.
int indexOfSeparator = hashedEntry.indexOf(':');
if (indexOfSeparator <= 0) return null;
String typeId = hashedEntry.substring(0, indexOfSeparator);
-
- // Compatibility with type id "PBKDF2WITHHMACSHA256", which is also handled by PBKDF2 type.
- if (typeId.equalsIgnoreCase("PBKDF2WITHHMACSHA256")) return Type.PBKDF2;
- try {
- return Type.valueOf(typeId.toUpperCase());
- }
- catch (Exception e) { return null;}
+ return Type.fromName(typeId);
}
+++ /dev/null
-package com.gitblit.utils;
-
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.PBEKeySpec;
-
-import org.apache.commons.codec.DecoderException;
-import org.apache.commons.codec.binary.Hex;
-
-import java.security.NoSuchAlgorithmException;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-
-/**
- * The Class SecurePasswordHashUtils provides methods to create and validate
- * secure hashes from user passwords.
- *
- * It uses the concept proposed by OWASP - Hashing Java:
- * https://www.owasp.org/index.php/Hashing_Java
- */
-public class SecurePasswordHashUtils {
-
- public static final String PBKDF2WITHHMACSHA256 = "PBKDF2WithHmacSHA256";
- public static final String PBKDF2WITHHMACSHA256_TYPE = PBKDF2WITHHMACSHA256.toUpperCase() + ":";
-
- private static final SecureRandom RANDOM = new SecureRandom();
- private static final int ITERATIONS = 10000;
- private static final int KEY_LENGTH = 256;
- private static final int SALT_LENGTH = 32;
-
- /** The instance. */
- private static SecurePasswordHashUtils instance;
-
- /**
- * Instantiates a new secure password hash utils.
- */
- private SecurePasswordHashUtils() {
- }
-
- /**
- * Gets an instance of {@link SecurePasswordHashUtils}.
- *
- * @return the secure password hash utils
- */
- public static SecurePasswordHashUtils get() {
- if (instance == null) {
- instance = new SecurePasswordHashUtils();
- }
- return instance;
- }
-
- /**
- * Gets the next salt.
- *
- * @return the next salt
- */
- protected byte[] getNextSalt() {
- byte[] salt = new byte[SALT_LENGTH];
- RANDOM.nextBytes(salt);
- return salt;
- }
-
- /**
- * Hash.
- *
- * @param password
- * the password
- * @param salt
- * the salt
- * @return the byte[]
- */
- protected byte[] hash(char[] password, byte[] salt) {
- PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
- Arrays.fill(password, Character.MIN_VALUE);
- try {
- SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
- return skf.generateSecret(spec).getEncoded();
- } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
- throw new IllegalStateException("Error while hashing password", e);
- } finally {
- spec.clearPassword();
- }
- }
-
- /**
- * Checks if is password correct.
- *
- * @param passwordToCheck
- * the password to check
- * @param salt
- * the salt
- * @param expectedHash
- * the expected hash
- * @return true, if is password correct
- */
- protected boolean isPasswordCorrect(char[] passwordToCheck, byte[] salt, byte[] expectedHash) {
- byte[] hashToCheck = hash(passwordToCheck, salt);
- Arrays.fill(passwordToCheck, Character.MIN_VALUE);
- if (hashToCheck.length != expectedHash.length) {
- return false;
- }
- for (int i = 0; i < hashToCheck.length; i++) {
- if (hashToCheck[i] != expectedHash[i]) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Creates the new secure hash from a password and formats it properly to be
- * stored in a file.
- *
- * @param password
- * the password to be hashed
- * @return the sting to be stored in a file (users.conf)
- */
- public String createStoredPasswordFromPassword(String password) {
- return createStoredPasswordFromPassword(password.toCharArray());
- }
-
- /**
- * Creates the new secure hash from a password and formats it properly to be
- * stored in a file.
- *
- * @param password
- * the password to be hashed
- * @return the sting to be stored in a file (users.conf)
- */
- public String createStoredPasswordFromPassword(char[] password) {
- byte[] salt = getNextSalt();
- return String.format("%s%s%s", SecurePasswordHashUtils.PBKDF2WITHHMACSHA256_TYPE, StringUtils.toHex(salt),
- StringUtils.toHex(hash(password, salt)));
- }
-
- /**
- * Gets the salt from stored password.
- *
- * @param storedPassword
- * the stored password
- * @return the salt from stored password
- */
- protected byte[] getSaltFromStoredPassword(String storedPassword) {
- byte[] pw = getStoredHashWithStrippedPrefix(storedPassword);
- return Arrays.copyOfRange(pw, 0, SALT_LENGTH);
- }
-
- /**
- * Gets the hash from stored password.
- *
- * @param storedPassword
- * the stored password
- * @return the hash from stored password
- */
- protected byte[] getHashFromStoredPassword(String storedPassword) {
- byte[] pw = getStoredHashWithStrippedPrefix(storedPassword);
- return Arrays.copyOfRange(pw, SALT_LENGTH, pw.length);
- }
-
- /**
- * Strips the prefix ({@link #PBKDF2WITHHMACSHA256_TYPE}) from a stored
- * password and returns the decoded hash
- *
- * @param storedPassword
- * the stored password
- * @return the stored hash with stripped prefix
- */
- protected byte[] getStoredHashWithStrippedPrefix(String storedPassword) {
- String saltAndHash = storedPassword.substring(PBKDF2WITHHMACSHA256_TYPE.length());
- try {
- return Hex.decodeHex(saltAndHash.toCharArray());
- } catch (DecoderException e) {
- throw new IllegalStateException("Error while reading stored credentials", e);
- }
- }
-
- /**
- * Checks if is password correct.
- *
- * @param password
- * the password
- * @param storedPassword
- * the stored password
- * @return true, if is password correct
- */
- public boolean isPasswordCorrect(char[] password, String storedPassword) {
- byte[] storedSalt = getSaltFromStoredPassword(storedPassword);
- byte[] storedHash = getHashFromStoredPassword(storedPassword);
- return isPasswordCorrect(password, storedSalt, storedHash);
- }
-}
*/\r
public class StringUtils {\r
\r
- public static final String MD5_TYPE = "MD5:";\r
-\r
- public static final String COMBINED_MD5_TYPE = "CMD5:";\r
-\r
/**\r
* Returns true if the string is null or empty.\r
*\r
import com.gitblit.GitBlitException;\r
import com.gitblit.Keys;\r
import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.SecurePasswordHashUtils;\r
-import com.gitblit.utils.StringUtils;\r
+import com.gitblit.utils.PasswordHash;\r
import com.gitblit.wicket.GitBlitWebSession;\r
import com.gitblit.wicket.NonTrimmedPasswordTextField;\r
\r
public class ChangePasswordPage extends RootSubPage {\r
\r
- IModel<String> password = new Model<String>("");\r
- IModel<String> confirmPassword = new Model<String>("");\r
+ private IModel<String> password = new Model<String>("");\r
+ private IModel<String> confirmPassword = new Model<String>("");\r
\r
public ChangePasswordPage() {\r
super();\r
\r
UserModel user = GitBlitWebSession.get().getUser();\r
\r
- // convert to MD5 digest, if appropriate\r
- String type = app().settings().getString(Keys.realm.passwordStorage, SecurePasswordHashUtils.PBKDF2WITHHMACSHA256);\r
- if (type.equalsIgnoreCase("md5")) {\r
- // store MD5 digest of password\r
- password = StringUtils.MD5_TYPE + StringUtils.getMD5(password);\r
- } else if (type.equalsIgnoreCase("combined-md5")) {\r
- // store MD5 digest of username+password\r
- password = StringUtils.COMBINED_MD5_TYPE\r
- + StringUtils.getMD5(user.username.toLowerCase() + password);\r
- } else if (type.equalsIgnoreCase(SecurePasswordHashUtils.PBKDF2WITHHMACSHA256)) {\r
- // store PBKDF2WithHmacSHA256 digest of password\r
- user.password = SecurePasswordHashUtils.get().createStoredPasswordFromPassword(password);\r
+ // convert to digest, if appropriate\r
+ String type = app().settings().getString(Keys.realm.passwordStorage, PasswordHash.getDefaultType().name());\r
+ PasswordHash pwdHash = PasswordHash.instanceOf(type);\r
+ if (pwdHash != null) {\r
+ password = pwdHash.toHashedEntry(password, user.username);\r
}\r
\r
user.password = password;\r
import java.util.Iterator;\r
import java.util.List;\r
\r
+import com.gitblit.utils.PasswordHash;\r
import org.apache.wicket.PageParameters;\r
import org.apache.wicket.behavior.SimpleAttributeModifier;\r
import org.apache.wicket.extensions.markup.html.form.palette.Palette;\r
return;\r
}\r
String password = userModel.password;\r
- if (!password.toUpperCase().startsWith(StringUtils.MD5_TYPE)\r
- && !password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)) {\r
+ if (!PasswordHash.isHashedEntry(password)) {\r
// This is a plain text password.\r
// Check length.\r
int minLength = app().settings().getInteger(Keys.realm.minPasswordLength, 5);\r
if (minLength < 4) {\r
minLength = 4;\r
}\r
- if (password.trim().length() < minLength) {\r
+ if (password.trim().length() < minLength) { // TODO: Why do we trim here, but not in EditUserDialog and ChangePasswordPage?\r
error(MessageFormat.format(getString("gb.passwordTooShort"),\r
minLength));\r
return;\r
userModel.cookie = userModel.createCookie();\r
\r
// Optionally store the password MD5 digest.\r
- String type = app().settings().getString(Keys.realm.passwordStorage, "md5");\r
- if (type.equalsIgnoreCase("md5")) {\r
- // store MD5 digest of password\r
- userModel.password = StringUtils.MD5_TYPE\r
- + StringUtils.getMD5(userModel.password);\r
- } else if (type.equalsIgnoreCase("combined-md5")) {\r
- // store MD5 digest of username+password\r
- userModel.password = StringUtils.COMBINED_MD5_TYPE\r
- + StringUtils.getMD5(username + userModel.password);\r
+ String type = app().settings().getString(Keys.realm.passwordStorage, PasswordHash.getDefaultType().name());\r
+ PasswordHash pwdh = PasswordHash.instanceOf(type);\r
+ if (pwdh != null) { // Hash the password\r
+ userModel.password = pwdh.toHashedEntry(password, username);\r
}\r
} else if (rename\r
- && password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)) {\r
+ && password.toUpperCase().startsWith(PasswordHash.Type.CMD5.name())) {\r
error(getString("gb.combinedMd5Rename"));\r
return;\r
}\r
Whitespace is illegal.\r
\r
### Passwords\r
-User passwords are CASE-SENSITIVE and may be *plain*, *md5*, *combined-md5* or *PBKDF2WithHmacSHA256* formatted (see `gitblit.properties` -> *realm.passwordStorage*).\r
+User passwords are CASE-SENSITIVE and may be *plain*, *md5*, *combined-md5* or *pbkdf2* formatted (see `gitblit.properties` -> *realm.passwordStorage*).\r
\r
### User Roles\r
There are four actual *roles* in Gitblit:\r
import javax.servlet.http.HttpUpgradeHandler;
import javax.servlet.http.Part;
+import com.gitblit.utils.PasswordHash;
import org.junit.Test;
import com.gitblit.IUserService;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.tests.mock.MemorySettings;
-import com.gitblit.utils.SecurePasswordHashUtils;
import com.gitblit.utils.XssFilter;
import com.gitblit.utils.XssFilter.AllowXssFilter;
users.updateUserModel(user);
assertNotNull(auth.authenticate(user.username, user.password.toCharArray(), null));
-
- // validate that plaintext password was automatically updated to hashed one
- assertTrue(user.password.startsWith(SecurePasswordHashUtils.PBKDF2WITHHMACSHA256_TYPE));
-
user.disabled = true;
users.updateUserModel(user);
assertNull(auth.authenticate(user.username, user.password.toCharArray(), null));
users.deleteUserModel(user);
}
-
+
+
+ @Test
+ public void testAuthenticateUpgradePlaintext() throws Exception {
+ IAuthenticationManager auth = newAuthenticationManager();
+
+ UserModel user = new UserModel("sunnyjim");
+ user.password = "password";
+ users.updateUserModel(user);
+
+ assertNotNull(auth.authenticate(user.username, user.password.toCharArray(), null));
+
+ // validate that plaintext password was automatically updated to hashed one
+ assertTrue(user.password.startsWith(PasswordHash.getDefaultType().name() + ":"));
+ }
+
+
+ @Test
+ public void testAuthenticateUpgradeMD5() throws Exception {
+ IAuthenticationManager auth = newAuthenticationManager();
+
+ UserModel user = new UserModel("sunnyjim");
+ user.password = "MD5:5F4DCC3B5AA765D61D8327DEB882CF99";
+ users.updateUserModel(user);
+
+ assertNotNull(auth.authenticate(user.username, "password".toCharArray(), null));
+
+ // validate that MD5 password was automatically updated to hashed one
+ assertTrue(user.password.startsWith(PasswordHash.getDefaultType().name() + ":"));
+ }
+
@Test
public void testContenairAuthenticate() throws Exception {
assertTrue("Failed to match " +CMD5_HASHED_ENTRY_0, pwdh.matches(CMD5_HASHED_ENTRY_0, CMD5_PASSWORD_0.toCharArray(), CMD5_USERNAME_0));
+ pwdh = PasswordHash.instanceOf("combined-md5");
+ assertNotNull(pwdh);
+ assertEquals(PasswordHash.Type.CMD5, pwdh.type);
+
+ pwdh = PasswordHash.instanceOf("COMBINED-MD5");
+ assertNotNull(pwdh);
+ assertEquals(PasswordHash.Type.CMD5, pwdh.type);
+
+
pwdh = PasswordHash.instanceOf("MD5");
assertNotNull(pwdh);
assertNotEquals(PasswordHash.Type.CMD5, pwdh.type);
assertFalse("Matched wrong hashed entry, with empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, PBKDF2_PASSWORD_0.toCharArray(), ""));
assertFalse("Matched wrong hashed entry, with user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, PBKDF2_PASSWORD_0.toCharArray(), "someuser"));
}
+
+ @Test
+ public void getEntryType() {
+ assertEquals(PasswordHash.Type.MD5, PasswordHash.getEntryType("MD5:blah"));
+ assertEquals(PasswordHash.Type.MD5, PasswordHash.getEntryType("md5:blah"));
+ assertEquals(PasswordHash.Type.MD5, PasswordHash.getEntryType("mD5:blah"));
+
+ assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("CMD5:blah"));
+ assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("cmd5:blah"));
+ assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("Cmd5:blah"));
+
+ assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("combined-md5:blah"));
+ assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("COMBINED-MD5:blah"));
+ assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("combined-MD5:blah"));
+
+ assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("PBKDF2:blah"));
+ assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("pbkdf2:blah"));
+ assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("Pbkdf2:blah"));
+ assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("pbKDF2:blah"));
+
+ assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("PBKDF2WithHmacSHA256:blah"));
+ assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("PBKDF2WITHHMACSHA256:blah"));
+ }
+
+ @Test
+ public void getEntryValue() {
+ assertEquals("value", PasswordHash.getEntryValue("MD5:value"));
+ assertEquals("plain text", PasswordHash.getEntryValue("plain text"));
+ assertEquals("what this", PasswordHash.getEntryValue(":what this"));
+ assertEquals("", PasswordHash.getEntryValue(":"));
+ }
}
+++ /dev/null
-package com.gitblit.utils;
-
-import static org.junit.Assert.*;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class SecurePasswordHashUtilsTest {
-
- private static final String STORED_PASSWORD = "PBKDF2WITHHMACSHA256:2d7d3ccaa277787f288e9f929247361bfc83607c6a8447bf496267512e360ba0a97b3114937213b23230072517d65a2e00695a1cbc47a732510840817f22c1bc";
- private static final byte[] STORED_SALT_BYTES = new byte[]{45, 125, 60, -54, -94, 119, 120, 127, 40, -114, -97, -110, -110, 71, 54, 27, -4, -125, 96, 124, 106, -124, 71, -65, 73, 98, 103, 81, 46, 54, 11, -96};
- private static final byte[] STORED_HASH_BYTES = new byte[]{-87, 123, 49, 20, -109, 114, 19, -78, 50, 48, 7, 37, 23, -42, 90, 46, 0, 105, 90, 28, -68, 71, -89, 50, 81, 8, 64, -127, 127, 34, -63, -68};
-
- private SecurePasswordHashUtils utils;
-
- @Before
- public void init(){
- utils = SecurePasswordHashUtils.get();
- }
-
- @Test
- public void testGetNextSalt() {
- assertEquals(32, utils.getNextSalt().length);
- }
-
- @Test
- public void testHash() {
- byte[] hash = utils.hash("foo".toCharArray(), STORED_SALT_BYTES);
- assertArrayEquals(STORED_HASH_BYTES, hash);
- }
-
- @Test
- public void testIsPasswordCorrectCharArrayByteArrayByteArray() {
- assertTrue(utils.isPasswordCorrect("foo".toCharArray(), STORED_SALT_BYTES, STORED_HASH_BYTES));
- assertFalse(utils.isPasswordCorrect("bar".toCharArray(), STORED_SALT_BYTES, STORED_HASH_BYTES));
- }
-
- @Test
- public void testCreateNewStorableHashFromPassword() {
- String newPwHash = utils.createStoredPasswordFromPassword("foo");
- assertTrue(newPwHash.startsWith(SecurePasswordHashUtils.PBKDF2WITHHMACSHA256_TYPE));
- }
-
- @Test
- public void testGetSaltFromStoredPassword() {
- byte[] saltFromStoredPassword = utils.getSaltFromStoredPassword(STORED_PASSWORD);
- assertArrayEquals(STORED_SALT_BYTES, saltFromStoredPassword);
-
- }
-
- @Test
- public void testGetHashFromStoredPassword() {
- byte[] hashFromStoredPassword = utils.getHashFromStoredPassword(STORED_PASSWORD);
- assertArrayEquals(STORED_HASH_BYTES, hashFromStoredPassword);
- }
-
- @Test
- public void testIsPasswordCorrectCharArrayString() {
- assertTrue(utils.isPasswordCorrect("foo".toCharArray(), STORED_PASSWORD));
- assertFalse(utils.isPasswordCorrect("bar".toCharArray(), STORED_PASSWORD));
- }
-
-}