diff --git a/README.md b/README.md
index 7850f8cd..486aea49 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,26 @@ ohmage depends on a set of directories to store log files and user data. By defa
Any Servlet 3.0 compliant container should work. Internally, we use Tomcat. To build the WAR file, use `ant clean dist`, which will produce an ssl-disabled container. It should be noted that we do not recommend having the servlet itself handle SSL, and instead suggest you use a web server like nginx or apache to do SSL termination.
+## Using SHA-512 for Password Hashing
+
+The Blowfish algorithm is used by default for password hashing. Ohmage supports SHA512 algorithm for password hashing.
+
+To use SHA-512, run the following commands in MySQL console as soon as ohmage is up and running:
+```
+--Enables SHA-512 password hashing
+UPDATE `ohmage`.`preference` SET `p_value` = 'true' WHERE `p_key` = 'sha512_password_hash_enabled';
+--Updates the default SHA512 password hash for `ohmage.admin` user to `ohmage.passwd`
+UPDATE `ohmage`.`user` SET `password` = '$6$Afmg23YTsd$113jh7VsD6q6wDnDWD9SqJUzobqjFIuGMhpgpuXM49acjyjFfWOGAhzT7W7zRleIhN2Xe.xH7ki2bk8nBlsX4/' WHERE `username` = 'ohmage.admin';
+```
+
+To use blowfish, run the following commands:
+```
+--Disables SHA-512 password hashing (thus enabling default blowfish)
+UPDATE `ohmage`.`preference` SET `p_value` = 'false' WHERE `p_key` = 'sha512_password_hash_enabled';
+--Updates the default blowfish password hash for `ohmage.admin` user to `ohmage.passwd`
+UPDATE `ohmage`.`user` SET `password` = '$2a$13$yxus2tQ3/QiOwWcELImOQuy9d5PXWbByQ6Bhp52b1se7fNYGFxN5i' WHERE `username` = 'ohmage.admin';
+```
+
# Collaboration
The coding rules are loose, and the best reference would be other parts of the code. A few rules we do have are:
diff --git a/build.xml b/build.xml
index 143c82a8..6fd575e5 100644
--- a/build.xml
+++ b/build.xml
@@ -13,6 +13,7 @@
+
@@ -45,6 +46,7 @@
+
@@ -125,7 +127,7 @@
-
+
1) {
@@ -160,7 +178,7 @@ else if(user.hashPassword()) {
else {
hashedPassword = user.getPassword();
}
-
+
// Get the user's information from the database.
try {
UserInformation userInformation = instance.getJdbcTemplate().queryForObject(
diff --git a/src/org/ohmage/service/ConfigServices.java b/src/org/ohmage/service/ConfigServices.java
index 5256115f..f9ff2281 100644
--- a/src/org/ohmage/service/ConfigServices.java
+++ b/src/org/ohmage/service/ConfigServices.java
@@ -174,6 +174,25 @@ public static ServerConfig readServerConfiguration()
catch(IllegalArgumentException e) {
throw new ServiceException("The local auth config was not a valid boolean.", e);
}
+
+ boolean sha512PasswordHashEnabled;
+ try {
+ sha512PasswordHashEnabled =
+ StringUtils.decodeBoolean(
+ PreferenceCache.instance().lookup(
+ PreferenceCache.KEY_SHA512_PASSWORD_HASH_ENABLED));
+ }
+ catch(CacheMissException e) {
+ sha512PasswordHashEnabled =
+ ServerConfig.DEFAULT_SHA512_PASSWORD_HASH_ENABLED;
+ LOGGER.warn("sha512_password_hash_enabled config is "
+ + "missing from the DB. Will default to "
+ + sha512PasswordHashEnabled);
+ }
+ catch(IllegalArgumentException e) {
+ throw new ServiceException("The sha512_password_hash_enabled "
+ + " was not a valid boolean.", e);
+ }
String publicClassId;
try {
@@ -200,6 +219,7 @@ public static ServerConfig readServerConfiguration()
userSetupEnabled,
keycloakAuthEnabled,
localAuthEnabled,
+ sha512PasswordHashEnabled,
publicClassId);
}
catch(DomainException e) {
diff --git a/src/org/ohmage/service/UserServices.java b/src/org/ohmage/service/UserServices.java
index 01229ab4..b3fe33ed 100644
--- a/src/org/ohmage/service/UserServices.java
+++ b/src/org/ohmage/service/UserServices.java
@@ -48,6 +48,9 @@
import javax.mail.internet.MimeMessage;
import com.sun.mail.smtp.SMTPTransport;
+import org.apache.commons.codec.digest.Crypt;
+import org.ohmage.service.ConfigServices;
+
import jbcrypt.BCrypt;
import net.tanesha.recaptcha.ReCaptchaImpl;
import net.tanesha.recaptcha.ReCaptchaResponse;
@@ -121,7 +124,7 @@ public final class UserServices {
private IUserClassQueries userClassQueries;
private IUserImageQueries userImageQueries;
private IImageQueries imageQueries;
-
+
/**
* Default constructor. Privately instantiated via dependency injection
* (reflection).
@@ -161,7 +164,7 @@ private UserServices(IUserQueries iUserQueries,
userCampaignQueries = iUserCampaignQueries;
userClassQueries = iUserClassQueries;
userImageQueries = iUserImageQueries;
- imageQueries = iImageQueries;
+ imageQueries = iImageQueries;
}
/**
@@ -170,7 +173,7 @@ private UserServices(IUserQueries iUserQueries,
public static UserServices instance() {
return instance;
}
-
+
/**
* Creates a new user.
*
@@ -211,8 +214,15 @@ public void createUser(
hashedPassword =
KeycloakUser.KEYCLOAK_USER_PASSWORD;
} else {
- hashedPassword =
- BCrypt.hashpw(password, BCrypt.gensalt(User.BCRYPT_COMPLEXITY));
+ if( ConfigServices.readServerConfiguration().getSha512PasswordHashingEnabled() ) {
+ hashedPassword =
+ Crypt.crypt(password);
+ }
+ else {
+ hashedPassword =
+ BCrypt.hashpw(password, BCrypt.gensalt(User.BCRYPT_COMPLEXITY));
+ }
+
}
userQueries
@@ -276,8 +286,14 @@ public boolean createUser(
hashedPassword =
KeycloakUser.KEYCLOAK_USER_PASSWORD;
} else {
- hashedPassword =
- BCrypt.hashpw(password, BCrypt.gensalt(User.BCRYPT_COMPLEXITY));
+ if(ConfigServices.readServerConfiguration().getSha512PasswordHashingEnabled()) {
+ hashedPassword =
+ Crypt.crypt(password);
+ }
+ else {
+ hashedPassword =
+ BCrypt.hashpw(password, BCrypt.gensalt(User.BCRYPT_COMPLEXITY));
+ }
}
return
@@ -338,11 +354,17 @@ public void createUserRegistration(
}
String registrationId = buffer.toString();
+
// Hash the password.
- String hashedPassword =
- BCrypt.hashpw(
- password,
- BCrypt.gensalt(User.BCRYPT_COMPLEXITY));
+ String hashedPassword;
+ if(ConfigServices.readServerConfiguration().getSha512PasswordHashingEnabled()) {
+ hashedPassword =
+ Crypt.crypt(password);
+ }
+ else {
+ hashedPassword =
+ BCrypt.hashpw(password, BCrypt.gensalt(User.BCRYPT_COMPLEXITY));
+ }
// Create the user in the database with all of its connections.
userQueries.createUserRegistration(
@@ -2039,9 +2061,18 @@ public void resetPassword(final String username)
}
try {
+ String hashedPassword;
+ if(ConfigServices.readServerConfiguration().getSha512PasswordHashingEnabled()) {
+ hashedPassword =
+ Crypt.crypt(newPassword);
+ }
+ else {
+ hashedPassword =
+ BCrypt.hashpw(newPassword, BCrypt.gensalt(User.BCRYPT_COMPLEXITY));
+ }
userQueries.updateUserPassword(
username,
- BCrypt.hashpw(newPassword, BCrypt.gensalt(13)),
+ hashedPassword,
true);
}
catch(DataAccessException e) {
@@ -2194,15 +2225,19 @@ public String updatePassword(final String username,
final String plaintextPassword) throws ServiceException {
try {
- String hashedPassword =
- BCrypt
- .hashpw(
- plaintextPassword,
- BCrypt.gensalt(User.BCRYPT_COMPLEXITY));
-
- userQueries.updateUserPassword(username, hashedPassword, false);
-
- return hashedPassword;
+ String hashedPassword;
+ if(ConfigServices.readServerConfiguration().getSha512PasswordHashingEnabled()) {
+ hashedPassword =
+ Crypt.crypt(plaintextPassword);
+ }
+ else {
+ hashedPassword =
+ BCrypt.hashpw(plaintextPassword, BCrypt.gensalt(User.BCRYPT_COMPLEXITY));
+ }
+
+ userQueries.updateUserPassword(username, hashedPassword, false);
+
+ return hashedPassword;
}
catch(DataAccessException e) {
throw new ServiceException(e);