typescriptspringcryptographysha256

How, exactly, does Spring's PasswordEncoder apply salt to the plaintext?


I have looked at the docs, and I don't know that it's specified. I'm not sure how to dive into the source code to learn this either. That's a viable solution.

I'm in the unfortunate position of migrating a small-medium database of username/password data from Spring framework to Remix.js.

The hard part appears to be getting my hashing algorithms to salt passwords correctly. I'm about to start doing some annoying permutations so just thought I'd ask.

Here's how the old hashes are being generated: https://github.com/Parrit/Parrit/blob/master/src/main/java/com/parrit/controllers/ProjectController.java#L68

and here's an example of what one looks like. Plaintext, this should be password

{sha256}{1htkE/1MXKL7uqfqhOC2SI39YzX2lEsd96BqJCHTUCs=}9f62dbe07df8ac7f049cdb1ae1291b02f2d1ea645c7f4df9a1235e93a0f213bd

My understanding is that this is {alg}{salt}hash

okay great, but when I try to compute a hash in JavaScript I get a mismatch

const compare_sha256 = (attempt: string, info: PasswordInfo): boolean => {
  let attemptHash;
  if (info.salt) {
    const saltedAttempt = attempt + info.salt;
    console.log("saltedAttempt", saltedAttempt);
    attemptHash = createHash("sha256").update(saltedAttempt).digest("hex");
  } else {
    attemptHash = createHash("sha256").update(attempt).digest("hex");
  }
  console.log({ attemptHash, actuallHash: info.hash });
  return attemptHash === info.hash;
};

log

saltedAttempt password1htkE/1MXKL7uqfqhOC2SI39YzX2lEsd96BqJCHTUCs=
{
  attemptHash: 'ae192cbdfa2abf7b82bfdeec0168cc0cd7fd359ed49d7494daa88046ef025599',
  actuallHash: '9f62dbe07df8ac7f049cdb1ae1291b02f2d1ea645c7f4df9a1235e93a0f213bd'
}

I figure there's got to be some separation character between the plaintext and salt. If I'm missing something please point that out to me.


Solution

  • The hash can be reproduced with the MessageDigestPasswordEncoder. Password and salt are concatenated in this order. The salt must be used including the curly brackets and base64 encoded:

    var crypto = require('crypto')
    var hash = crypto.createHash('sha256')
        .update('password')
        .update('{1htkE/1MXKL7uqfqhOC2SI39YzX2lEsd96BqJCHTUCs=}')
        .digest('hex')
    console.log(hash) // 9f62dbe07df8ac7f049cdb1ae1291b02f2d1ea645c7f4df9a1235e93a0f213bd