I have an algorithm use to generate and return a checkSum when an update operation is called. The second update need to send the given checksum, who is the proof that the object is exactly the same. Otherwise, an exception is throw.
Here is my class used to manage the checksum :
public class TokenUpdate
{
internal readonly DateTime expireDateTime;
private static readonly Object locker = new Object();
internal readonly byte[] _dataTokenKey;
public TokenUpdate(object insuranceAgreement, DateTime newExpireDateTime)
{
expireDateTime = newExpireDateTime;
_dataTokenKey = GetSignedToken(insuranceAgreement);
}
public TokenUpdate(byte[] dataTokenKey, DateTime newExpireDateTime)
{
expireDateTime = newExpireDateTime;
_dataTokenKey = dataTokenKey;
}
public bool IsExpired
{
get
{
return !(expireDateTime > DateTime.Now);
}
}
public byte[] GetSignedToken(object insuranceAgreement)
{
byte[] tokenKey;
byte[] tokenData;
// serialize data in byte[]
using (var ms = new MemoryStream())
{
using (var writer = new BinaryWriter(ms))
{
writer.Write(ObjectToByteArray(insuranceAgreement));
tokenData = ms.ToArray();
}
}
using (var sha1 = new SHA256Managed())
{
tokenKey = sha1.ComputeHash(tokenData);
}
return tokenKey;
}
public string GetTokenString()
{
byte[] tokenData;
// serialize data in byte[]
using (var ms = new MemoryStream())
{
using (var writer = new BinaryWriter(ms))
{
writer.Write(expireDateTime.Ticks);
tokenData = ms.ToArray();
}
}
return Convert.ToBase64String(tokenData.Concat(_dataTokenKey).ToArray());
}
public static TokenUpdate BuildToken(object insuranceAgreement, DateTime expireDateTime)
{
if (insuranceAgreement == null)
{
return null;
}
return new TokenUpdate(insuranceAgreement, expireDateTime);
}
public static bool AreEqual(TokenUpdate token, object toCompare)
{
var compareToken = new TokenUpdate(toCompare, DateTime.Now);
return token._dataTokenKey.SequenceEqual(compareToken._dataTokenKey);
}
public static TokenUpdate GetTokenFromString(string tokenString)
{
TokenUpdate returnedToken = null;
byte[] buffer;
try
{
buffer = Convert.FromBase64String(tokenString);
}
catch (FormatException)
{
return null;
}
if (buffer.Length != 40)
{
return null;
}
var data = buffer.Take(buffer.Length - 32).ToArray();
var sig = buffer.Skip(data.Length).Take(32).ToArray();
using (var ms = new MemoryStream(data))
using (var reader = new BinaryReader(ms))
{
var ticks = reader.ReadInt64();
var expires = new DateTime(ticks);
returnedToken = new TokenUpdate(sig, expires);
}
return returnedToken;
}
internal static byte[] ObjectToByteArray(object objectToSerialize)
{
if (objectToSerialize == null)
{
return null;
}
MemoryStream fs = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
try
{
//Here's the core functionality! One Line!
//To be thread-safe we lock the object
lock (locker)
{
formatter.Serialize(fs, objectToSerialize);
}
return fs.ToArray();
}
catch (SerializationException se)
{
throw;
}
finally
{
fs.Close();
}
}
}
With some unit test everything is fine, but running some stress-test show us that in 30% of case, the second update is rejected because the comparaison of the checksum sent with the one generated from the data in the database is failing.
I checked and I'm almost sure there is no inner-update between the two others.
I'm wondering, is the hash algorithm related to the SHA256Managed
class related to the mac adresse of the system datetime ?
It may cause the issue because we are load-balanced.