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);