Is there any way to authenticate users from other web apps using the DNN logins?
We have a main site that is using DNN and user logins are stored in the asp net mem
Most likely the password is not encrypted, it is hashed. Hashing is different from encrypting, because it is not reversible.
You would not use ColdFusion's encrypt() function for this, you would use its hash() function.
So the questions you'll need to answer to figure out how to hash the passwords in CF to be able to auth against the DNN users are:
All of those questions must be answered to determine how CF must use the hash() function in combination with the salt and user-submitted passwords.
I'll make some assumptions to provide an answer.
If we assume that noiteration is being done and that the salt is simply being appended to the password prior to using SHA1 to hash the password, then you'd be able to reproduce the hash digest like this:
<cfset hashDigest = hash(FORM.usersubmittedPassword & saltFromDB, "SHA1") />
(Edit: Original answer did not work in all cases. Substantially revised ...)
From what I have read, DNN uses an "SHA1" hash by default. The thread @barnyr posted shows it simply hashes the concatenated salt and password, but with a few twists.
hash()
does. (See demo below)Given that CF9's Hash function does not accept binary (supported in CF11), I do not think it is possible to duplicate the results with native CF functions alone. Instead I would suggest decoding the strings into binary, then using java directly:
Code:
<cfscript>
thePassword = "DT!@12";
base64Salt = "+muo6gAmjvvyy5doTdjyaA==";
// extract bytes of the salt and password
saltBytes = binaryDecode(base64Salt, "base64");
passBytes = charsetDecode(thePassword, "UTF-16LE" );
// next combine the bytes. note, the returned arrays are immutable,
// so we cannot use the standard CF tricks to merge them
ArrayUtils = createObject("java", "org.apache.commons.lang.ArrayUtils");
dataBytes = ArrayUtils.addAll( saltBytes, passBytes );
// hash binary using java
MessageDigest = createObject("java", "java.security.MessageDigest").getInstance("SHA-1");
MessageDigest.update(dataBytes);
theBase64Hash = binaryEncode(MessageDigest.digest(), "base64");
WriteOutput("theBase64Hash= "& theBase64Hash &"<br/>");
</cfscript>
Demo of Differences:
<cfscript>
theEncoding = "UTF-16LE";
thePassword = "DT!@12";
base64Salt = "+muo6gAmjvvyy5doTdjyaA==";
// extract the bytes SEPARATELY
saltBytes = binaryDecode(base64Salt, "base64");
passBytes = charsetDecode(thePassword, theEncoding );
ArrayUtils = createObject("java", "org.apache.commons.lang.ArrayUtils");
separateBytes = ArrayUtils.addAll( saltBytes, passBytes );
// concatenate first, THEN extract the bytes
theSalt = charsetEncode( binaryDecode(base64Salt, "base64"), theEncoding );
concatenatedBytes = charsetDecode( theSalt & thePassword, theEncoding );
// these are the raw bytes BEFORE hashing
WriteOutput("separateBytes= "& arrayToList(separateBytes, "|") &"<br>");
WriteOutput("concatenatedBytes"& arrayToList(concatenatedBytes, "|") );
</cfscript>
Results:
separateBytes = -6|107|-88|-22|0|38|-114|-5|-14|-53|-105|104|77|-40|-14|104|68|0|84|0|33|0|64|0|49|0|50|0
concatenatedBytes = -6|107|-88|-22|0|38|-114|-5|-14|-53|-105|104|-3|-1|68|0|84|0|33|0|64|0|49|0|50|0
(Posting a new response to keep the "encrypted" process separate from "hashing")
For "encrypted" keys, the DNN side uses the standard algorithms ie DES, 3DES or AES - depending on your machineKey settings. But with a few differences you need to match in your CF code. Without knowing your actual settings, I will assume you are using the default 3DES
for now.
Data To Encrypt
The encrypted value is a combination of the salt and password. But as with hashing, DNN uses UTF-16LE. Unfortunately, ColdFusion's Encrypt()
function always assumes UTF-8, which will produce a very different result. So you need to use the EncryptBinary function instead.
// sample valus
plainPassword = "password12345";
base64Salt = "x7le6CBSEvsFeqklvLbMUw==";
hexDecryptKey = "303132333435363738393031323334353637383930313233";
// first extract the bytes of the salt and password
saltBytes = binaryDecode(base64Salt, "base64");
passBytes = charsetDecode(plainPassword, "UTF-16LE" );
// next combine the bytes. note, the returned arrays are immutable,
// so we cannot use the standard CF tricks to merge them
ArrayUtils = createObject("java", "org.apache.commons.lang.ArrayUtils");
dataBytes = ArrayUtils.addAll( saltBytes, passBytes );
Encryption Algorithm
With block ciphers, ColdFusion defaults to ECB mode. (See Strong Encryption in ColdFusion) Whereas .NET defaults to CBC mode, which requires an additional IV value. So you must adjust your CF code to match.
// convert DNN hex key to base64 for ColdFusion
base64Key = binaryEncode(binaryDecode( hexDecryptKey, "hex"), "base64");
// create an IV and intialize it with all zeroes
// block size: 16 => AES, 8=> DES or TripleDES
blockSize = 8;
iv = javacast("byte[]", listToArray(repeatString("0,", blocksize)));
// encrypt using CBC mode
bytes = encryptBinary(dataBytes, base64Key, "DESede/CBC/PKCS5Padding", iv);
// result: WBAnoV+7cLVI95LwVQhtysHb5/pjqVG35nP5Zdu7T/Cn94Sd8v1Vk9zpjQSFGSkv
WriteOutput("encrypted password="& binaryEncode( bytes, "base64" ));