Automatically converting password hashes in Grails spring-security-core

I was looking at this Stack Overflow question about converting password hashes and realized that it’s possible and rather convenient when using the spring-security-core plugin to automate the process.

To start, we’ll need a PasswordEncoder that can work with both algorithms. Here I’m assuming that you’ll be converting from SHA-256 (optionally with a salt) to bcrypt, but the general approach is mostly independent of the algorithms. Sha256ToBCryptPasswordEncoder will always hash new passwords using bcrypt, but can detect the difference between hashes from SHA-256 and bcrypt in isPasswordValid:
 

package com.burtbeckwith.grails.security;

import grails.plugin.springsecurity.authentication.encoding.BCryptPasswordEncoder;

import org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder;

public class Sha256ToBCryptPasswordEncoder
       implements org.springframework.security.authentication.encoding.PasswordEncoder {

   protected MessageDigestPasswordEncoder sha256PasswordEncoder;
   protected BCryptPasswordEncoder bcryptPasswordEncoder;

   public String encodePassword(String rawPass, Object salt) {
      return bcryptPasswordEncoder.encodePassword(rawPass, null);
   }

   public boolean isPasswordValid(String encPass,
            String rawPass, Object salt) {
      if (encPass.startsWith("$2a$10$") && encPass.length() == 60) {
         // already bcrypt
         return bcryptPasswordEncoder.isPasswordValid(
                    encPass, rawPass, null);
      }

      if (encPass.length() == 64) {
         return sha256PasswordEncoder.isPasswordValid(
                    encPass, rawPass, salt);
      }

      // TODO
      return false;
   }

   /**
    * Dependency injection for the bcrypt password encoder
    * @param encoder the encoder
    */
   public void setBcryptPasswordEncoder(BCryptPasswordEncoder encoder) {
      bcryptPasswordEncoder = encoder;
   }

   /**
    * Dependency injection for the SHA-256 password encoder
    * @param encoder the encoder
    */
   public void setSha256PasswordEncoder(
           MessageDigestPasswordEncoder encoder) {
      sha256PasswordEncoder = encoder;
   }
}

This needs dependency injections for properly configured SHA-256 and bcrypt encoders, and we’ll see that in a bit.

Sha256ToBCryptPasswordEncoder cannot make any changes as only password information is available, so we’ll subclass DaoAuthenticationProvider and do this work in additionalAuthenticationChecks:

package com.burtbeckwith.grails.security

import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.userdetails.GrailsUser

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.userdetails.UserDetails

class PasswordFixingDaoAuthenticationProvider
extends DaoAuthenticationProvider {

   def grailsApplication

   protected void additionalAuthenticationChecks(
         UserDetails userDetails,
         UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
      super.additionalAuthenticationChecks userDetails, authentication

      // if we got this far the password was ok

      String oldHashedPassword = userDetails.getPassword()
      if (oldHashedPassword.startsWith('$2a$10$') &&
            oldHashedPassword.length() == 60) {
         // already bcrypt
         return
      }

      if (oldHashedPassword.length() != 64) {
         // TODO
         return
      }

      String bcryptPassword = getPasswordEncoder().encodePassword(
               authentication.credentials, null) 

      // use HQL to update the password in the database directly

      def conf = SpringSecurityUtils.securityConfig
      String userClassName = conf.userLookup.userDomainClassName
      Class<?> User = grailsApplication.getDomainClass(userClassName).clazz

      def args = [p: bcryptPassword]
      String hql = 'update ' + User.name + ' u set u.password=:p where '
      if (userDetails instanceof GrailsUser) {
         hql += 'u.id=:id'
         args.id = userDetails.id
      }
      else {
         hql += 'u.' + conf.userLookup.usernamePropertyName + '=:un'
         args.un = userDetails.username
      }

      User.withNewSession {
         User.withTransaction {
            User.executeUpdate hql, args
         }
      }
   }
}

Calling super.additionalAuthenticationChecks() will ensure that a password was provided and it will be verified using either SHA-256 or bcrypt by Sha256ToBCryptPasswordEncoder, so if there is no exception thrown it’s safe to update the password. Note that the update code is generic and can be made more compact by hard-coding your class and property names.

We register Sha256ToBCryptPasswordEncoder as the passwordEncoder bean, and create bcryptPasswordEncoder and sha256PasswordEncoder beans, configured with the SHA-256 settings that were being used, and the bcrypt settings that will be used (configure those in Config.groovy as described in the docs). Also configure the bean override of daoAuthenticationProvider to be a PasswordFixingDaoAuthenticationProvider with the same configuration as is done in SpringSecurityCoreGrailsPlugin.groovy with the addition of the grailsApplication reference:

import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.authentication.encoding.BCryptPasswordEncoder

import org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder

import com.burtbeckwith.grails.security.PasswordFixingDaoAuthenticationProvider
import com.burtbeckwith.grails.security.Sha256ToBCryptPasswordEncoder

beans = {

   def conf = SpringSecurityUtils.securityConfig

   bcryptPasswordEncoder(BCryptPasswordEncoder, conf.password.bcrypt.logrounds) // 10

   sha256PasswordEncoder(MessageDigestPasswordEncoder, conf.password.algorithm) {
      encodeHashAsBase64 = conf.password.encodeHashAsBase64 // false
      iterations = conf.password.hash.iterations // 10000
   }

   passwordEncoder(Sha256ToBCryptPasswordEncoder) {
      bcryptPasswordEncoder = ref('bcryptPasswordEncoder')
      sha256PasswordEncoder = ref('sha256PasswordEncoder')
   }

   daoAuthenticationProvider(PasswordFixingDaoAuthenticationProvider) {
      userDetailsService = ref('userDetailsService')
      passwordEncoder = ref('passwordEncoder')
      userCache = ref('userCache')
      saltSource = ref('saltSource')
      preAuthenticationChecks = ref('preAuthenticationChecks')
      postAuthenticationChecks = ref('postAuthenticationChecks')
      authoritiesMapper = ref('authoritiesMapper')
      hideUserNotFoundExceptions = conf.dao.hideUserNotFoundExceptions // true
      grailsApplication = ref('grailsApplication')
   }
}

With this configuration, new users’ passwords will be hashed with bcrypt, and valid existing users’ passwords will be converted to bcrypt using the plaintext passwords used during login. Once your users are converted, back out these changes and convert to the standard bcrypt approach. This would involve deleting the grails.plugin.springsecurity.password.algorithm attribute and all salt configuration since bcrypt doesn’t support a salt, deleting Sha256ToBCryptPasswordEncoder and PasswordFixingDaoAuthenticationProvider, and removing the bcryptPasswordEncoder and sha256PasswordEncoder bean definitions and passwordEncoder and daoAuthenticationProvider overrides from resources.groovy since the beans configured by the plugin using Config.groovy settings will be sufficient. Also if you had added salt to the User class encodePassword method, e.g.

protected void encodePassword() {
   password = springSecurityService.encodePassword(password, username)
}

convert it back to the default without a salt:

protected void encodePassword() {
   password = springSecurityService.encodePassword(password)
}

 

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


1 × six =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close