With DES and 3DES we have a 56-bit or 112-bit encryption key, respectively. Both are block ciphers and based on the Feistel ciphers, with a 64-bit block size.
3DES with Bouncy Castle and C# |
Outline
IBM is a renowned world leader in cryptography, and is developing an amazing platform for trust with their Hyperledger project. The roots of their lead can be traced back to the creation of the Feistel cipher and which implements a symmetric key method.
In the 1960s, though, most of the cryptography research was conducted by governments, but IBM spotted a commercial opportunity and setup a cryptography research group in their Yorktown Heights, NY laboratory (and named after IBM’s founder — Thomas J. Watson Sr.). The lab went on to produce amazing advancements such as DRAM, the relational database and the FORTRAN programming language.
One of their best recruits was Horst Feistel, a physicist turned cryptographer, and who joined them in the 1970s. His work led to the creation of the Lucifer and DES (Data Encryption Standard) ciphers.
In the early 1970s, IBM patented the Lucifer cipher and which was then used by Lloyds Bank within some of the first ATM cash dispensers. After an evaluation by the NSA, Lucifer’s key size was reduced from 112 bits to 56 bits, after which it was published as the DES standard in 1975. DES then became manditory within its usage within US government electronic fund transfers, and went on to became a de-factor interational standard.
The Festel cipher
The Feistel cipher applies a symmetric key infrastructure and was named after Horst Feistel (IBM). It uses essentially the same encryption and decryption process, and where the key application is just reversed. The basic structure is given below and where we split the input data into blocks. Each block is then split into two (left and right). Each round is then:
\(L_{i+1} = R_i\)
\(R_{i+1} = L_i \oplus F(R_i,K_i)\)
The function applied (F) does not have to be reversible, which is unlike the case for AES. Also, in AES, we have an inverse function between the encryption and the decryption process, while a Feistel just applies the key in the reverse order.
A sample run is:
Input text: hello Key: qwerty Mode: ecb ============================== Cipher: 769e845b64e6f7fe Cipher: dp6EW2Tm9/4= Plain text: hello
As we have an input of 40 bits (5 x 8 bit characters), we will thus only fill one block. The cipher is 0x769e845b64e6f7fe, which is 16 hex values, and which gives 64 bits (16 x 4).
Code example
The following uses 64-bit block sizes [here] and with the operation of:
\(F(x,k) = {(x \times k)}^i \pmod {2^{32} -1} \)
\(L_i = R_{i-1}\)
\(R_i = L_{i-1} \oplus F(R_{i-1},k_i)\)
where \(i\) is the round number, and we have \(N\) rounds. In this case we will use eight rounds, and a 256 bit key. Each round will thus use part of the key.
Typical modes are ECB (Electronic Code Book) and CBC (Cipher Block Chain). ECB adds a counter value within each round, whereas CBC takes the output from a previous round and feeds into the present round. DES is most commonly used Feistel cipher. Format-preserving, Feistel-based encryption (FFX) is used in format-preserving encryption (FPE).
3-DES
One of the great problems with DES is that its key had been reduced to 56 bits, and which is easily cracked by brute force these days. And so 3-DES was created, and which used two keys (K1 and K2). The encryption process then becomes:
\(C=E_{K1}(D_{K2}(E_{K1}(P)))\)
and to decrpyt:
\(P=D_{K1}(E_K2(D_{K1}(C)))\)
and where \(E_{K1}\) is the encrpytion with \(K_1\), and \(D_{K1}\) is the decryption with \(K_1\). If you are interested, here’s how it all works:
Coding
First we create a folder named "bc_3des", and then go into that folder.We can create a Dotnet console project for .NET 8.0 with:
dotnet new console --framework net8.0
This produces a Csproject file of:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project>
We then add the latest Bouncy Castle library:
dotnet add package BouncyCastle.Cryptography --version 2.2.1
The following is the coding:
namespace ThreeDES { using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Paddings; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; class Program { static void Main(string[] args) { var msg="Hello"; var iv="00112233445566778899AABBCCDDEEFF00"; var size=128; var mode="CBC"; if (args.Length >0) msg=args[0]; if (args.Length >1) iv=args[1]; if (args.Length >2) size=Convert.ToInt32(args[2]); if (args.Length >3) mode=args[3]; try { var plainTextData=System.Text.Encoding.UTF8.GetBytes(msg); var cipher= new DesEdeEngine(); byte[] nonce = new byte[16]; Array.Copy(Convert.FromHexString(iv), nonce, 16); PaddedBufferedBlockCipher cipherMode = new PaddedBufferedBlockCipher(new CbcBlockCipher(cipher), new Pkcs7Padding()); if (mode=="ECB") cipherMode = new PaddedBufferedBlockCipher(new EcbBlockCipher(cipher), new Pkcs7Padding()); else if (mode=="CFB") cipherMode = new PaddedBufferedBlockCipher(new CfbBlockCipher (cipher,128 ), new Pkcs7Padding()); CipherKeyGenerator keyGen = new CipherKeyGenerator(); keyGen.Init(new KeyGenerationParameters(new SecureRandom(), size)); KeyParameter keyParam = keyGen.GenerateKeyParameter(); ICipherParameters keyParamIV = new ParametersWithIV(keyParam,nonce); if (mode=="ECB") { cipherMode.Init(true,keyParam); } else { cipherMode.Init(true,keyParamIV); } int outputSize = cipherMode.GetOutputSize(plainTextData.Length); byte[] cipherTextData = new byte[outputSize]; int result = cipherMode.ProcessBytes(plainTextData, 0, plainTextData.Length, cipherTextData, 0); cipherMode.DoFinal(cipherTextData, result); var rtn = cipherTextData; // Decrypt cipherMode.Init(false,keyParam); outputSize = cipherMode.GetOutputSize(cipherTextData.Length); plainTextData = new byte[outputSize]; result = cipherMode.ProcessBytes(cipherTextData, 0, cipherTextData.Length,plainTextData, 0); cipherMode.DoFinal(plainTextData, result); var pln=plainTextData; Console.WriteLine("=== {0} ==",cipher.AlgorithmName); Console.WriteLine("Message:\t\t{0}",msg); Console.WriteLine("Block size:\t\t{0} bits",cipher.GetBlockSize()*8); Console.WriteLine("Mode:\t\t\t{0}",mode); Console.WriteLine("IV:\t\t\t{0}",iv); Console.WriteLine("Key size:\t\t{0} bits",size); Console.WriteLine("Key:\t\t\t{0} [{1}]",Convert.ToHexString(keyParam.GetKey()),Convert.ToBase64String(keyParam.GetKey())); Console.WriteLine("\nCipher (hex):\t\t{0}",Convert.ToHexString(rtn)); Console.WriteLine("Cipher (Base64):\t{0}",Convert.ToBase64String(rtn)); Console.WriteLine("\nPlain:\t\t\t{0}",System.Text.Encoding.ASCII.GetString(pln).TrimEnd('\0')); } catch (Exception e) { Console.WriteLine("Error: {0}",e.Message); } } } }
A sample run is:
=== DESede == Message: Hello 123 Block size: 64 bits Mode: ECB IV: 00112233445566778899AABBCCDDEEFF00 Key size: 128 bits Key: EF9C147BC199C8A9BF9BD39715940DAE [75wUe8GZyKm/m9OXFZQNrg==] Cipher (hex): 05E6D021188087BF5FD09E559D796211 Cipher (Base64): BebQIRiAh79f0J5VnXliEQ== Plain: Hello 123