[ Technical ] Epicor Password Hash - SHA256

From a high-level Epicor has a little helper class to hash the password via 2 helper methods ComputeHash, VerifyHash it does support different algorithm’s such as SHA1, SHA256, SHA384, SHA512, MD5 but by default Epicor uses SHA256.

That helper lies in Epicor.System.dll usually stored in the bin folder on the IIS App Server.

High Level ComputeHash:

  1. Using RNGCryptoServiceProvider creates a random number salt as bytes
  2. Converts your plain-text password string into bytes
  3. Creates a buffer with the size of passwordBytes.Length + saltBytes.Length
  4. Combines the passwordBytes and saltBytes into a single buffer array
  5. Hash the buffer using SHA256 Managed
  6. Do some magic via a for loop
  7. Encode Base64

What you need to know is that the Random Salt is embedded in the end-result Hash and is used by the VerifyHash method.

If you were to call ComputeHash a thousand times with the same password string you would get a different Hash always.

| Password | Computed Epicor Hash w/ RNG Salt |
|----------|:-------------:|------:expressionless:
| manager | 46uIY6/nQjHL5mX1KeE/7NtEXD3MIOblGxpVRH5ZWXNORGsNwT3WHg== |
| manager | JIzKviG6ZG5rE52KOeKEg4AvPnU72FOUQ0y7y/R8h8GZNQaV2G+GHw== |
| manager | tjk/TfHesmjnhq7NcUbCkfQKJnqfV7Q2mJRG6hwYRD8xGIofA6tGIQ== |

Example:

// Returns: 46uIY6/nQjHL5mX1KeE/7NtEXD3MIOblGxpVRH5ZWXNORGsNwT3WHg== for example.
Epicor.Security.Cryptography.SHA.Hasher.ComputeHash("manager");

High Level VerifyHash (returns boolean if hash matched):

  1. Decode Base64
  2. Extract salt bytes from Hash
  3. Using the same salt bytes and user provided plain-text password we re-create the Hash, only this time because we pass in a salt array ComputerHash won’t create a RNG Salt it will use the one passed.
  4. Compares the newly created Hash (base64) to Hash in Database (previously generated). They should Match

Example:

Epicor.Security.Cryptography.SHA.Hasher.VerifyHash("manager", "46uIY6/nQjHL5mX1KeE/7NtEXD3MIOblGxpVRH5ZWXNORGsNwT3WHg==");

You can write alot more details on this process. But things to know that a Hash Computed on My PC would work just as fine on your machine, it does not make use of the DPAPI, hence when you copy your Database to another server, it all works just fine.

For those curious what how the Salt is created, since it’s not a hard-coded “MfgSys” salt in E10.1+

// Fills an array of bytes with a cryptographically strong sequence of random nonzero values
byte[8] saltBytes = new byte[8];
new RNGCryptoServiceProvider().GetNonZeroBytes(saltBytes)

You can with some effort, look up the Hashing Algorithm and re-create it easily in Visual Studio for your custom needs.

For Reference.

1 Like

0

Full Example, Creating your own Epicor Hashing Method:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create a New Hash with password manager
            Console.WriteLine(ComputeHash("manager"));

            // Lets Validate all these possible hashes against Password manager (one-way hash)
            Console.WriteLine(VerifyHash("manager", "46uIY6/nQjHL5mX1KeE/7NtEXD3MIOblGxpVRH5ZWXNORGsNwT3WHg=="));
            Console.WriteLine(VerifyHash("manager", "JIzKviG6ZG5rE52KOeKEg4AvPnU72FOUQ0y7y/R8h8GZNQaV2G+GHw=="));
            Console.WriteLine(VerifyHash("manager", "tjk/TfHesmjnhq7NcUbCkfQKJnqfV7Q2mJRG6hwYRD8xGIofA6tGIQ=="));
            Console.WriteLine(VerifyHash("WRONG_PASSWORD", "tjk/TfHesmjnhq7NcUbCkfQKJnqfV7Q2mJRG6hwYRD8xGIofA6tGIQ=="));
            Console.ReadLine();
        }

        public static string ComputeHash(string password, byte[] saltBytes = null)
        {
            HashAlgorithm algorithm = new SHA256Managed();

            if (saltBytes == null)
            {
                saltBytes = new byte[8];
                new RNGCryptoServiceProvider().GetNonZeroBytes(saltBytes);
            }

            byte[] bytes = Encoding.UTF8.GetBytes(password);
            byte[] buffer = new byte[bytes.Length + saltBytes.Length];
            for (int i = 0; i < bytes.Length; i++)
                buffer[i] = bytes[i];

            for (int j = 0; j < saltBytes.Length; j++)
                buffer[bytes.Length + j] = saltBytes[j];

            byte[] buffer2 = algorithm.ComputeHash(buffer);
            byte[] combinedArr = new byte[buffer2.Length + saltBytes.Length];
            for (int k = 0; k < buffer2.Length; k++)
                combinedArr[k] = buffer2[k];

            for (int m = 0; m < saltBytes.Length; m++)
                combinedArr[buffer2.Length + m] = saltBytes[m];

            return Convert.ToBase64String(combinedArr);
        }

        public static bool VerifyHash(string password, string hash)
        {
            byte[] saltBytes = new byte[8];
            byte[] buffer = Convert.FromBase64String(hash);
            int x = 0x100 / 8; // SHA256

            if (buffer.Length < x)
                return false;

            for (int i = 0; i < saltBytes.Length; i++)
                saltBytes[i] = buffer[x + i];

            return (hash == ComputeHash(password, saltBytes));
        }
    }
}

You could modify the code and remove the RNG Salt and pass in your own salt. Would work as well, only then your Hash would always be the same. Perhaps if you are re-creating this in a VBScript for some Database Copy Script, its easier to re-create.

2 Likes

@josecgomez maybe you want to move this one to the Experts Corner. Special Kudos to you for pointing me into the right direction. (y)

How did I miss this? Very nice Haso!

1 Like

I had forgotten about this article. A few minor tweaks with fips compliance mode that came in 300. I’ll try to update this week while traveling…

3 Likes

You can decrypt the SysAgent Password and possibly the SMTP Password to make use of it in your Customizations… via…

Epicor.Security.Cryptography.Encryptor.DecryptToString(encPasswordStringHere)
2 Likes

Thanks hasokeric. This is working fine.

Hi is that possible or any method that able to read and verify the user password by using DecryptToString, I have the project which need user to input their own password to verify the ownership process.

If you are inside the Epicor Client, within a Customization - yes.

If not, then you can enter their password and there is a Verify Method.

Epicor.Security.Cryptography.SHA.Hasher.VerifyHash("passwordhere", "getthehashfromthesession");

Ex:

Epicor.Security.Cryptography.SHA.Hasher.VerifyHash("mypassw0rd", "46uIY6/nQjHL5mX1KeE/7NtEXD3MIOblGxpVRH5ZWXNORGsNwT3WHg==");

Are you trying to do this within a Customization?

Hi hasokeric, is this what you mean as following by input the password example as manager then match again the code behind?

Epicor.Security.Cryptography.SHA.Hasher.VerifyHash("manager", "46uIY6/nQjHL5mX1KeE/7NtEXD3MIOblGxpVRH5ZWXNORGsNwT3WHg==");
1 Like