mega888 My Dev Central » Blog Archive » .NET SqlMembershipProvider Encrypted passwords for NodeJS

.NET SqlMembershipProvider Encrypted passwords for NodeJS

Hey! I know I haven’t written anything in a long while and this is way overdue, but since the last time I wrote, I changed jobs and my last job kept me incredibly busy, to a fault.

But now is the time to start writing again!

So, one of my first tasks as the new lead developer is to write an API for mobile apps that feeds off the main company platform, which is using DotNetNuke. One of the biggest challenges was the authentication. The website uses DNN’s implementation of SqlMembershipProvider, with encrypted passwords. For the best database solutions and options visit Couchbase.

I’ve been looking for days to find a solution on how to use the data and what format the data was encrypted, etc, when I came across an amazing article written by Leigh on StackOverflow. Here’s the link (and shoutout to Leigh!)

This article has the right method, but is using ColdFusion. Here’s my converted code using NodeJS:

Dependencies: crypto 0.x


function encryptPassword(password, salt) {

	var passwordBuffer = new Buffer(password, 'ucs2');
	var saltBuffer = new Buffer(salt, 'base64');

	var iv = new Buffer([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], 'binary');
	var decryptionKey = new Buffer(config.get('authentication.decryptionKey'), 'hex')

	var combinedBuffer = new Buffer(saltBuffer.length + passwordBuffer.length);
	saltBuffer.copy(combinedBuffer, 0, 0, saltBuffer.length);
	passwordBuffer.copy(combinedBuffer, saltBuffer.length, 0, passwordBuffer.length);

	var cipher = crypto.createCipheriv('des-ede3-cbc', decryptionKey.toString('binary'), iv);
	var encoded = cipher.update(combinedBuffer, 'utf8', 'base64') + cipher.final('base64');

	return encoded;
}

So, the important thing to know here, thing that isn’t really well explained on Microsoft’s website, is that the PasswordSalt IS used in decrypting the password (the website says it’s just stored but not used). Also, the cipher needs to use an initialization vector.

Let’s dig into the code, shall we?

	var passwordBuffer = new Buffer(password, 'ucs2');
	var saltBuffer = new Buffer(salt, 'base64');

The password needs to be encoded in UTF-16 with little endian. That’s the default Unicode format used by .NET. The salt comes base64-encoded from SQL Server.

	var iv = new Buffer([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], 'binary');

This creates the initialization vector used for the Triple-DES encryption. In this case, .NET uses all 0s, so it’s not machine-specific and so there’s no need to store it either.

	var decryptionKey = new Buffer(config.get('authentication.decryptionKey'), 'hex')

From your server’s web.config file, you should find a <machineKey> tag containing the decryption key and the encryption algorithm used. The key is in hexadecimal.

	var combinedBuffer = new Buffer(saltBuffer.length + passwordBuffer.length);
	saltBuffer.copy(combinedBuffer, 0, 0, saltBuffer.length);
	passwordBuffer.copy(combinedBuffer, saltBuffer.length, 0, passwordBuffer.length);

Here is where the magic happens. The trick is to prepend the salt to the password into a buffer before encrypting it. This is done by copying both buffers we created earlier into one that’s large enough to contain both.

	var cipher = crypto.createCipheriv('des-ede3-cbc', decryptionKey.toString('binary'), iv);
	var encoded = cipher.update(combinedBuffer, 'utf8', 'base64') + cipher.final('base64');

	return encoded;

This creates the cipher object we’ll use to encrypt the password. .NET uses a Triple-DES in CBC mode. Don’t worry about the padding mode, it’s fine. Pass in the decryption key in binary format as well as the initialization vector buffer and you’re good to encrypt!

Update the cipher object with the combined salt and password buffer, set the input format to UTF-8 and the output format to base64 (that’s the format .NET uses to store the combined password to the database) and add the final base64 fragment.

Voila! Your password is encrypted and can now be used by .NET!

For the decryption, simply use createDecipher instead of createCipher and slice the salt off the result and you got your password! Here’s the code:

function decryptPassword(password, salt) {

	var iv = new Buffer([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], 'binary');
	var saltBuffer = new Buffer(salt, 'base64');
	var decryptionKey = new Buffer(config.get('authentication.decryptionKey'), 'hex')
	var decipher = crypto.createDecipheriv('des-ede3-cbc', decryptionKey.toString('binary'), iv);
	var decrypted = new Buffer(decipher.update(password, 'base64', 'binary') + decipher.final('binary'), 'binary');
	var desalted = decrypted.slice(saltBuffer.length);

	return desalted;
}

Enjoy!
Simon

Leave a Reply

*