Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compare password hashes between C# and ColdFusion (CFMX_COMPAT)

I have a password hash that is stored in a table and is put there by the following coldfusion script-

#Hash(Encrypt(Form.UserPassword,GetSiteVars.EnCode))#

I am trying to add some outside functionality within a c# application. I would like to be able to take advantage of the data that already exists so that I can authenticate users. Does anyone know how I can replicate the above coldfusion code in c#?

Thanks for any thoughts.

like image 223
czuroski Avatar asked Apr 26 '10 14:04

czuroski


5 Answers

I looked through the Railo code as someone else here mentioned in comments.

Following is CFMX_Compat ported to C# from the Railo Java source. See below for an example of usage.

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;

namespace RailoUtil
{
    // SOURCE: Railo Source Code License LGPL v2
    // http://wiki.getrailo.org/wiki/RailoLicense
    public class RailoCFMXCompat
    {
        private String m_Key;
        private int m_LFSR_A = 0x13579bdf;
        private int m_LFSR_B = 0x2468ace0;
        private int m_LFSR_C = unchecked((int)0xfdb97531);
        private int m_Mask_A = unchecked((int)0x80000062);
        private int m_Mask_B = 0x40000020;
        private int m_Mask_C = 0x10000002;
        private int m_Rot0_A = 0x7fffffff;
        private int m_Rot0_B = 0x3fffffff;
        private int m_Rot0_C = 0xfffffff;
        private int m_Rot1_A = unchecked((int)0x80000000);
        private int m_Rot1_B = unchecked((int)0xc0000000);
        private int m_Rot1_C = unchecked((int)0xf0000000);

        public byte[] transformString(String key, byte[] inBytes)
        {
            setKey(key);
            int length = inBytes.Length;
            byte[] outBytes = new byte[length];
            for (int i = 0; i < length; i++)
            {
                outBytes[i] = transformByte(inBytes[i]);
            }
            return outBytes;
        }

        private byte transformByte(byte target)
        {
            byte crypto = 0;
            int b = m_LFSR_B & 1;
            int c = m_LFSR_C & 1;
            for (int i = 0; i < 8; i++)
            {
                if (0 != (m_LFSR_A & 1))
                {
                    m_LFSR_A = m_LFSR_A ^ m_Mask_A >> 1 | m_Rot1_A;
                    if (0 != (m_LFSR_B & 1))
                    {
                        m_LFSR_B = m_LFSR_B ^ m_Mask_B >> 1 | m_Rot1_B;
                        b = 1;
                    }
                    else
                    {
                        m_LFSR_B = m_LFSR_B >> 1 & m_Rot0_B;
                        b = 0;
                    }
                }
                else
                {
                    m_LFSR_A = (m_LFSR_A >> 1) & m_Rot0_A;
                    if (0 != (m_LFSR_C & 1))
                    {
                        m_LFSR_C = m_LFSR_C ^ m_Mask_C >> 1 | m_Rot1_C;
                        c = 1;
                    }
                    else
                    {
                        m_LFSR_C = m_LFSR_C >> 1 & m_Rot0_C;
                        c = 0;
                    }
                }

                crypto = (byte)(crypto << 1 | b ^ c);
            }

            target ^= crypto;
            return target;
        }

        private void setKey(String key)
        {
            int i = 0;
            m_Key = key;
            if (String.IsNullOrEmpty(key)) key = "Default Seed";
            char[] Seed = new char[key.Length >= 12 ? key.Length : 12];
            Array.Copy(m_Key.ToCharArray(), Seed, m_Key.Length);
            int originalLength = m_Key.Length;
            for (i = 0; originalLength + i < 12; i++)
                Seed[originalLength + i] = Seed[i];

            for (i = 0; i < 4; i++)
            {
                m_LFSR_A = (m_LFSR_A <<= 8) | Seed[i + 4];
                m_LFSR_B = (m_LFSR_B <<= 8) | Seed[i + 4];
                m_LFSR_C = (m_LFSR_C <<= 8) | Seed[i + 4];
            }
            if (0 == m_LFSR_A) m_LFSR_A = 0x13579bdf;
            if (0 == m_LFSR_B) m_LFSR_B = 0x2468ace0;
            if (0 == m_LFSR_C) m_LFSR_C = unchecked((int)0xfdb97531);
        }
    }
}

Here is a usage example that Hex-encodes the encrypted text, and then decrypts the same thing.

RailoCFMXCompat cfmx = new RailoCFMXCompat();
UTF8Encoding encoding = new UTF8Encoding();

//encrypt my string
byte[] encrypted = cfmx.transformString("mySecretKey", encoding.GetBytes("clear text"));
string encryptedHex = BitConverter.ToString(encrypted); //72-07-AA-1B-89-CB-01-96-4F-51

//decrypt my string
byte[] encryptedBytes = HexToBytes("72-07-AA-1B-89-CB-01-96-4F-51");
byte[] decrypted = cfmx.transformString("mySecretKey", encryptedBytes);
string cleartext = encoding.GetString(decrypted);
like image 131
Seibar Avatar answered Oct 22 '22 06:10

Seibar


MD5 is the default hashing algorithm for the hash(). I'm not a C# programmer, but it shouldn't be too hard to create an MD5 hash to compare to your ColdFusion result.

As for encrypt(), is there a reason you're encrypting the username before hashing it? I can't think of any benefit to doing this, but that doesn't mean there isn't one. I would simply do:

Hash( UCase( GetPass.username ) )

Which should be easier to replicate in C#.

like image 21
mwcz Avatar answered Oct 22 '22 08:10

mwcz


I'll leave the original answer content below for historical reference, but it should be noted that this is NOT a working answer to the original question.

Instead, see the top-voted answer in this thread, by @Terrapin in January 2011. I hope the OP sees this and can change the accepted answer. Heck, I'll even flag the mods to see if anything can be done about this.


To build on the answer by Edward Smith, and the follow-up comments by czuroski, here is my solution.

First, you need an XOR function in C#, which I've taken from here and modified slightly.

using System;
using System.Collections.Generic;
using System.Text;

namespace SimpleXOREncryption
{    
    public static class EncryptorDecryptor
    {
        public static string EncryptDecrypt(string textToEncrypt, int key)
        {            
            StringBuilder inSb = new StringBuilder(textToEncrypt);
            StringBuilder outSb = new StringBuilder(textToEncrypt.Length);
            char c;
            for (int i = 0; i < textToEncrypt.Length; i++)
            {
                c = inSb[i];
                c = (char)(c ^ key);
                outSb.Append(c);
            }
            return outSb.ToString();
        }   
    }
}

Then, take the result of the XOR and base-64 encode it. After you have that string, MD5 hash it. The result should match the result from the original code snippet:

#Hash(Encrypt(Form.UserPassword,GetSiteVars.EnCode))#
like image 33
Adam Tuttle Avatar answered Oct 22 '22 07:10

Adam Tuttle


One of the solutions would be to have the DB do the hashing and encription, could be easier...

like image 1
intnick Avatar answered Oct 22 '22 06:10

intnick


The default "encryption" in CF is simply an XOR:

ciphertext = base64_encode(plaintext ^ key)

So, to decrypt:

plaintext = base64_decode(ciphertext) ^ key

The default hash, as mentioned, is md5.

Edit:

Well, further research shows this is not true - just one of those pervasive myths.

I can't find any documentation of the actual algorithm for the CFMX_COMPAT encryption method.

Sorry about the wild goose chase.

like image 1
Edward M Smith Avatar answered Oct 22 '22 06:10

Edward M Smith