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.