DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 14.5 Verifying that a String Is Uncorrupted During Transmission

Problem

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.

Solution

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.");
}

Discussion

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.

See Also

See the "SHA256Managed Class," "Convert.ToBase64String Method," and "Convert.FromBase64String Method" topics in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section