You have some text that will be sent across a network to another machine for processing. It is critical that you are able to verify that this text remains intact and unmodified when it arrives at its destination.
Calculate a hash value from
this string and append it to the string before it is sent to its
destination. Once the destination receives the string, it can remove
the hash value and determine whether the string is the same one that
was initially sent. The CreateStringHash
method
takes a string
as input, adds a hash value to the
end of it, and returns the new
string:
public class HashOps { public static string CreateStringHash(string unHashedString) { byte[] encodedUnHashedString = Encoding.Unicode.GetBytes(unHashedString); SHA256Managed hashingObj = new SHA256Managed( ); byte[] hashCode = hashingObj.ComputeHash(encodedUnHashedString); string hashBase64 = Convert.ToBase64String(hashCode); string stringWithHash = unHashedString + hashBase64; hashingObj.Clear( ); return (stringWithHash); } public static bool TestReceivedStringHash(string stringWithHash, out string originalStr) { // Code to quickly test the handling of a tampered string //stringWithHash = stringWithHash.Replace('a', 'b'), if (stringWithHash.Length < 45) { originalStr = null; return (true); } string hashCodeString = stringWithHash.Substring(stringWithHash.Length - 44); string unHashedString = stringWithHash.Substring(0, stringWithHash.Length - 44); byte[] hashCode = Convert.FromBase64String(hashCodeString); byte[] encodedUnHashedString = Encoding.Unicode.GetBytes(unHashedString); SHA256Managed hashingObj = new SHA256Managed( ); byte[] receivedHashCode = hashingObj.ComputeHash(encodedUnHashedString); bool hasBeenTamperedWith = false; for (int counter = 0; counter < receivedHashCode.Length; counter++) { if (receivedHashCode[counter] != hashCode[counter]) { hasBeenTamperedWith = true; break; } } if (!hasBeenTamperedWith) { originalStr = unHashedString; } else { originalStr = null; } hashingObj.Clear( ); return (hasBeenTamperedWith); } }
The
TestReceivedStringHash
method is called by the
code that receives a string with a hash value appended. This method
removes the hash value, calculates a new hash value for the string,
and checks to see whether both hash values match. If they match, both
strings are exactly the same, and the method returns
false
. If they don’t match, the
string has been tampered with, and the method returns
true
.
Since the
CreateStringHash
and
TestReceivedStringHash
methods are static members
of a class named HashOps
, we can call these
methods with code like the following:
public static void VerifyNonStringCorruption( ) { string testString = "This is the string that we'll be testing."; string unhashedString; string hashedString = HashOps.CreateStringHash(testString); bool result = HashOps.TestReceivedStringHash(hashedString, out unhashedString); Console.WriteLine(result); if (!result) Console.WriteLine("The string sent is: " + unhashedString); else Console.WriteLine("The string " + unhashedString + " has become corrupted."); }
You can use a hash, checksum, or cyclic redundancy check (CRC) to calculate a value based on a message. This value is then used at the destination to determine whether the message has been modified during transmission between the source and destination.
This recipe uses a hash value as a reliable method of determining
whether a string has been modified. The hash value for this recipe is
calculated using the SHA256Managed
class. This
hash value is 256 bits in size and produces greatly differing results
when calculated from strings that are very similar, but not exactly
the same. In fact, if a single letter is removed or even capitalized,
the resulting hash value will change.
By appending this value to the string, both the string and hash value can be sent to its destination. The destination then removes the hash value and calculates a hash value of its own based on the received string. These two hash values are then compared. If they are equal, the strings are exactly the same. If they are not equal, you can be sure that somewhere between the source and destination, the string was corrupted. This technique is great for verifying that transmission succeeded without errors, but it does not guarantee against malicious tampering. To protect against malicious tampering, use an asymmetric algorithm: sign the string with a private key and verify the signature with a public key.
The
CreateStringHash
method first converts the
unhashed string into a byte
array using the
GetBytes
method of the
UnicodeEncoding
class. This
byte
array is then passed into the
ComputeHash
method of the
SHA256Managed
class.
Once the hash value is calculated, the byte
array
containing the hash code is converted to a string containing base64
digits, using the Convert.ToBase64String
method.
This method accepts a byte
array, converts it to a
string of base64 digits, and returns that string. The reason for
doing this is to convert all unsigned integers in the
byte
array to values that can be represented in a
string data type. The last thing that this method does is to append
the hash value to the end of the string and return the newly hashed
string.
The
TestReceivedStringHash
method accepts a hashed
string and an out
parameter that will return the
unhashed string. This method returns a Boolean; as previously
mentioned, true
indicates that the string has been
modified, false
indicates that the string is
unmodified.
This method first removes the hash value from the end of the
StringWithHash
variable. Next, a new hash is
calculated using the string portion of the
StringWithHash
variable. These two hash values are
compared. If they are the same, the string has been received,
unmodified. Note that if you change the hashing algorithm used, you
must change it both in this method and the
CreateStringHash
method. You must also change the
numeric literal 44
in the
TestReceivedStringHash
method to an appropriate
size for the new hashing algorithm. This number is the exact length
of the base64 representation of the hash value, which was appended to
the string.