Introduction
There are essentially two situations where data is the most vulnerable: when it is stored persistently, and when it transferred across a network. For instance, when we program in Win32, we often have to write code to convert Unicode characters and strings to MBCS (Multi-Byte Character Set) characters and strings. In .NET, all characters are Unicode and all strings are Unicode to make things easier at runtime. But, when strings are saved to a file that is meant to be transmitted across a network, transmitting 16-bit values is not efficient if half of the bytes are zero. The solution is to encode the 16-bit values into a compressed array of bytes and then decode the array of bytes back into 16-bit values. At the same time, if data is persisted to disk, you can use permission demands to control access to an application, and ACLs to protect data. But, attackers with access to a hard disk or network infrastructure can bypass software security, and either extract private information from the data or modify that information. Imagine if you had to tell a client that his personal information was extracted. This is why any .NET developer should know Cryptography.
Stated loosely, a cipher block is an algorithm, a computational model, that intends to transform and thus disguise data by a series of rounds that are comprised of repetitive operations. A symmetric algorithm is mathematically reversible, and an asymmetric algorithm is not. The behavior of the algorithm is largely influenced by the length of the key that is inserted as input with the plain text. An MD5 digital signature, however, is a one-way hash function that is dependent on four additive constants. A hash function is not mathematically reversible; a hash function is used for passwords, and for digitally signing an encrypted message. This paper will start with a basic example of the use of base-64 encoding in order to show how to convert between characters and bytes. Encoding is normally done when you want to send a string to a file or network stream by using the System.IO.BinaryWriter
or System.IO.StreamWriter
type. Decoding is done when you want to read a string from a file or network stream by using the System.IO.BinaryReader
or System.IO.StreamReader
type. Here is an example of base-64 encoding and decoding. Note that when messages are encrypted to be sent by email, they are normally encoded prior to being encrypted, and decoded prior to being decrypted.
using System;
public static class Program {
public static void Main() {
Byte[] bytes = new Byte[10];
new Random().NextBytes(bytes);
Console.WriteLine(BitConverter.ToString(bytes));
String s = Convert.ToBase64String(bytes);
Console.WriteLine(s);
bytes = Convert.FromBase64String(s);
Console.WriteLine(BitConverter.ToString(bytes));
}
}
Output:
EA-3A-3F-08-95-E2-EC-B1-37-6F
6jo/CJXi7LE3bw==
EA-3A-3F-08-95-E2-EC-B1-37-6F
Symmetrical Encryption
The code below demonstrates the steps for encrypting a file using symmetric keys. Symmetric keys mean that the same key is used to both encrypt and decrypt a message. This might not sound like strong encryption, but the purpose is to make it work. For instance, you can use XOR to encrypt and decrypt. If you have a value of 55h, and XOR it with a key having a value of A1h, then the output value is F4h. But, if you XOR F4h with the key A1h, then you have the original 55h value. Definitely not strong encryption, but learning the basics can lead to stronger encryption practices. Note that the file that is going to be encrypted is c:\file.txt.The output file, "c:\file.txt.enc", must be created as a blank file where the output of the encrypted c:\file.txt data must stream to:
using System;
using System.IO;
using System.Security.Cryptography;
class App {
static void Main(string[] args) {
string inFileName = @"C:\file.txt";
string outFileName = @"C:\file.txt.enc";
FileStream inFile = new FileStream(inFileName,
FileMode.Open, FileAccess.Read);
FileStream outFile = new FileStream(outFileName,
FileMode.Open, FileAccess.Write);
SymmetricAlgorithm myAlg = new RijndaelManaged();
myAlg.GenerateKey();
byte[] fileData = new byte[inFile.Length];
inFile.Read(fileData, 0, (int)inFile.Length);
ICryptoTransform encryptor = myAlg.CreateEncryptor();
CryptoStream encryptStream = new CryptoStream(outFile,
encryptor, CryptoStreamMode.Write);
encryptStream.Write(fileData, 0, fileData.Length);
encryptStream.Close();
inFile.Close();
outFile.Close();
}
}
Having created a blank file in order to pipe, or stream the cipher to, we know examine the contents of the encrypted file (not using the type con > ..) command but the DOS type command: c:...\>type c:\file.txt.enc
Any non-keyed hash algorithm in .NET derives from a single class. If we run a console application to calculate for a single file, we can run it repeatedly and still get the same result: the same hash result will result until the file is modified. After the file is modified, the hash result also changes. Consider the example below:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
class App {
static void Main(string[] args) {
MD5 myHash = new MD5CryptoServiceProvider();
FileStream file = new FileStream(args[0],
FileMode.Open, FileAccess.Read);
BinaryReader reader = new BinaryReader(file);
myHash.ComputeHash(reader.ReadBytes((int)file.Length));
Console.WriteLine(Convert.ToBase64String(myHash.Hash));
}
}
And finally, the output:
C:\Windows\MICROS~1.NET\FRAMEW~1\V20~1.507>echo Enter your name: > MyHash.txt
C:\Windows\MICROS~1.NET\FRAMEW~1\V20~1.507>hashdemo MyHash.txt U1isy9jyCs2IWfzOwNNeKQ==
C:\Windows\MICROS~1.NET\FRAMEW~1\V20~1.507>hashdemo MyHash.txt U1isy9jyCs2IWfzOwNNeKQ==
C:\Windows\MICROS~1.NET\FRAMEW~1\V20~1.507>echo Enter my Name: > MyHash.txt
C:\Windows\MICROS~1.NET\FRAMEW~1\V20~1.507>hashdemo MyHash.txt UNxj+QNkP7axjnsOD5EzOg==