My goal is to use NodeJS to create LDAP password hashes that are similar to what comes out of the slappasswd
command-line tool.
Here’s how LDAP passwords can be produced with command-line:
slappasswd -h '{SSHA}' -s 'P@ssw0rd' {SSHA}1RHPt8m4AWLjK8Px1MT6FEBJOBJpdzqT
The result is a base64 encoded, salted SHA1 password.
Here’s what I tried initially to recreate it:
#!/usr/bin/env node import sha1 from 'crypto-js/sha1.js'; let password = 'P@ssW0rd'; let salt = btoa(0xA5); // Not random, just a proof of concept temporary value. let hash = sha1(password + salt); console.log('{SSHA}' + btoa(hash));
But, I got a much longer string than what the slappasswd
command produced and I’m not sure why.
{SSHA}NDVkN2JjODQ2ZDk3Yjc2YmViNTU3MzUzYjBiNzExN2ZmYzMxYWY5ZA==
I did some digging around on the net and found this on an LDAP password generator web page:
<script src="lib/cryptojs/core.js"></script> <script src="lib/cryptojs/sha1.js"></script> <script src="lib/cryptojs/enc-base64.js"></script> <script> function slappasswd(password) { var salt = CryptoJS.lib.WordArray.random(128/8).toString().substr(0,4); var hash = CryptoJS.SHA1(password + salt); var base = CryptoJS.enc.Latin1.parse(hash.toString(CryptoJS.enc.Latin1) + salt).toString(CryptoJS.enc.Base64); return '{SSHA}' + base; } ...
The web page produces a string that is the same length as what comes out of slappasswd
, so I assume it’s an accurate recreation of the slappasswd
logic.
Using this information, my next attempt looks like this:
#!/usr/bin/env node import * as CryptoJS from 'crypto-js'; let password = 'P@ssW0rd'; let salt = CryptoJS.lib.WordArray.random(128/8).toString().substr(0,4); let hash = sha1(password + salt); let base = CryptoJS.enc.Latin1.parse(hash.toString(CryptoJS.enc.Latin1) + salt).toString(CryptoJS.enc.Base64); console.log('{SSHA}' + base);
However, I get errors.
First, there is TypeError: Cannot read properties of undefined (reading 'WordArray')
If I replace let salt =
with let salt = btoa(0xA5)
from my first attempt code, I then get the error: ReferenceError: sha1 is not defined
My feeling is that I’ve got the import wrong somehow.
I’m trying to do the ES6 module equivalent of var CryptoJS = require("crypto-js");
, but failing somewhere.
So my question is two-fold:
- Can my first attempt be made to produce a string length similar to what slappassword outputs?
- If not, what can I do to fix the errors I’m getting in the second attempt?
Ideally, I’d like to understand where I went wrong in my first attempt rather than simply copying and pasting someone else’s code (second attempt) without fully grasping it.
Advertisement
Answer
Here is alternative of python/php implementations for NodeJS.
Import Crypto module
const crypto = require('crypto');
It will be used to create LDAP password hashes (SSHA)
function generate_hash(passwd, salt) { if (!salt) { const buf = crypto.randomBytes(4); salt = buf.toString('base64'); } let ctx = crypto.createHash('sha1'); ctx.update(passwd, 'utf-8'); ctx.update(salt, 'binary'); let digest = ctx.digest('binary'); return '{SSHA}' + Buffer.from(digest + salt, 'binary').toString('base64'); }
It will be used to verify hash
function verify_hash(passwd, hash) { let bhash = Buffer.from(hash.substr(6), 'base64'); let salt = bhash.toString('binary', 20); let newssha = generate_hash(passwd, salt); return hash === newssha; }
Test it together
const hash = generate_hash("qwe1234"); let test = verify_hash("qwe1234", hash); console.log(test); //Output: true let test = verify_hash("XXXX", hash); console.log(test); //Output: false
Hope it help you. Please let me know.