I needed a way to verify that the OpenLDAP server had the correct hash recorded. That is, a SSHA Hash Generator that I could run off the command line was in order. After fiddling through it, I thought it would be worth documenting in a blog post.
We need to find the salt (the last four bytes of the hash), and then concatenate PASSWORD+SALT, take the SHA hash, convert to base64, prepend {SSHA}, and then finally base64 encode that whole string again.
Here is what ldapsearch says is the password hash:
userPassword:: e1NTSEF9OWZ1MHZick15Tmt6MnE3UGh6eW94SDMrNmhWb3llajU=
The first step is to retrieve the (binary) salt which is stored at the end of a double base64 encoded hash. I wrote a perl SSHA Salt Extractor for that:
$ cat getSalt.pl #!/usr/bin/perl -w my $hash=$ARGV[0]; # The hash is encoded as base64 twice: use MIME::Base64; $hash = decode_base64($hash); $hash=~s/{SSHA}//; $hash = decode_base64($hash); # The salt length is four (the last four bytes). $salt = substr($hash, -4); # Split the salt into an array. my @bytes = split(//,$salt); # Convert each byte from binary to a human readable hexadecimal number. foreach my $byte (@bytes) { $byte = uc(unpack "H*", $byte); print "$byte"; } $ ./getSalt.pl e1NTSEF9OWZ1MHZick15Tmt6MnE3UGh6eW94SDMrNmhWb3llajU= 68C9E8F9
See, that is four ASCII characters represented by human readable hexadecimal. Another way to do this is like this:
echo "e1NTSEF9OWZ1MHZick15Tmt6MnE3UGh6eW94SDMrNmhWb3llajU=" | base64 -d | sed 's/^{SSHA}//' | base64 -d | tail -c4 | od -tx1
Now that we have extracted the salt, we can regenerate the salted hash with what we think the password should be.
$ cat openldap-sha.pl #!/usr/bin/perl use Digest::SHA1; use MIME::Base64; my $p=$ARGV[0]; my $s=$ARGV[1]; # Convert from hex to binary. $s_bin=pack 'H*', $s; # Hash the password with the salt. $digest = Digest::SHA1->new; $digest->add("$p"); $digest->add("$s_bin"); # Encode PASSWORD+SALT as Base64. $hashedPasswd = '{SSHA}' . encode_base64($digest->digest . "$s_bin" ,''); # Encode as Base64 again: print 'userPassword:: ' . encode_base64($hashedPasswd) . "\n"; $ ./openldap-sha.pl correcthorsebatterystaple 68C9E8F9 userPassword:: e1NTSEF9OWZ1MHZick15Tmt6MnE3UGh6eW94SDMrNmhWb3llajU=
And there you have it.