3

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 membership table. From what I have been reading, the passwords are encrypted using the machine key and then salted. I see where this info is, but can't seem to encrypt passwords correctly using this method.

I'm trying with a Coldfusion web application on the same server where our DNN site is, but it doesn't want to work. You'd think it would be strait forward with the ColdFusion encryption function:

    Encrypt(passwordstring, key [, algorithm, encoding, IVorSalt, iterations])

No matter what I try, I never get a matching value.

Any help, insight or pointing me in the right direction would be greatly appreciated!

Leigh
  • 28,765
  • 10
  • 55
  • 103
Macncheese
  • 31
  • 1
  • 1
    Have you seen the code here: http://stackoverflow.com/a/2551717/826714 - looks like it may help you confirm the CF version of the same function – barnyr Sep 21 '12 at 08:51

3 Answers3

3

(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.

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 


Community
  • 1
  • 1
Leigh
  • 28,765
  • 10
  • 55
  • 103
  • I just found out that the setup we have is using Encrypted password format, not hashed. Although, one thing I did find was that the SHA-256 produces the correct encrypted length, but still not the end result it should be. – Macncheese Sep 22 '12 at 04:38
  • Do you mean `HMAC256`? But what *kind* of encryption, AES, 3DES, ... ? The supported types seem to [vary by version](http://msdn.microsoft.com/en-us/library/w8h3skw9%28v=vs.85%29.aspx) What are your `` settings? (Obviously sanitize any encryption keys.) – Leigh Sep 22 '12 at 14:46
  • Machine key settings are SHA1 and 3DES – Macncheese Sep 27 '12 at 03:47
2

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:

  1. What algorithm is DNN using to hash the passwords?
  2. How is the salt being used with the password prior to hashing?
  3. Is DNN iterating over the hash X number of times to improve security?

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") />
Jason Dean
  • 9,585
  • 27
  • 36
1

(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" ));
Leigh
  • 28,765
  • 10
  • 55
  • 103
  • Note, if your version does not support `ArrayUtils.addAll` just merge the arrays manually like shown at the bottom of [this post](http://stackoverflow.com/a/10760835/104223) – Leigh Sep 25 '12 at 15:02
  • Um, wow! Thank you Leigh! That code you posted worked perfect! This is a life saver! Thank you thank you thank you! – Macncheese Sep 27 '12 at 03:57
  • You are very welcome. This was a fun question :) (I like interop problems) – Leigh Sep 27 '12 at 13:36
  • 1
    Ha! I was finally able to re-create this for NodeJS. Thank you so much for your code! :) – Simon Germain Apr 17 '15 at 14:29