I am trying to overload a method which uses a Guid as its parameter with another method that has a string as its parameter.
// read a student object from the dictionary
static public User Retrieve(Guid ID)
{
User user;
// find the Guid in the Dictionary
user = Users[ID];
return user;
}
static public User Retrieve(string Email)
{
User user;
Guid id;
// find the Guid in the Dictionary
using (SHA1 sha1 = SHA1.Create())
{
byte[] hash = SHA1.ComputeHash(Encoding.Default.GetBytes(Email));
id = new Guid(hash);
}
user = Users[id];
return user;
}
Test Results
Result Message:
Test method SRS.CRUDUsers.UpdateStudent threw exception:
System.ArgumentException: Byte array for GUID must be exactly 16 bytes long.
Test Method:
public void UpdateStudent()
{
// Arrange
Student expected = (Student)UserRepo.Retrieve("info#info.com");
// Act
Student actual = (Student)UserRepo.Retrieve("info#info.com");
actual.FirstName = "Joe";
actual.LastName = "Brown";
actual.Birthday = new DateTime(1977, 2, 23);
actual.DegreeSelected = 1;
// Assert (this is only really checking the object agains itself
// because both variables are referencing the same object).
Assert.IsNotNull(actual.ID);
Console.WriteLine(actual.ID);
Assert.AreEqual(expected.Name, actual.Name);
Assert.AreEqual(expected.GetType(), actual.GetType());
Assert.AreEqual(expected.Birthday, actual.Birthday);
Assert.AreEqual(expected.Age, actual.Age);
}
It seems to be a type issue so probably something obvious.
From wiki:
SHA-1 produces a 160-bit (20-byte)
Error:
Byte array for GUID must be exactly 16 bytes long.
So you can't just use SHA1 as Guid. You can try MD5 (it is 128) if this is not security related code.
Try following
new Guid(hash.Take(16).ToArray())
Or you can use MD5 hash, since it is a 16-byte hash
var md5 = MD5.Create();
var hash = md5.ComputeHash(Encoding.Default.GetBytes(Email));
new Guid(hash);
Related
c# Generate Random number passing long as a seed instead of int32, but l need to pass phone numbers or accounts number
https://learn.microsoft.com/en-us/dotnet/api/system.random.-ctor?view=netframework-4.8#System_Random__ctor_System_Int32_
Please suggest any reliable NuGet package which does this or any implementation who has already done something like this.
I need to pass the complete PhoneNumber as the seed which I'm able to do in python but not with C# and my code stack is all in C#
using System;
public class Program
{
public static void Main()
{
int seed = 0123456789;
Random random = new Random(seed);
double result = random.NextDouble();
Console.WriteLine(result);
}
}
Some insights on my requirements and what I'm trying to achieve:
1)We're doing this for A/B testing and todo data analysis on the
experience of two services.
2)When a request comes with
phoneNumber based on random.NextDouble() there is a preset percentage
which we use to determine whether to send a request to service A or
service B
3)For example, let's says the request comes and falls
under >0.5 then we direct the request to service A and the next time
the request with the same phone number comes in it will be >0.5 and
goes service A since the seed is a unique hash of phoneNumber.
The method GetHashCode() belongs to Object class, it has nothing to do with random number generation. Please read here (https://learn.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=netframework-4.8). The documentation clearly states that it is possible to get collisions specially if input is consistent.
The method HashAlgorithm.ComputeHash (documented here - https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.hashalgorithm.computehash?view=netframework-4.8) calculates the hash for a given value, but it is consistent in nature, i.e. if input is same, generated output is also same. Obviously this is not the desired output (I assume). I have attached the sample code I tried to generate this.
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
while (true)
{
Console.WriteLine("Enter a 9 digit+ number to calculate hash");
var val = Console.ReadLine();
long target = 0;
bool result = long.TryParse(val,out target);
if (result)
{
var calculatedHash = OutputHash(target);
Console.WriteLine("Calculated hash is : " + calculatedHash);
}
else
{
Console.WriteLine("Incorrect input. Please try again.");
}
}
}
public static string OutputHash(long number)
{
string source = Convert.ToString(number);
string hash;
using (SHA256 sha256Hash = SHA256.Create())
{
hash = GetHash(sha256Hash, source);
Console.WriteLine($"The SHA256 hash of {source} is: {hash}.");
Console.WriteLine("Verifying the hash...");
if (VerifyHash(sha256Hash, source, hash))
{
Console.WriteLine("The hashes are the same.");
}
else
{
Console.WriteLine("The hashes are not same.");
}
}
return hash;
}
private static string GetHash(HashAlgorithm hashAlgorithm, string input)
{
// Convert the input string to a byte array and compute the hash.
byte[] data = hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(input));
// Create a new Stringbuilder to collect the bytes
// and create a string.
var sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
// Return the hexadecimal string.
return sBuilder.ToString();
}
// Verify a hash against a string.
private static bool VerifyHash(HashAlgorithm hashAlgorithm, string input, string hash)
{
// Hash the input.
var hashOfInput = GetHash(hashAlgorithm, input);
// Create a StringComparer an compare the hashes.
StringComparer comparer = StringComparer.OrdinalIgnoreCase;
return comparer.Compare(hashOfInput, hash) == 0;
}
I agree with #Knoop 's comment above that you might end up with same integer mapping to multiple long number input values.
If you are looking for a 'pure' random number generator with long value as seed, you don't have a choice but to go for third party libraries (or implementing your own custom algorithm). However, rather than getting into such complexities, simple
Guid g = Guid.NewGuid();
should do the trick (https://learn.microsoft.com/en-us/dotnet/api/system.guid.newguid?view=netframework-4.8).
Documentation (https://learn.microsoft.com/en-gb/windows/win32/api/combaseapi/nf-combaseapi-cocreateguid?redirectedfrom=MSDN )says that even this can end up having collisions but chances are very minimal.
Finally, this sounds like potential duplicate of .NET unique object identifier
take the hash of the phone number, eg:
var phoneNumber = 123456789L;
var seed = phoneNumber.GetHashCode();
This means that for the same phoneNumber you will get the same sequence. It also means that for some phone numbers you will get identical sequences, but that is going to be slim. And it might be different on different .net runtimes as commented, but you might not care.
Not sure why you want to, but I there are reasons, e.g. test code
Our user recieve emails with a prepared hyperlink.
This hyperlink has a parameter.
When the user clicks the hyperlink I need to make sure that the user did not tamper with the parameter.
The parameter is a simple integer.
I would like to create a hash from the integer.
I would like to sign the hash.
What are the current .NET classes I should use for this task?
The easiest way to do this is use a HMAC ("keyed-hash message authentication code").
public static string CreateHMAC(int data, byte[] key)
{
using(var hmac = new HMACSHA1(key))
{
var dataArray = BitConverter.GetBytes(data);
var resultArray = hmac.ComputeHash(dataArray);
return Convert.ToBase64String(resultArray);
}
}
You keep key secret on your server, and pass data and the result of CreateHMAC to the user in the URL. When the user clicks the link you verify that the data was not modified.
public static bool VerifyHMAC(int data, byte[] key, string verification)
{
using(var hmac = new HMACSHA1(key))
{
var dataArray = BitConverter.GetBytes(data);
var computedHash = hmac.ComputeHash(dataArray);
var verificationHash = Convert.FromBase64String(verification);
for (int i = 0; i < verificationHash.Length; i++)
{
if (computedHash[i] != verificationHash[i])
{
return false;
}
}
return true;
}
}
You can see here how to calculate the hash.
Since HASH functions accepts only ARRAYS, I advice you to create an array of ONE single item, with your integer there.
You may "sign" the input calculating the HASH of the calculated HASH above PLUS a constant of your application (or date and hour, for instance)
I'm actually trying to implement a very simple login mecanism for an app I'm developping in Visual C# .NET 2.0 on an embedded device. After some researches, I've found on the msdn a code sample performing password hashing :
How to store passwords
Unfortunately, when I try to use it, that code sample is raising a FormatException on the call to byte.Parse on the substrings of the hexadecimal string SaltValue. I really have trouble to understand why, since I haven't done any change to the code.
Here is the code :
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Globalization;
private const int SaltValueSize = 4;
private static string GenerateSaltValue()
{
UnicodeEncoding utf16 = new UnicodeEncoding();
if (utf16 != null)
{
// Create a random number object seeded from the value
// of the last random seed value. This is done
// interlocked because it is a static value and we want
// it to roll forward safely.
Random random = new Random(unchecked((int)DateTime.Now.Ticks));
if (random != null)
{
// Create an array of random values.
byte[] saltValue = new byte[SaltValueSize];
random.NextBytes(saltValue);
// Convert the salt value to a string. Note that the resulting string
// will still be an array of binary values and not a printable string.
// Also it does not convert each byte to a double byte.
//Original line :
//string saltValueString = utf16.GetString(saltValue);
//Replaced by :
string saltValueString = utf16.GetString(saltValue, 0, SaltValueSize);
// Return the salt value as a string.
return saltValueString;
}
}
return null;
}
private static string HashPassword(string clearData, string saltValue, HashAlgorithm hash)
{
UnicodeEncoding encoding = new UnicodeEncoding();
if (clearData != null && hash != null && encoding != null)
{
// If the salt string is null or the length is invalid then
// create a new valid salt value.
if (saltValue == null)
{
// Generate a salt string.
saltValue = GenerateSaltValue();
}
// Convert the salt string and the password string to a single
// array of bytes. Note that the password string is Unicode and
// therefore may or may not have a zero in every other byte.
byte[] binarySaltValue = new byte[SaltValueSize];
//FormatException raised here
binarySaltValue[0] = byte.Parse(saltValue.Substring(0, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
binarySaltValue[1] = byte.Parse(saltValue.Substring(2, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
binarySaltValue[2] = byte.Parse(saltValue.Substring(4, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
binarySaltValue[3] = byte.Parse(saltValue.Substring(6, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
//...
//Some more code
//...
}
}
I only have changed one line :
string saltValueString = utf16.GetString(saltValue);
to
string saltValueString = utf16.GetString(saltValue, 0, SaltValueSize);
because the first version of the method doesn't seem to be available for embedded C#. But anyway I've tested without changing this line (on a non-embedded environment), and it still was raising a FormatException.
I've copied the SaltValueSize value from that other msdn code sample (which is related) :
How to validate passwords
The test that raises the exception :
HashPassword("youpi", null, new SHA1CryptoServiceProvider());
The problem lies in the fact that your GenerateSaltValue method does not return string of hexademical numbers.
It returns string of some random symbols, that may or usually may not be valid hexademical symbols - for me it created string of mostly Chinese hieroglyphs that for sure aren't parseable by Byte.Parse method.
Also, your example pertains to Microsoft Commerce Server - I have no idea whatsoever it is.
"SOLUTION:"
I am not sure what all this examples wants to accomplish with this string-tohex-tobinary conversions, but for it to successfully execute the GenerateSaltValue should be something like:
public static string ByteArrayToString(byte[] byteArray)
{
StringBuilder hex = new StringBuilder(byteArray.Length * 2);
foreach (byte b in byteArray)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
// Renamed GenerateSaltValue method
private static string GenerateHexSaltString()
{
Random random = new Random();
// Create an array of random values.
byte[] saltValue = new byte[SaltValueSize];
random.NextBytes(saltValue);
string saltValueString = ByteArrayToString(saltValue);
// Return the salt value as a string.
return saltValueString;
}
And your program will "work", thanks to How do you convert Byte Array to Hexadecimal String, and vice versa?
BUT:
Using Random for Salt creation is a bad idea.
string-tohex-tobinary conversion looks even worser.
And other problems...
SO:
Read some articles that really pertains to C# password hashing and encryption, like:
Hash and salt passwords in C#
And be very attentive while searching for code examples - they could use another version, platform or even language. Good luck.
I was provided the following code sample in Java and I'm having trouble converting it to C#. How would I go about converting this so it'll work in .NET 4.5?
public static String constructOTP(final Long counter, final String key)
throws NoSuchAlgorithmException, DecoderException, InvalidKeyException
{
// setup the HMAC algorithm, setting the key to use
final Mac mac = Mac.getInstance("HmacSHA512");
// convert the key from a hex string to a byte array
final byte[] binaryKey = Hex.decodeHex(key.toCharArray());
// initialize the HMAC with a key spec created from the key
mac.init(new SecretKeySpec(binaryKey, "HmacSHA512"));
// compute the OTP using the bytes of the counter
byte[] computedOtp = mac.doFinal(
ByteBuffer.allocate(8).putLong(counter).array());
//
// increment the counter and store the new value
//
// return the value as a hex encoded string
return new String(Hex.encodeHex(computedOtp));
}
Here is the C# code that I've come up with thanks to Duncan pointing out the HMACSHA512 class, but I'm unable to verify the results match without installing java, which I can't do on this machine. Does this code match the above Java?
public string ConstructOTP(long counter, string key)
{
var mac = new HMACSHA512(ConvertHexStringToByteArray(key));
var buffer = BitConverter.GetBytes(counter);
Array.Resize(ref buffer, 8);
var computedOtp = mac.ComputeHash(buffer);
var hex = new StringBuilder(computedOtp.Length * 2);
foreach (var b in computedOtp)
hex.AppendFormat("{0:x2", b);
return hex.ToString();
}
A SecretKeySpec is used to convert binary input into something that is recognised by Java security providers as a key. It does little more than decorate the bytes with a little post-it note saying "Pssst, it's an HmacSHA512 key...".
You can basically ignore it as a Java-ism. For your .NET code, you just need to find a way of declaring what the HMAC key is. Looking at the HMACSHA512 class, this seems quite straight-forward. There is a constructor that takes a byte array containing your key value.
I am having problems with our third party vendor implimenting an sso. They are receiving the following error when verifying my signature:
java.lang.ArithmeticException: BigInteger: modulus not positive--at java.math.BigInteger.modPow(BigInteger.java:1556)
I have no control over their Java code. Here is what I am doing now:
I created a key pair in C# using this code:
CspParameters csp = new CspParameters();
csp.KeyNumber = (int)KeyNumber.Signature;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024, csp))
{
File.AppendAllText(path + "PrivateKey.xml", rsa.ToXmlString(true));
File.AppendAllText(path + "PublicKey.xml", rsa.ToXmlString(false));
}
Here is the code for the signature:
public string MD5withRSASignature(string encryptedStringToSign)
{
byte[] signature;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024))
{
XmlDocument xDoc = new XmlDocument();
xDoc.Load(PRIVATE_KEY_PATH);
rsa.FromXmlString(xDoc.OuterXml);
byte[] bytes = Encoding.UTF8.GetBytes(encryptedStringToSign);
signature = rsa.SignData(bytes, new MD5CryptoServiceProvider());
}
return Convert.ToBase64String(signature);
}
(Yes I know the private key should be in a key store).
Here is the code they use to convert xml keys (this is Java)
private static RSAPublicKey ReadXMLKey(String fileName)
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse( new File(fileName) );
byte[] modBytes = GetBytesFromElement(document, "Modulus");
byte[] expBytes = GetBytesFromElement(document, "Exponent");
RSAPublicKeySpec rsaKeyspec = new RSAPublicKeySpec(new BigInteger(modBytes), new BigInteger(expBytes));
RSAPublicKey key = (RSAPublicKey)KeyFactory.getInstance("RSA").generatePublic(rsaKeyspec);
return key;
}
private static byte[] GetBytesFromElement(Document doc, String tag) throws IOException
{
BASE64Decoder decoder = new BASE64Decoder();
NodeList list = doc.getElementsByTagName(tag);
byte[] results = null;
if (list.getLength() == 1)
{
Element item = (Element)list.item(0);
Text text = (Text)item.getFirstChild();
results = decoder.decodeBuffer(text.getNodeValue().trim());
}
return results;
}
The exception has to do with the RSA public key that Java is using. None of your code addresses that point. How did the Java side obtain that key, what format is being used?
One common mistake that can account for the error is if the modulus is converted to an array of bytes but a leading zero byte is not present when it needs to be. Basically, this BigInteger constructor is a little trickier to use than it may first appear. It is designed for compatibility with DER-encoded ASN.1 integers. The upshot of it all is that if the first byte b of your modulus has the high bit set, i.e. 128 <= b < 256, you must prepend a leading zero byte or your modulus will be interpreted as a negative number. For simplicity you can always prepend a leading zero byte; no harm will come if it wasn't necessary.