0
\$\begingroup\$

We had a string with byte array (hexadecimal) sequence, like: "0x65,0x31,0xb6,0x9e,0xaf,0xd2,0x39,0xc9,0xad,0x07,0x78,0x99,0x73,0x52,0x91,0xf5,0x93,0x1a,0x49,0xc6" and we need to recovery this sequence to byte array again.

We are using the following approach:

string byteSequence = "0x65,0x31,0xb6,0x9e,0xaf,0xd2,0x39,0xc9,0xad,0x07,0x78,0x99,0x73,0x52,0x91,0xf5,0x93,0x1a,0x49,0xc6";

byte[] myBytes = stringByteSequence.Split(',').Select(s => Convert.ToByte(s, 16)).ToArray();

This hash sequence it been generated by this sample:

string password = "<your password here>";

using (var cryptoProvider = System.Security.Cryptography.SHA1.Create())
{
    byte[] passwordHash = cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(password));
    string result = "new byte[] { " + 
        String.Join(",", passwordHash.Select(x => "0x" + x.ToString("x2")).ToArray())
         + " } ";
  
    //...
    // result = "new byte[] { 0x65,0x31,0xb6,0x9e,0xaf,0xd2,0x39,0xc9,0xad,0x07,0x78,0x99,0x73,0x52,0x91,0xf5,0x93,0x1a,0x49,0xc6 }"
    //...
}

Is there a "cleaner" way to do this conversion?

Illustrative code, like:

byte[] myBytes = byteSequence.Which.Method.I.Can.Use.To.Convert.It.To.Byte.Array.Again?();

Original sample (referenced link above):

new BasicAuthAuthorizationUser
{
      Login = "Administrator-2",
      // Password as SHA1 hash
      Password = new byte[]{0xa9,
                 0x4a, 0x8f, 0xe5, 0xcc, 0xb1, 0x9b,
                 0xa6, 0x1c, 0x4c, 0x08, 0x73, 0xd3,
                 0x91, 0xe9, 0x87, 0x98, 0x2f, 0xbb,
                 0xd3}
}

Our main idea:

new BasicAuthAuthorizationUser
{
      Login = "Administrator-2",
      // Password as SHA1 hash
      Password = configuration["MY_ENV_VAR_NAME"].Split(',').Select(s => Convert.ToByte(s, 16)).ToArray();

}
\$\endgroup\$
4
  • 2
    \$\begingroup\$ Please note that your edits seem to be responding directly to the answers provided. We take answer invalidation quite seriously here, so please make sure the answers still make sense after your edits. \$\endgroup\$
    – Mast
    Mar 17, 2022 at 15:02
  • 1
    \$\begingroup\$ Please do not edit the question, especially the code, after an answer has been posted. Changing the question may cause answer invalidation. Everyone needs to be able to see what the reviewer was referring to. What to do after the question has been answered. \$\endgroup\$
    – pacmaninbw
    Mar 17, 2022 at 16:26
  • \$\begingroup\$ All right np! But the "edited code" do not change the main objective of question. Please read the first version of question and compare with the only remaining "answer" and tell me if this answer make sense... \$\endgroup\$
    – Igor
    Mar 17, 2022 at 16:36
  • \$\begingroup\$ Whyy downvoted? \$\endgroup\$
    – Igor
    Mar 17, 2022 at 18:48

1 Answer 1

1
\$\begingroup\$

Not sure why are you storing the hexadecimal hash like that, if you can just use plain hexadecimal it would be more readable and would prevent from adding unnecessary work.

For your current work, you can use extension methods to convert from hexadecimal string to bytes, and vise versa.

public static class Extensions
{
    public static string ToHexadecimalSeparatedString(this byte[] bytes) => bytes != null ? string.Join(",", bytes.Select(x => $"0x{x:X2}")) : null;
    
    public static byte[] FromHexadecimalSeparatedString(this string hexadecimal)
    {
        if (string.IsNullOrWhiteSpace(hexadecimal)) return null;
        return hexadecimal.Split(',').Select(x=> Convert.ToByte(x, 16)).ToArray();
    }
    
}

usage :

string byteSequence = "0x65,0x31,0xb6,0x9e,0xaf,0xd2,0x39,0xc9,0xad,0x07,0x78,0x99,0x73,0x52,0x91,0xf5,0x93,0x1a,0x49,0xc6";

byte[] bytes = byteSequence.FromHexadecimalSeparatedString();

string hexadecimal = bytes.ToHexadecimalSeparatedString();

so in your sample would be :

new BasicAuthAuthorizationUser
{
      Login = "Administrator-2",
      // Password as SHA1 hash
      Password = configuration["MY_ENV_VAR_NAME"].FromHexadecimalSeparatedString()

}

Although, I'm still not convinced of the way that hexadecimal is stored, if you planning to revert it to plain hexadecimal (no separators, and no hexadecimal prefix) then you might be interested in using the following extensions :

public static class Extensions
{
    public static string ToHexadecimal(this byte[] bytes) => bytes != null ? string.Concat(bytes.Select(x => $"{x:X2}")) : null;
    
    public static byte[] FromHexadecimal(this string hexadecimal, string separater = null)
    {
        if(string.IsNullOrWhiteSpace(hexadecimal)) return null;

        if(!string.IsNullOrWhiteSpace(separater))
        {
            hexadecimal = hexadecimal.Replace(separater, string.Empty);
        }

        var temp = hexadecimal.Replace("0x", string.Empty);

        byte[] bytes = new byte[temp.Length / 2];

        for (int i = 0; i < bytes.Length; i++)
        {
            bytes[i] = Convert.ToByte(temp.Substring(i * 2, 2), 16);
        }
        
        return bytes;
    }
    
}

the following part is just for backward compatibility, to support the hashes that were already stored with separators. If all stored hashes are plain (no separators, no hexadecimal prefix) then, you can drop that part.

if(!string.IsNullOrWhiteSpace(separater))
{
    hexadecimal = hexadecimal.Replace(separater, string.Empty);
}

var temp = hexadecimal.Replace("0x", string.Empty);
\$\endgroup\$
13
  • \$\begingroup\$ Hey @iSR5 thanks for your time! The extension method is not the mais objetive. The main objetive is another "algorithm" to make the reverse conversion (string sequence to byte). \$\endgroup\$
    – Igor
    Mar 19, 2022 at 0:32
  • \$\begingroup\$ When you say "Not sure why are you storing the hexadecimal hash like that, if you can just use plain hexadecimal it would be more readable and would prevent from adding unnecessary work." What the better form to make it (store)? To avoid in "hard code"? The library (Hangfire) "requires" SHA1 formated byte array hexadecimal. \$\endgroup\$
    – Igor
    Mar 19, 2022 at 0:36
  • \$\begingroup\$ In this cenario, we only store the "string byte sequence" generated by code semple: cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(password))... (at the body of question) and after use Joins. This method has been created by "Hangfire" library creators. \$\endgroup\$
    – Igor
    Mar 19, 2022 at 0:48
  • 1
    \$\begingroup\$ @Igor the better form would be either storing the original hash bytes (no conversion to string) or convert it to hexadecimal if needs to be stored as string (use ToHexadecimal). The Hangfire seems to only requires byte[] in Password property, so using the hash bytes that generated from ComputeHash with Password property would be enough. It's been done also in the source code take a look at PasswordClear property github.com/HangfireIO/Hangfire.Dashboard.Authorization/blob/… \$\endgroup\$
    – iSR5
    Mar 19, 2022 at 11:07
  • 1
    \$\begingroup\$ @Igor I've never thought about it, however, doing direct Select using LINQ won't do the job. But you could use Enumerable.Range instead. with something like Enumerable.Range(0, hex.Length / 2).Select(x => Convert.ToByte(hex.Substring(x * 2, 2), 16)).ToArray(); \$\endgroup\$
    – iSR5
    Mar 30, 2022 at 14:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.