Click here to Skip to main content
15,881,709 members
Articles / Desktop Programming / Windows Forms

Securing Data in .NET

Rate me:
Please Sign up or sign in to vote.
4.81/5 (29 votes)
28 Oct 2007CPOL10 min read 93.1K   5.9K   108   8
Need to learn further on cryptology? This article takes an overview of common cryptosystems, along with a step by step description of the Data Encryption Standard and the Advanced Encryption Standard.

Screenshot - Screenshot2.png

  1. Introduction
  2. An Overview of Common Cryptosystems
  3. Data Encryption Standard
  4. Advanced Encryption Standard
    1. Enciphering
      1. SubBytes()
      2. ShiftRows()
      3. MixColumns()
      4. AddRoundKey()
    2. Deciphering
    3. Generating Keys
  5. References & External Links
  6. History

1. Introduction

Cryptology is a field that deals with providing security and privacy. This field includes many cryptosystems, each one consisting of a set of algorithms that aims to provide data security. Nowadays, cryptosystems are widely used in all areas of digital technology. Digital signatures, electronic mails, and internet banking are just a few applications that cryptosystems are used in. This work briefly explains the common cryptosystems, and details the two most popular private-key ciphers: DES ,which is probably the most widely used, and AES, which is intended to replace DES. Now, let's begin with an overview of the common cryptosystems.

2. An Overview of Common Cryptosystems

Three cryptosystems are the base of many security applications: symmetric cryptosystems, asymmetric cryptosystems, and hashing.

2.a Symmetric (Private Key) Cryptosystems

Symmetric cryptosystems use the same key in both the encryption and decryption operations. These two operations are usually similar. However, some parts of these operations follow the reverse order of each other (e.g., for AES, the decryption algorithm follows the reverse order of the encryption algorithm, with a little modification on some other methods).

Symmetric cryptosystems divide data into small blocks and encrypt them one by one using a secret key. Then, the blocks are combined and sent to the recipient as a whole. At the recipient side, the decryption operation is applied using the same key, which yields the restored data.

Symmetric algorithms are extremely fast compared to asymmetric cipher algorithms, and are well suited for performing cryptographic transformations on large streams of data. The most popular symmetric cipher algorithms are DES, AES, and TripleDES. TripleDES uses DES three times in a sequence with different keys. DES and AES will be covered in detail later.

Block diagram of symmetric cryptosystems

Screenshot - SymmetricCipher.png

Plain text is encrypted with the private key, transmitted, then decrypted with the same private key.

C#
//Simple Private-Key Encryption Operation
private void buttonEncrypt_Click(object sender, EventArgs e)
{
    string password = textBoxKey.Text;
    string salt = textBoxSalt.Text;
    string plainText = textBoxInput.Text;
    byte[]plainBytes=Encoding.ASCII.GetBytes(plainText);

    //Rfc2898DeriveBytes: Used to Generate Strong Keys
    Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(password, 
        Encoding.ASCII.GetBytes(salt));
        //Non-English Alfhabets Will not Work on ASCII Encoding

    SymmetricAlgorithm Alg = GetSelectedAlgorithm();

    Alg.Key = rfc.GetBytes(Alg.KeySize / 8);
    Alg.IV = rfc.GetBytes(Alg.BlockSize / 8);

    MemoryStream strCiphered = new MemoryStream();//To Store Encrypted Data

    CryptoStream strCrypto = new CryptoStream(strCiphered, 
        Alg.CreateEncryptor(), CryptoStreamMode.Write);

    strCrypto.Write(plainBytes, 0, plainBytes.Length);
    strCrypto.Close();
    textBoxCiphered.Text = Convert.ToBase64String(strCiphered.ToArray());
    strCiphered.Close();
}
        
private SymmetricAlgorithm GetSelectedAlgorithm()
{
    SymmetricAlgorithm Alg = null;
    if (comboBoxAlgorithm.SelectedIndex == 0)
        Alg = new DESCryptoServiceProvider();
    else if (comboBoxAlgorithm.SelectedIndex == 1)
        Alg = new RijndaelManaged();//RijndaelManaged: AES Algorithm in .NET
    else if (comboBoxAlgorithm.SelectedIndex == 2)
        Alg = new TripleDESCryptoServiceProvider();
    else
        Alg = new RC2CryptoServiceProvider();
    return Alg;
}

2.b Asymmetric (Public Key) Cryptosystems

Asymmetric cryptosystems perform encryption and decryption operations via public-private key pairs which are mathematically linked with each other. Unlike symmetric cryptosystems, when a public key is used to encrypt data, it becomes impossible to restore that data without using a private key. More simply, if you use a private key to encrypt data, then you use a public key to decrypt that data, and vice versa.

Asymmetric cipher algorithms are based on heavy mathematical operations, thus they are not efficient at processing large blocks of data. They are often used to safely exchange small session keys. Asymmetric ciphers can be used for authentication and confidentially. The most popular asymmetric cipher algorithm is RSA, which requires a strong mathematical background to be understood. Here is the structure of RSA:

//Generating Public-Private Keys
Select p,q that p and q are two different large prime numbers
Let n=p*q
Let m=(p-1)*(q-1)
Select e that 1<e<m and gcd(m,e)=1 (e is coprime to m)
Calculate d such that (d*e)mod(m) = 1

Public Key: {e,n}
Private Key:{d,n} 

//Encryption
PlainText : M   (M<n)
CipherText: C = M<sup>e</sup> Mod(n) 

//Decryption
CipherText : C
PlainText  : M = C<sup>d</sup> Mod(n)
Block diagram of asymmetric cryptosystems

Screenshot - AsymmetricCipher.png

Plain text is encrypted [by Bob] with Alice's public key, transmitted, then decrypted [by Alice] with Alice's private key.

C#
/*
 * Unlike symmetric ciphers, we are not free to choose our 
 * public-private key pairs. That is because these two keys
 * are mathematically linked each other. A proper way on 
 * creating these keys in .NET is to use an asymmetric class 
 * which generates these keys on its constructor. 
 */
private void GenerateKeyPairs()
{                        
    //Each time this constructor is called, 
    //a different public-private key pair is generated.
    rsaCipher = new RSACryptoServiceProvider();

    //retrieve public parameters
    textBoxPublicKey.Text = rsaCipher.ToXmlString(false);

    //retrieve private parameters
    textBoxPrivateKey.Text = rsaCipher.ToXmlString(true);
}

private void buttonEncrypt_Click(object sender, EventArgs e)
{
    rsaCipher = new RSACryptoServiceProvider();

    string publicKey = textBoxPublicKey.Text;
    rsaCipher.FromXmlString(publicKey);

    string plainText = textBoxPlain.Text;
    byte[] plainBytes = Encoding.ASCII.GetBytes(plainText);

    byte[] cipheredBytes = rsaCipher.Encrypt(plainBytes, true);
    string cipheredText = Convert.ToBase64String(cipheredBytes);

    textBoxCiphered.Text = cipheredText;
}

2.c Hashing

Hashing is the operation of taking the fingerprint of data. Thus, the purpose of this method is to produce a constant size unique key [from input data] that no different data can produce, thereby making it impossible to produce the data from the key. A very useful application of hashing is used in database systems. Think about a database that stores the passwords of users. Keeping the passwords clear is not as safe as keeping the hash values of passwords in tables. Even though any hash value in a database is enclosed, it is impossible to restore the original password from this value. This method provides the same functionality for authorization processes. The only difference is that the hash of the user's password is compared with the value in the database instead of comparing the password itself.

C#
//hash the input and display the result 
private void buttonComputeHash_Click(object sender, EventArgs e)
{
    byte[] input = Encoding.ASCII.GetBytes(textBoxInput.Text);

    HashAlgorithm Alg = GetSelectedAlgorithm();
    Alg.ComputeHash(input);

    textBoxHash.Text = Convert.ToBase64String(Alg.Hash);
}

private HashAlgorithm GetSelectedAlgorithm()
{
    //for keyed hashing algorithms only
    Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(textBoxKey.Text,
        Encoding.ASCII.GetBytes(textBoxSalt.Text));

    HashAlgorithm Alg = null;
    if (comboBoxAlgorithm.SelectedIndex == 0)
        Alg = new MD5CryptoServiceProvider();
    else if (comboBoxAlgorithm.SelectedIndex == 1)
        Alg = new RIPEMD160Managed();
    else if (comboBoxAlgorithm.SelectedIndex == 2)
        Alg = new SHA1CryptoServiceProvider();
    else if (comboBoxAlgorithm.SelectedIndex == 3)
        Alg = new SHA256Managed();
    else if (comboBoxAlgorithm.SelectedIndex == 4)
        Alg = new SHA384Managed();           
    else if (comboBoxAlgorithm.SelectedIndex == 5)
        Alg = new  SHA512Managed();
    else if (comboBoxAlgorithm.SelectedIndex == 6)
        Alg = new MACTripleDES(rfc.GetBytes(16));
    else if (comboBoxAlgorithm.SelectedIndex == 7)
        Alg = new HMACSHA1(rfc.GetBytes(29));

    return Alg;
}

2.d Digital Signature Applications [+]

Digital signature applications are based on two cryptographic methods: public key cryptosystem and hashing. These two are covered above, and we are now concerned with how to use these two in a simple application.

Think about a case where you want to send a message to your recipient in a way that s/he can certainly be sure that the message is sent by you and no one has modified the content during transmission. Sending the message with its hash will allow the recipient to check whether the hash belongs to the message or not. If the message is modified, the hash of the message will be the same as the attached hash. But what if someone else modifies both the message and the hash during transmission? Then we have to care about this case.

The figure below depicts the block diagram of a digital signature system which is constructed in two phases: signing document (left side) and verifying document (right side). Both phases use a public-key cryptosystem to encrypt and decrypt data. On the signing phase, the hash of the message is encrypted with the private key of the sender. This value (encrypted hash) becomes the signature and is sent to the recipient with the original message. Then, on the verifying phase, the recipient decrypts the signature (encrypted hash) with the public key of the sender. This operation yields the hash value which the sender has already computed. The recipient also computes the hash of the received message and compares it with the sender's hash. If these two hash values are the same, the documents are verified, otherwise not.

Block diagram of a digital signature application

Screenshot - DigitalSignatureViaRSA.png

3. Data Encryption Standard

DES has been the most widely used private-key cipher. It exhibits the classic Feistel structure which consists of a number of identical rounds of processing. DES uses 64-bit block and 54-bit key size. The key is used in both encryption and decryption. The encryption operation on DES follows the rounds on the figure.

Block diagram of DES encryption

Screenshot - DES_Structure.png

The figure depicts the encryption operation. However, just reversing the order of keys applied would yield the decryption operation. Before applying this figure, sixteen keys are generated from the secret key. These phases are explained next.

3.a Enciphering

Here is the algorithm for the encryption operation which is also depicted on the figure above. Three phases are the base of this algorithm: an initial permutation, an F() function, and a final permutation.

C#
//Algorithm for DES Encryption
private Bit[] Encrypt64Bit(Bit[] block)
{
    InitialPermutation(block);

    Bit[] Left = block[0 - 31];
    Bit[] Right = block[31 - 63];
    
    for (int i = 1; i <= 16; i++)
    {
        Temp = Left;
        Left = Right;
        Right = (Temp) Xor (F(Right, Keys[i - 1]));
    }
    block[0 - 31] = Right;
    block[32 - 63] = Left;

    RevInitialPermutation(block);

    return block;
}

3.a.1 Initial Permutation

This operation takes a 64 bit input and permutes a different block in the order of the IP table. For example, the first element of the IP table is 58, which means the first element of the new block has the 58th element of the input block. Thus, the second element of the new block has the 50th element of the input block and the last element of the new block has the 7th element of the input block.

C#
IP = new byte[8 * 8] {   58,    50,   42 ..... 15,    7 }; 

3.a.2 The Function F()

This method takes two parameters: the right block (32 bit) and the current key (48 bit). It first expands the right block in the order of E selection table which yields a 48-bit block. Then, applies the Exclusive-OR operation on this new block and the current key. Then, divides the XORed block into 8 pieces (the first 6 bits become the first piece, the second 6 bits become the second piece, and so on). Now, there are 8 blocks, each having 6 bits. Then, from each 6-bit block, a 4-bit block is produced according to the following rule:

(There are eight S tables namely S1, S2, S3... S8. The maximum element in these tables is 15, meaning that the maximum element's bit number is 4). Let the first 6 bits be abcdef, then compute B1 = S1[2*a+f, 8*b+4*c+2*d+e]. Let the second 6 bits be asdfgh, then compute B2 = S2[2*a+h, 8*s+4*d+2*f+g]. Let the eighth 6 bits be zxcvbn, then compute B8 = S8[2*z+n, 8*x+4*c+2*v+b].

Each one of these 8 blocks has 4 bits. This yields a 4*8=32 bit block (B1 B2 B3 B4 B5 B6 B7 B8) which is finally permuted in the order of P table as demonstrated in the following code segment:

C#
private BitArray F(BitArray R, BitArray K)
{
    R = Table(E, R);
    BitArray B = R.Xor(K);
    BitArray S = new BitArray(8 * 4);

    int x, y;
    BitArray Temp;
    for (int i = 0; i < 8; i++)
    {
        x = (B[i * 6 + 0] ? 2 : 0) + (B[i * 6 + 5] ? 1 : 0);
        y = (B[i * 6 + 1] ? 8 : 0) + (B[i * 6 + 2] ? 4 : 0) +
            (B[i * 6 + 3] ? 2 : 0) + (B[i * 6 + 4] ? 1 : 0);

        Temp = new BitArray(new byte[] { Ss[i, 16 * x + y] });
        Copy(Temp, 0, S, i * 4, 4);
    }

    S = Table(P, S);
    return S;
}
Block diagram of F function

Screenshot - F.png

3.a.3 XOR Operation

This method just takes two bit arrays and applies an Exclusive-OR operation to each bit of the input array. Then it stores the eXOR-ed bits to a new bit array. An implementation of this method would be like this:

C#
Bit[] Xor(Bit[] Left, Bit[] Right)
{
    Bit[] Result = new Bit[Right.Length];
    for (int i = 0; i < Right.Length; i++)
        Result[i] = Left[i] ^ Right[i];
    return Result;
}

3.a.4 Reversing the Initial Permutation

This method is just like the Initial Permutation method except that the input is permuted in the order of IP-1 table.

3.b Deciphering

The decryption algorithm on DES is similar to the encryption algorithm except that it applies the reverse order of keys as shown below:

C#
//Algorithm for DES Decryption
private Bit[] Decrypt64Bit(Bit[] block)
{
    InitialPermutation(block);

    Bit[] Left = block[0 - 31];
    Bit[] Right = block[31 - 63];

    for (int i = 1; i <= 16; i++)
    {
        Temp = Left;
        Left = Right;
        Right = (Temp)Xor(F(Right, Keys[16 - i]));
    }
    block[0 - 31] = Right;
    block[32 - 63] = Left;

    RevInitialPermutation(block);

    return block;
}

3.c Generating Keys

This operation takes a 56-bit key and produces 16 different 48-bit keys. First, the input key is permuted in the order of PC1 table, which yields a 56-bit block. Then, divide this new block into two parts, yielding two 28-bit blocks, namely C0 and D0. Then follows the 16 rounds. At each round, C(n+1) and D(n+1) are permuted by applying a left shift to C(n) and D(n). Finally, permuting (Cn)(Dn) in the order of PC-2 table yields the nth Key (C0 D0 in the order of PC-2 table yields the first key, C1 D1 in the order of PC-2 table yields the second key ... C15 D15 in the order of PC-2 table yields the fifteenth key). The number of left shifts differ at each round. This means C1 D1 is obtained from C0 D0 by one left shift, C2 D2 is obtained from C1 D1 by one left shift, C3 D3 is obtained from C2 D2 by two left shift, and so on. A single left shift means a rotation of the bits one place to the left, so that after one left shift, the bits in the 28 positions are the bits that were previously in positions 2, 3,..., 28, 1.

C#
//Number of left shifts at each round
LeftShifts = new byte[16] { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
Block diagram of the DES key generation

Screenshot - DES_key_generation.png

4. Advanced Encryption Standard

AES (known as Rijndael) is also a block cipher, but it does not use a Feistel structure. The block size of AES is 128-bit, but the key size may differ as 128, 192, or 256 bits.

Block diagram of AES encryption/decryption

Screenshot - AES_structure.png

4.a Enciphering

As the figure depicts, the encryption operation consists of four separate functions: byte substitution, permutation, arithmetic operations over a finite field, and XOR with a key.

C#
protected byte[] Encrypt128Bit(byte[] block)
{
    AddRoundKey(block, 0);
    //Nr=10,12 or 14 depending on key size
    for (int i = 1; i < Nr; i++)
    {
        SubBytes(block);
        ShiftRows(block);
        MixColumns(block);
        AddRoundKey(block, i);
    }
    SubBytes(block);
    ShiftRows(block);
    AddRoundKey(block, Nr);
    return block;
}

4.a.1 SubBytes()

This method substitutes each byte of the block in the order of Sbox. It provides an invertible transformation of blocks during encryption, with the reverse during decryption. Implementation of this method is very easy, as depicted:

C#
private void SubBytes(byte[] block)
{
    for (int i = 0; i < 16; i++)
    {
        block[i] = Sbox[i];
    }
}

4.a.2 ShiftRows()

The ShiftRows operation performs left circular shifts of rows 1, 2, and 3 by 1, 2 and 3, as depicted:

Screenshot - AES_ShiftRows.png

C#
void ShiftRows(byte[] state)
{
    byte[] t = new byte[4];
    for (int r = 1; r < 4; r++)
    {
        for (int c = 0; c < 4; c++)
            t[c] = state[r * 4 + ((c + r) % 4)];
        for (int c = 0; c < 4; c++)
            state[r * 4 + c] = t[c];
    }
}

4.a.3 MixColumns()

This method multiplies each column of the input block with a matrix. The multiplication operation is just like matrix multiplication, except that it uses a Finite Field to multiply two elements and performs an XOR operation instead of addition.

Screenshot - GF_Matrix.png

Here is the result for the multiplication of the column above (dots denote FF multiplication, pluses in circle denote eXOR operation):

Screenshot - GF_Res.png

C#
private void MixColumns(byte[] block)
{
    //temp=block
    byte[,] t = new byte[4, 4];
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            t[i, j] = block[i * 4 + j];
        }
    }
    for (int i = 0; i < 4; i++)
    {
        block[00 + i] = (byte)(M(2, t[0, i]) ^ M(3, t[1, i]) ^ t[2, i] ^ t[3, i]);
        block[04 + i] = (byte)(t[0, i] ^M(2, t[1, i])^ M(3, t[2, i]) ^  t[3, i] );
        block[08 + i] = (byte)(t[0, i] ^ t[1, i] ^ M(2, t[2, i]) ^ M(3, t[3, i]));
        block[12 + i] = (byte)(M(3, t[0, i]) ^ t[1, i] ^ t[2, i] ^ M(2, t[3, i]));
    }
}

4.a.4 AddRoundKey()

This operation just applies an eXOR operation to each byte of the input block and the current weight (key) matrix.

C#
private void AddRoundKey(byte[] block, int round)
{
    for (int i = 0; i < 16; i++)
    {
        block[i] = (byte)(block[i] ^ Keys[(round * 4 + i )]);
    }
}

4.b Deciphering

The following reverses the steps of the encryption algorithm with some modifications on the methods, yielding the decryption algorithm:

C#
protected byte[] Decrypt128Bit(byte[] block)
{
    AddRoundKey(block, Nr);
    //Nr=10,12 or 14 depending on key size
    for (int i = Nr - 1; i > 0; i--)
    {
        InvShiftRows(block);
        InvSubBytes(block);
        AddRoundKey(block, i);
        InvMixColumns(block);
    }
    InvShiftRows(block);
    InvSubBytes(block);
    AddRoundKey(block, 0);
    return block;
}

Here is how the method differs from the encryption operation:

  • The InvShiftRows operation performs right (left in ShiftRows) circular shifts of rows 1, 2, and 3 by 1, 2, and 3.
  • InvSubBytes differs from SubBytes as it uses InvSbox instead of Sbox.
  • InvMixColumns multiplies each column with a different matrix (available in the source).

4.c Generating Keys

This method is also known as key expansion. It takes a matrix in [4,Nk] (Nk = 4, 6, or 8) dimensions and expands it to [4,4*(Nr+1)] (Nr = 10, 12 or 14) dimensions. Here is the algorithm for this method as described in Fips-197:

VB
KeyExpansion(byte key[4*Nk], word w[Nb*(Nr+1)], Nk)
begin
    word temp
    i = 0
    while (i < Nk)
       w[i] = word(key[4*i], key[4*i+1], key[4*i+2], key[4*i+3])
       i = i+1
    end while
    i = Nk
    while (i < Nb * (Nr+1)]
       temp = w[i-1]
       if (i mod Nk = 0)
          temp = SubWord(RotWord(temp)) xor Rcon[i/Nk]
       else if (Nk > 6 and i mod Nk = 4)
          temp = SubWord(temp)
       end if
       w[i] = w[i-Nk] xor temp
       i = i + 1
    end while
end

5. References & External Links

6. History

  • October 28, 2007: Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Turkey Turkey
Has BS degree on computer science, working as software engineer in istanbul.

Comments and Discussions

 
GeneralError in encryption and decryption Pin
Lothar Behrens1-Oct-10 5:38
professionalLothar Behrens1-Oct-10 5:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.