Extract Salt From Password C# [duplicate] - c#

This question already has answers here:
BitConverter.ToString() in reverse? [duplicate]
(7 answers)
Closed 8 years ago.
I have a function to calculate a SHA256 hash with a salt. The salt is then appended onto the hashed password and stored. The function for doing this looks like so:
public static string CalculateHash(string input)
{
var inputBuffer = new List<byte>(Encoding.Unicode.GetBytes(input));
var saltBytes = new byte[16];
using (var rnd = RandomNumberGenerator.Create())
{
rnd.GetBytes(saltBytes);
}
inputBuffer.AddRange(saltBytes);
byte[] hashedBytes;
using (var hasher = new SHA256Managed())
{
hashedBytes = hasher.ComputeHash(inputBuffer.ToArray());
}
var hash = BitConverter.ToString(hashedBytes).Replace("-", string.Empty);
var salt = BitConverter.ToString(saltBytes).Replace("-", string.Empty);
return string.Format("{0}:{1}", hash, salt);
}
It stores a string of 97 characters (including the ':') in length and seems to work well. I am struggling to strip the salt off of the hash when I retrieve it back. The issue I am having is converting the salt, in string form, back to a byte[16], which contains the original bytes. I assume once I have these original 16 bytes, I can append them to the user input, and hash the passwords to check for a match.
My current attempt is to split the hashed password and the salt at the colon delimiter and use a GetBytes function. It works like this:
public static bool ValidatePassword(string password, string hashWithSalt)
{
var split = hashWithSalt.Split(':');
var salt = split[1];
var saltBytes = GetBytes(salt);
}
private static byte[] GetBytes(string str)
{
var bytes = new byte[str.Length * sizeof(char)];
Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
But GetBytes is currently returning back byte[64], which is clearly not the original byte format. How can I go about fixing this?

Figured out my own solution. Just needed to change the GetBytes function to handle the hex conversion.
private static byte[] GetBytes(string str)
{
return Enumerable.Range(0, str.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(str.Substring(x, 2), 16))
.ToArray();
}

Related

Convert an byte array to MD5 Hash in react native

I have a system in C# which receives a password and this password is encrypted into a MD5 Hash using this function. I had read a lot of posts and suggestion, but I couldn't create the MD5 byte array as in C#.
public static string GetMD5HashData(string data)
{
//create new instance of md5
MD5 md5 = MD5.Create();
//convert the input text to array of bytes
byte[] hashData = md5.ComputeHash(Encoding.Default.GetBytes(data));
//create new instance of StringBuilder to save hashed data
StringBuilder returnValue = new StringBuilder();
//loop for each byte and add it to StringBuilder
for (int i = 0; i < hashData.Length; i++)
{
returnValue.Append(hashData[i].ToString());
}
// return hexadecimal string
return returnValue.ToString();
}
The return of this function is this string 207154234292557519022585191701391052252168 . I need to generate the same string in React Native.
This part Encoding.Default.GetBytes(data) in the C# function I've reproduced in React native, so both C# and React native return the same array of bytes from the input string.
Input string: 'system123' byte array: '[115, 121, 115, 116, 101, 109,
49, 50, 51]'
The React native function to generate the array of bytes.
convertStringToByteArray = (str) =>{
var bufferedVal = Buffer.from(str, 'utf8').toString('hex');
String.prototype.encodeHex = function () {
var bytes = [];
for (var i = 0; i < this.length; ++i) {
bytes.push(this.charCodeAt(i));
}
return bytes;
};
var byteArray = str.encodeHex();
return byteArray;
};
I've tried some libs like crypto-js for react-native to create the MD5 hash, but could not generate the same value as C# '207154234292557519022585191701391052252168'. Could someone help me?
Applying CryptoJS and assuming UTF8 encoding, the C# logic can be implemented as follows:
var result = '';
var hashBytes = CryptoJS.MD5('system123').toString(CryptoJS.enc.Latin1);
for (var i = 0; i < hashBytes.length; i++)
result += hashBytes.codePointAt(i).toString();
console.log(result); // 207154234292557519022585191701391052252168
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
Explanation:
CryptoJS.MD5() implicitly performs a UTF-8 encoding since the data is passed as string (here). The Latin1 encoder converts the WordArray into a bytes string. In the loop, the Unicode code point value for each byte is determined as non-negative integer, converted to a string, and concatenated.
The issue is that you use a different encoding in your C# code compared to your js code.
Try to use Encoding.UTF8 instead of Encoding.Default in your code.
public static string GetMD5HashData(string data)
{
//create new instance of md5
MD5 md5 = MD5.Create();
//convert the input text to array of bytes
byte[] hashData = md5.ComputeHash(Encoding.UTF8.GetBytes(data));
//create new instance of StringBuilder to save hashed data
StringBuilder returnValue = new StringBuilder();
//loop for each byte and add it to StringBuilder
for (int i = 0; i < hashData.Length; i++)
{
returnValue.Append(hashData[i].ToString());
}
// return hexadecimal string
return returnValue.ToString();
}

Converting a byte array to string and then back again produced different results

I'm using the .net port of libsodium. The hash generation function has two forms, one that accepts byte arrays and one that accepts strings:
public static byte[] ArgonHashBinary(string password, string salt, long opsLimit, int memLimit, long outputLength = ARGON_SALTBYTES)
public static byte[] ArgonHashBinary(byte[] password, byte[] salt, long opsLimit, int memLimit, long outputLength = ARGON_SALTBYTES)
What i'm having an issue with is both forms producing the same hash when the input values are identical.
var saltAsBytes = PasswordHash.ArgonGenerateSalt();
var saltAsString = Encoding.UTF8.GetString(saltAsBytes);
var tmp = Encoding.UTF8.GetBytes(saltAsString);
var hash1 = PasswordHash.ArgonHashBinary(password, saltAsString, 6, 134217728, 16);
var hash2 = PasswordHash.ArgonHashBinary( Encoding.UTF8.GetBytes(password), saltAsBytes, 6, 134217728, 16);
Anything with "PasswordHash." is libsodium and not my code.
From the code above when i convert it from a string and then back to a byte array the byte array. The byte array array is always a different length. ArgonGenerateSalt() produces a byte array with a length of 16. When i convert it back from a string above its generally ~30 (different every time because of different salts produced).
Why am i converting to UTF8? Because thats what they are doing internally:
https://github.com/adamcaudill/libsodium-net/blob/master/libsodium-net/PasswordHash.cs
public static byte[] ArgonHashBinary(string password, string salt, StrengthArgon limit = StrengthArgon.Interactive, long outputLength = ARGON_SALTBYTES)
{
return ArgonHashBinary(Encoding.UTF8.GetBytes(password), Encoding.UTF8.GetBytes(salt), limit, outputLength);
}
When i convert the salt to a UTF8 string the hashing function will fail because they are checking the length of the byte array to make sure its 16 bytes. If i convert it to a ASCII string it works but produces a different hash (which is expected).
To clarify the hashing piece in this code is not the issue. Figuring out why tmp is different then saltAsBytes is the key.
I think the problem here is that the ArgonGenerateSalt method doesn't return a UTF8 encoded string, it returns completely random bytes.
You can't decode random bytes as a UTF8 string and expect it to round trip. A trivial example to see where this blows up is to do the following:
var data = new byte[] { 128 };
var dataAsString = Encoding.UTF8.GetString( data );
var dataAsBytes = Encoding.UTF8.GetBytes( dataAsString );
After this, dataAsBytes will be 3 bytes (specifically 239, 191, 189).
Converting a byte array to string and then back again produced different results
A binary data may not be converted to string and then back to byte array
using Encoding.[AnyEncoding].GetBytes and Encoding.[AnyEncoding].GetString
Instead use Convert.ToBase64String and Convert.FromBase64String
You can easily test...
var bytes = new byte[] { 255, 255, 255 };
var buf = Encoding.UTF8.GetString(bytes);
var newbytes = Encoding.UTF8.GetBytes(buf);
newbytes's length will be 9.....
Edit: This is the test case for #Theo
var bytes = new byte[] { 0, 216 }; //any new byte[] { X, 216 };
var buf = Encoding.Unicode.GetString(bytes);
var newbytes = Encoding.Unicode.GetBytes(buf); //253,255

Simple string encryption/decryption with a small resulting string [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I need to encrypt a string and then be able to decrypt it again.
I implemented the solution here and it works well, but the resulting string is not suitable as it needs to be simple and short enough for a user to use.
I am encrypting incrementing database ID's (from 1) and there won't be more than 500. Ideally I'd like the encrypted string to be not more than 6 characters in length.
Any ideas appreciated..
edit: It's a lengthy form which the user can resume at a later date with this generated string
You can use AES in CTR mode without any padding. In this mode there is a counter that is encrypted and then the result is xor'd with your plaintext which is the number. The result should be small and you will get the encryption of AES which will be better than any substitution cipher you use (which you could probably break by hand). You will have to get the BouncyCastle crypto library however as the Microsoft implementation of Rijndael does not have CTR as an available mode. Below is an example of the AES class you would need to implement. As well as an example of encryption and decryption.
using System;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
public class AES
{
private readonly Encoding encoding;
private SicBlockCipher mode;
public AES(Encoding encoding)
{
this.encoding = encoding;
this.mode = new SicBlockCipher(new AesFastEngine());
}
public static string ByteArrayToString(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", string.Empty);
}
public static byte[] StringToByteArray(string hex)
{
int numberChars = hex.Length;
byte[] bytes = new byte[numberChars / 2];
for (int i = 0; i < numberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
public string Encrypt(string plain, byte[] key, byte[] iv)
{
byte[] input = this.encoding.GetBytes(plain);
byte[] bytes = this.BouncyCastleCrypto(true, input, key, iv);
string result = ByteArrayToString(bytes);
return result;
}
public string Decrypt(string cipher, byte[] key, byte[] iv)
{
byte[] bytes = this.BouncyCastleCrypto(false, StringToByteArray(cipher), key, iv);
string result = this.encoding.GetString(bytes);
return result;
}
private byte[] BouncyCastleCrypto(bool forEncrypt, byte[] input, byte[] key, byte[] iv)
{
try
{
this.mode.Init(forEncrypt, new ParametersWithIV(new KeyParameter(key), iv));
BufferedBlockCipher cipher = new BufferedBlockCipher(this.mode);
return cipher.DoFinal(input);
}
catch (CryptoException)
{
throw;
}
}
}
Example Usage
string test = "1";
AES aes = new AES(System.Text.Encoding.UTF8);
RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
byte[] key = new byte[32];
byte[] iv = new byte[32];
// Generate random key and IV
rngCsp.GetBytes(key);
rngCsp.GetBytes(iv);
string cipher = aes.Encrypt(test, key, iv);
string plaintext = aes.Decrypt(cipher, key, iv);
Response.Write(cipher + "<BR/>");
Response.Write(plaintext);
Output Example
CB
1
//encryption
string output="";
char[] readChar = yourInput.ToCharArray();
for (int i = 0; i < readChar.Length; i++)
{
int no = Convert.ToInt32(readChar[i]) + 10;
string r = Convert.ToChar(no).ToString();
output+=r;
}
//decryption
string output="";
char[] readChar = yourInput.ToCharArray();
for (int i = 0; i < readChar.Length; i++)
{
int no = Convert.ToInt32(readChar[i]) - 10;
string r = Convert.ToChar(no).ToString();
output+=r;
}
if you want it to be simple maybe use a main key of 6 letter
add it to the 6 letters input do a modulo based on the allowed input chars
its like a =1
b =2
c = 3
and then simply add a number a + 13 mod 24 > ...
wont say its secure but its simple as you requested
You might also do some combinations like for the next char is deoded prev char as +xx
Since you said that these are database IDs that are incrementing I'm assuming we're talking about positive integers. Limiting encryption is really not a good idea because the more limited your encryption is the easier it becomes to break. I'm not sure of your goal but it sounds like you may want to simply hide the ID from the end user by "encrypting it". So, if my assumption about these being positive whole integers between 1 and 999 then my suggestions are as follows. One, a simple character substitution. It will be pretty easy to figure out since you'll be limited to 10 characters. Two, convert them to hexidecimal. Won't fool a lot of people but the average end user might not recognize it. Third, convert the number to Base64. This won't be recognized as easily as hexidecimal. Last, come up with some sort of formula you can apply to the number that will always yield a unique result. I really don't advise that however because it's pretty difficult.

How to use 3DES encryption results to calculate the MAC value

I have a project to do for work to do in C#. I have the requirements for a project and this part is just a small piece in the entire project. I was given test data and the result. I need to code it so that I get the correct results. And at the moment I'm not getting the final result.
Please don't question or criticise the requirements, it's what I have and need to sort out and code it.
I was told to take input string "abc" and compute the SHA-1 hash for this. I got this part to work, here is the code:
private string CalculateSHA1Hash(string text, Encoding characterEncoding)
{
byte[] buffer = characterEncoding.GetBytes(text);
SHA1CryptoServiceProvider sha1CryptoServiceProvider = new SHA1CryptoServiceProvider();
byte[] s = sha1CryptoServiceProvider.ComputeHash(buffer);
string hash = BitConverter.ToString(sha1CryptoServiceProvider.ComputeHash(buffer)).Replace("-", "");
return hash;
}
I used UTF8Encoding because none was specified in the requirements doc. The result that I got from this is A9993E364706816ABA3E25717850C26C9CD0D89D.
I was then told to break up this string into 3 string blocks, of 16 characters each, and use just the 1st block. This is what I got:
block1: A9993E364706816A
I was also given 2 keys:
K1: 0123456789ABCDEF
K2: FEDCBA9876543210
Block1 is to be used as input string to a 3DES encryption using the 2 keys.
The result of the cipher text must be 6E5271A3F3F5C418, I am not getting this.
Below is my calculations. Can someone please see if I am doing this correctly and where I am doing this wrong. Chris (on SO) gave me some articles to read but I still can't get the results that I need to get. Is there something that cater for this already, I'm I just totally confused, or what?
public string Encrypt(string message)
{
string result = string.Empty;
// Calculate the SHA1 hash
UTF8Encoding characterEncoding = new UTF8Encoding();
string sha1HashResult = CalculateSHA1Hash(message, characterEncoding);
block1 = sha1HashResult.Substring(0, 16);
byte[] block1ByteArray = characterEncoding.GetBytes(block1);
string key = "0x" + accessKey1 + accessKey2 + accessKey1;
byte[] keyByteArray = StringToByteArray(key).ToArray();
byte[] enc = ComputeTripleDesEncryption(block1ByteArray, keyByteArray);
result = ByteArrayToString(enc);
return result;
}
public byte[] ComputeTripleDesEncryption(byte[] plainText, byte[] key)
{
TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
des.Key = key;
des.GenerateIV();
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.None;
ICryptoTransform ic = des.CreateEncryptor();
byte[] enc = ic.TransformFinalBlock(plainText, 0, plainText.Length);
return enc;
}
private byte[] StringToByteArray(String hex)
{
if (hex.Substring(0, 2) == "0x")
{
hex = hex.Substring(2);
}
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
private string ByteArrayToString(byte[] ba)
{
string hex = BitConverter.ToString(ba);
return hex.Replace("-", "");
}
I really do not know what to do further.
There's a few things wrong with what you have right now:
You're specifying that you need to use an IV, but you're using ECB (which doesn't use an IV)
You're generating the IV randomly, using GenerateIV(). This will cause the result to be different every time, if you're not using ECB.
You're only performing the transform on the final block, instead of the whole data.
See the following code sample for a decent idea of how to use 3DES in C#:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.tripledescryptoserviceprovider.aspx
I'd guess that since you're specifying an IV, you're actually meant to be using CBC instead of ECB. Try it out and see what you get.

how to recode back? BitConverter

byte[] test = Form1.StrToByteArray("simpletext");
string encoded_text = BitConverter.ToString(test).Replace("-", "").ToLowerInvariant();
textBox1.Text = encoded_text;//73696d706c6574657874
as from this line "73696d706c6574657874" to get back "simpletext" ??
//StrToByteArray()
public static byte[] StrToByteArray(string str)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
return encoding.GetBytes(str);
}
Do you absolutely have to use hex to start with? One slightly more efficient (and reversible with framework methods) option would be to use base 64:
string base64 = Convert.ToBase64String(test);
byte[] originalBytes = Convert.FromBase64String(base64);
string text = Encoding.ASCII.GetString(originalBytes);
I personally wouldn't suggest using ASCII as your encoding, however - UTF-8 will work the same way for ASCII characters, but allow all of Unicode to be encoded.
If you do have to use hex, you'll need a method to parse hex - I have an example here.
var input = "73696d706c6574657874";
var bytes = Enumerable
.Range(0, input.Length)
.Where(x => 0 == x % 2)
.Select(x => Convert.ToByte(input.Substring(x, 2), 16))
.ToArray();
Console.WriteLine(Encoding.ASCII.GetString(bytes));
should have done so
public static byte[] StrToByteArray(string str)
{
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
return encoding.GetBytes(str);
}
//here im replace ASCIIEncoding to UTF8Encoding how said me Darin Dimitrov. respect man!!! very thx!!!!
and now i have result equal A-ґР^E—‹

Categories

Resources