6

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.

Shawn Holmes
  • 3,752
  • 22
  • 25
czuroski
  • 4,316
  • 9
  • 50
  • 86

5 Answers5

12

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);
Leigh
  • 28,765
  • 10
  • 55
  • 103
Seibar
  • 68,705
  • 38
  • 88
  • 99
  • 1
    This C# port of the Railo code is the actual working algorithm that maps to CF's Encrypt/Decrypt using CFMX_COMPAT. Other XOR-based algorithms supplied (while correct in their own context) are incompatible with ColdFusion's Encrypt/Decrypt. This is the version you will want to use. – Shawn Holmes May 25 '12 at 15:58
  • And how looks your "HexToBytes" methode? – Wekerle Tibor Aug 03 '18 at 06:03
3

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

mwcz
  • 8,949
  • 10
  • 42
  • 63
2

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))#
Adam Tuttle
  • 19,505
  • 17
  • 80
  • 113
  • Thanks - I have tried this but it still isn't working. Actually, the key that I have is actually a string - not an int, so I don't know if that is throwing it off, or if I have to use the key for the base-64 encoding. to base-64 encode it, am I able to simply use Convert.ToBase64String, or not? – czuroski Apr 28 '10 at 11:31
  • According to the [docs](http://goo.gl/zGbJ), the string passed in is used as a random seed to generate the integer key value. Unfortunately, I don't have any more information on exactly how it gets that value. If I were in your shoes, I might consider (1) removing the encryption from existing passwords (simple enough with a one-time script), and (2) rewriting the existing CF code to no longer use encryption. Hashing is sufficient, and encryption is wasting cpu in this case, in my opinion. – Adam Tuttle Apr 28 '10 at 17:22
  • yeah - I agree that it is wasting cpu, but this is a suppoted application and I am not able to change that right now. So I have to attempt to decrypt it myself, but have been having no luck. – czuroski Apr 28 '10 at 19:25
  • 2
    If you really have to do this, take a look at the Railo source code (and licensing info). It has CFMX_COMPAT algorithm you may be able to port to C#. – Leigh Apr 29 '10 at 06:09
  • 2
    For anyone reading, I emailed with czuroski offline and we did get a C# port of the Railo classes working. – Leigh May 18 '10 at 21:37
  • As Leigh stated above, he was able to help me get a port of the Railo classes working successfully. – czuroski May 28 '10 at 13:16
1

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

intnick
  • 331
  • 1
  • 3
  • yeah - that would be easier. unfortunately, I didn't write the initial application, and don't want to change it. – czuroski Apr 26 '10 at 15:20
1

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.

Edward M Smith
  • 10,627
  • 2
  • 46
  • 50
  • I guess it all boils down to the value of the variable `EnCode`. – mwcz Apr 26 '10 at 17:14
  • Right, that's just the key. I'm sure the OP has access to it in his CF source code. – Edward M Smith Apr 26 '10 at 17:46
  • I can't seem to get this to match up. I tried narrowing it down on a test CF page to just do the encrypt (and ignore the hash), but I still can't get the encrypted CF value to match up to my encrypted c# value. I used a straight base64 encrypt in my c# app. I saw somewhere that CF also performs a uuencode as part of the encryption - is there any truth to this? – czuroski Apr 27 '10 at 13:17
  • You're doing the XOR in the C#, and not getting the same result? – Edward M Smith Apr 27 '10 at 19:59