I want to create a method in c# that will accept my unique email or username and will return me a unique string just like youtube's video id (https://www.youtube.com/watch?v=_MSYfOYFF14). I can't simply use GUID because I want to generate a unique string against every user and it will remain same for that user each time I hit that method.
So is that possible anyhow?
1) Use the MD5 to get the byte array
2) Convert the byte array to string
3) Remove last two character
using System.Security.Cryptography;
//...
private string GenerateUniqueString(string input )
{
using (MD5 md5 = MD5.Create())
{
byte[] hash = md5.ComputeHash(Encoding.Default.GetBytes(input));
var res = Convert.ToBase64String(hash);
return res.Substring(0, res.Length - 2);
}
}
If it is not going to be exposed to someone else then it can be human readable. Stated that username or password is unique, that means that also combination of these values concatenated with some character (that can not be used in email and username) must be unique. The result is then very simple:
var uniqueString = $"{uniqueName}|{uniqueEmail}";
Simple example of hashing together multiple strings.
public static string Hash(bool caseInsensitive, params string[] strs)
{
using (var sha256 = SHA256.Create())
{
for (int i = 0; i < strs.Length; i++)
{
string str = caseInsensitive ? strs[i].ToUpperInvariant() : strs[i];
byte[] bytes = Encoding.UTF8.GetBytes(str);
byte[] length = BitConverter.GetBytes(bytes.Length);
sha256.TransformBlock(length, 0, length.Length, length, 0);
sha256.TransformBlock(bytes, 0, bytes.Length, bytes, 0);
}
sha256.TransformFinalBlock(new byte[0], 0, 0);
var hash = sha256.Hash;
return Convert.ToBase64String(hash);
}
}
There is a caseInsensitive parameter, because foo#bar.com is equivalent to foo#BAR.COM (and quite often FOO#bar.com is equivalent to all of them). Note how I'm encoding the strings: I'm prepending before each string the length of the encoded string (in UTF8). In this way "Hello", "World" is differento from "Hello World", because one will be converted to something similar to 5Hello5World while the other will be "11Hello World".
Usange:
string base64hash = Hash(true, "Donald Duck", "donaldduck#disney.com");
Note that thanks to the params keyword, the Hash method can accept any number of (string) parameters.
Related
Below is the code I use for hashing and I am trying to store the hash in a string(strSHA256) with UTF-8 encoding.
using (SHA256 sha256Hash = SHA256.Create())
{
bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(utf8EncodedString));
strSHA256 = Encoding.UTF8.GetString(bytes);
}
When I inspect the string variable I see "�u��Ou\u0004����O��zA�\u0002��\0�\a�}\u0012�L�Dr�"
type of value. My question is regarding to ? character marked in bold. They should be UTF-8 chars.
Why cant I view the UTF-8 special chars when I inspect from visual studio. The string o/p contains this weird char as well.
Note: I tried SHA256 with Node Js and I tend to see perfect UTF-8 special chars.
As #user2864740 mentioned bytes are not UTF8. Use base64 for bytes string representation.
var hello = "Hello world!";
using (var sha256Hash = SHA256.Create())
{
var bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(hello));
// String representation
var stringForBytes = Convert.ToBase64String(bytes);
// HEX string representation
var str = new StringBuilder(bytes.Length * 2);
foreach (var b in bytes)
{
str.Append(b.ToString("X2"));
}
var hexString = str.ToString();
}
I have a string that I need to hash in order to access an API. The API-creator has provided a codesnippet in Python, which hashes the code like this:
hashed_string = hashlib.sha1(string_to_hash).hexdigest()
When using this hashed string to access the API, everything is fine. I have tried to get the same hashed string result in C#, but without success. I have tried incredibly many ways but nothing has worked so far. I am aware about the hexdigest part aswell and I have kept that in mind when trying to mimic the behaviour.
Does anyone know how to get the same result in C#?
EDIT:
This is one of the many ways I have tried to reproduce the same result in C#:
public string Hash(string input)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(input));
var sb = new StringBuilder(hash.Length * 2);
foreach (byte b in hash)
{
sb.Append(b.ToString("X2"));
}
return sb.ToString().ToLower();
}
}
This code is taken from: Hashing with SHA1 Algorithm in C#
Another way
public string ToHexString(string myString)
{
HMACSHA1 hmSha1 = new HMACSHA1();
Byte[] hashMe = new ASCIIEncoding().GetBytes(myString);
Byte[] hmBytes = hmSha1.ComputeHash(hashMe);
StringBuilder hex = new StringBuilder(hmBytes.Length * 2);
foreach (byte b in hmBytes)
{
hex.AppendFormat("{0:x2}", b);
}
return hex.ToString();
}
This code is taken from: Python hmac and C# hmac
EDIT 2
Some input/output:
C# (using second method provided in above description)
input: callerId1495610997apiKey3*_&E#N#B1)O)-1Y
output: 1ecded2b66e152f0965adb96727d96b8f5db588a
Python
input: callerId1495610997apiKey3*_&E#N#B1)O)-1Y
output: bf11a12bbac84737a39152048e299fa54710d24e
C# (using first method provided in above description)
input: callerId1495611935apiKey{[B{+%P)s;WD5&5x
output: 7e81e0d40ff83faf1173394930443654a2b39cb3
Python
input: callerId1495611935apiKey{[B{+%P)s;WD5&5x
output: 512158bbdbc78b1f25f67e963fefdc8b6cbcd741
C#:
public static string Hash(string input)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(input));
var sb = new StringBuilder(hash.Length * 2);
foreach (byte b in hash)
{
sb.Append(b.ToString("x2")); // x2 is lowercase
}
return sb.ToString().ToLower();
}
}
public static void Main()
{
var x ="callerId1495611935apiKey{[B{+%P)s;WD5&5x";
Console.WriteLine(Hash(x)); // prints 7e81e0d40ff83faf1173394930443654a2b39cb3
}
Python
import hashlib
s = u'callerId1495611935apiKey{[B{+%P)s;WD5&5x'
enc = s.encode('utf-8') # encode in utf8
hash = hashlib.sha1(enc)
formatted = h.hexdigest()
print(formatted) # prints 7e81e0d40ff83faf1173394930443654a2b39cb3
Your main problem is that you are using different encodings for the same string in C# and Python. Use UTF8 in both languages and use the same casing. The output is the same.
Note that inside your input string (between callerId1495611935 and apiKey{[B{+%P)s;WD5&5x) there is an hidden \u200b character. That's why encoding your string in UTF-8 gives a different result than encoding it using ASCII. Does that character have to be inside your string?
I have simple encrypt function which takes string, convert it to bytes, xor it and apply base64.
JAVA:
String key = "1234";
public String encrypt(String plainText) throws Exception {
byte [] input = plainText.getBytes("UTF8");
byte [] output = new byte[input.length];
for(int i=0; i<input.length; i++)
output[i] = ((byte)(input[i] ^ key.charAt(i % key.length())));
String utf8 = new String(output);
return Utils.encode(utf8);
}
Then I save it to a file and open it in another application in C# using this decrypting method:
C#:
string key="1234";
public string Decrypt(string CipherText)
{
var decoded = System.Convert.FromBase64String(CipherText);
var dexored = xor(decoded, key);
return Encoding.UTF8.GetString(dexored);
}
byte[] xor(byte[] text, string key)
{
byte[] res = new byte[text.Length];
for (int c = 0; c < text.Length; c++)
{
res[c] = (byte)((uint)text[c] ^ (uint)key[c % key.Length]);
}
return res;
}
Problem is that accented characters like ěščřžýáí fail to decode.
Do you have any idea how to determine from which part the problem comes from or how to find it out? Looks to me that it has something to do with UTF-8.
I don't need suggestions for a better encryption. I have already working AES but I want to switch to xored base64 due to performance issues.
Thank you.
This is the problem:
String utf8 = new String(output);
return Utils.encode(utf8);
You should be using base64 here on output, rather than constructing a string out of the now-not-really-text data. It's possible that Utils.encode performs base64 encoding, having converted the input string back to byte for some reason - but fundamentally you shouldn't be constructing a string with your encrypted bytes using the String constructor.
If your Utils class has an encode(byte[]) method - and if that really does base64-encode the data (it's very frustrating to only have half of the code you're using) then you can just use:
return Utils.encode(output);
I use Crypto-JS v2.5.3 (hmac.min.js) http://code.google.com/p/crypto-js/ library to calculate client side hash and the script is:
$("#PasswordHash").val(Crypto.HMAC(Crypto.SHA256, $("#pwd").val(), $("#PasswordSalt").val(), { asByte: true }));
this return something like this:
b3626b28c57ea7097b6107933c6e1f24f586cca63c00d9252d231c715d42e272
Then in Server side I use the following code to calculate hash:
private string CalcHash(string PlainText, string Salt) {
string result = "";
ASCIIEncoding enc = new ASCIIEncoding();
byte[]
baText2BeHashed = enc.GetBytes(PlainText),
baSalt = enc.GetBytes(Salt);
System.Security.Cryptography.HMACSHA256 hasher = new HMACSHA256(baSalt);
byte[] baHashedText = hasher.ComputeHash(baText2BeHashed);
result = string.Join("", baHashedText.ToList().Select(b => b.ToString("x")).ToArray());
return result;
}
and this method returned:
b3626b28c57ea797b617933c6e1f24f586cca63c0d9252d231c715d42e272
As you see there is just some zero characters that the server side method ignore that. where is the problem? is there any fault with my server side method? I just need this two value be same with equal string and salt.
As you see there is just some zero characters that the server side method ignore that. where is the problem?
Here - your conversion to hex in C#:
b => b.ToString("x")
If b is 10, that will just give "a" rather than "0a".
Personally I'd suggest a simpler hex conversion:
return BitConverter.ToString(baHashedText).Replace("-", "").ToLowerInvariant();
(You could just change "x" to "x2" instead, to specify a length of 2 characters, but it's still a somewhat roundabout way of performing a bytes-to-hex conversion.)
Everyone else keeps reccomending to use things like using BitConverter and trimming "-" or using ToString(x2). There is a better solution, a class that has been in .NET since 1.1 SoapHexBinary.
using System.Runtime.Remoting.Metadata.W3cXsd2001;
public byte[] StringToBytes(string value)
{
SoapHexBinary soapHexBinary = SoapHexBinary.Parse(value);
return soapHexBinary.Value;
}
public string BytesToString(byte[] value)
{
SoapHexBinary soapHexBinary = new SoapHexBinary(value);
return soapHexBinary.ToString();
}
This will produce the exact format you want.
I believe the problem is here:
result = string.Join("", baHashedText.ToList().Select(b => b.ToString("x")).ToArray());
change it to:
result = string.Join("", baHashedText.ToList().Select(b => b.ToString("x2")).ToArray());
I am taking the MD5 hash of an image file and I want to use the hash as a filename.
How do I convert the hash to a string that is valid filename?
EDIT: toString() just gives "System.Byte[]"
How about this:
string filename = BitConverter.ToString(yourMD5ByteArray);
If you prefer a shorter filename without hyphens then you can just use:
string filename =
BitConverter.ToString(yourMD5ByteArray).Replace("-", string.Empty);
System.Convert.ToBase64String
As a commenter pointed out -- normal base 64 encoding can contain a '/' character, which obivously will be a problem with filenames. However, there are other characters that are usable, such as an underscore - just replace all the '/' with an underscore.
string filename = Convert.ToBase64String(md5HashBytes).Replace("/","_");
Try this:
Guid guid = new Guid(md5HashBytes);
string hashString = guid.ToString("N");
// format: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
string hashString = guid.ToString("D");
// format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
string hashString = guid.ToString("B");
// format: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
string hashString = guid.ToString("P");
// format: (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
This is probably the safest for file names. You always get a hex string and never worry about / or +, etc.
byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(inputString));
StringBuilder sBuilder = new StringBuilder();
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
string hashed = sBuilder.ToString();
Try this:
string Hash = Convert.ToBase64String(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes("sample")));
//input "sample" returns = Xo/5v1W6NQgZnSLphBKb5g==
or
string Hash = BitConverter.ToString(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes("sample")));
//input "sample" returns = 5E-8F-F9-BF-55-BA-35-08-19-9D-22-E9-84-12-9B-E6
Technically using Base64 is bad if this is Windows, filenames are case insensitive (at least in explorers view).. but in base64, 'a' is different to 'A', this means that perhaps unlikely but you end up with even higher rate of collision..
A better alternative is hexadecimal like the bitconverter class, or if you can- use base32 encoding (which after removing the padding from both base64 and base32, and in the case of 128bit, will give you similar length filenames).
Try Base32 hash of MD5. It gives filename-safe case insensitive strings.
string Base32Hash(string input)
{
byte[] buf = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(input));
return String.Join("", buf.Select(b => "abcdefghijklmonpqrstuvwxyz234567"[b & 0x1F]));
}