PBKDF2 (Password-Based Key Derivation Function 2) is defined in RFC 2898 and generates a salted hash. Often this is used to create an encryption key from a defined password, and where it is not possible to reverse the password from the hashed value. It is used in TrueCrypt to generate the key required to read the header information of the encrypted drive, and which stores the encryption keys. Also, it is used in WPA-2 in order to create a hashed version of the password. With this, WPA-2 uses 4,096 interations. The code used to create these programs must thus be free of flaws and often have to comply with the Federal Information Processing Standard (FIPS) standard. In this case, we will use the Bouncy Castle FIPS (FIPS) C# API. This needs the bc-fips-1.0.2.dll assembly and which integrates into the System.Security.Cryptography provider.
PBKDF2 with C# with FIPS integration |
Output
PBKDF2 is used in WPA-2 and TrueCrypt. Its main focus is to produced a hashed version of a password, and includes a salt to reduce the opportunity for a rainbow table attack. It generally uses over 1,000 iterations in order to slow down the creation of the hash, so that it can overcome brute force attacks. The generalise format for PBKDF2 is:
DK = PBKDF2(Password, Salt, Miterations, dkLen)
Where Password is the pass phrase, Salt is the salt, Miterations is the number of iterations, and dklen is the length of the derived hash.
WPA-2
The IEEE 802.11i standard defines that the pre-shared key is defined by:
PSK = PBKDF2(PassPhrase, ssid, ssidLength, 4096, 256)
Presentation
Explanation
In TrueCrypt we use PBKDF2 to generate the key (with salt) and which will decrypt the header, and reveal the keys which have been used to encrypt the disk (using AES, 3DES or Twofish):
We use:
byte[] result = passwordDerive.GenerateDerivedKey(16, ASCIIEncoding.UTF8.GetBytes(message), salt, 1000);
which has a key length of 16 bytes (128 bits - dklen), uses a salt byte array, and 1000 iterations of the hash (Miterations). You should find that the resulting hash value will have 32 hexadecimal characters (16 bytes).
First we create a folder named "bc_fips01", and then go into that folder.We can create a Dotnet console project for .NET 8.0 with:
dotnet new console --framework net8.0
Next we download the FIPS module (bc-fips-1.0.2.dll) from [here] and add it as a reference to the project:
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> <ItemGroup> <Reference Include="Org.BouncyCastle.Crypto"> <HintPath>bc-fips-1.0.2.dll</HintPath> </Reference> </ItemGroup> </Project>
Coding
The following is the coding:
namespace PB { using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Fips; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Security; class Program { static void Main(string[] args) { var noBytes=32; var password="Hello"; var salt="00"; var hash="sha256"; var iterations=1024; if (args.Length >0) password=args[0]; if (args.Length >1) hash=args[1]; if (args.Length >2) salt=args[2]; if (args.Length >3) iterations=Convert.ToInt16(args[3]); if (args.Length >4) noBytes=Convert.ToInt16(args[4]); try { var hashmethod=FipsShs.Sha256; if (hash=="SHA224") hashmethod=FipsShs.Sha224; if (hash=="SHA1") hashmethod=FipsShs.Sha1; if (hash=="SHA384") hashmethod=FipsShs.Sha384; if (hash=="SHA512") hashmethod=FipsShs.Sha512; if (hash=="SHA3_256") hashmethod=FipsShs.Sha3_256; if (hash=="SHA3_512") hashmethod=FipsShs.Sha3_512; IPasswordBasedDeriverBuilderpbeDeriverBuilder =CryptoServicesRegistrar.CreateService(FipsPbkd.PbkdF2).From(System.Text.Encoding.ASCII.GetBytes( password)); IPasswordBasedDeriver pbeDeriver =pbeDeriverBuilder.WithPrf(hashmethod).WithSalt(Org.BouncyCastle.Utilities.Encoders.Hex.Decode("")).WithIterationCount(iterations).Build(); var pb= pbeDeriver.DeriveKey(TargetKeyType.CIPHER, (noBytes)); Console.WriteLine("Password: {0}",password); Console.WriteLine("Salt: {0}",salt); Console.WriteLine("Iterations: {0}",iterations); Console.WriteLine("Hash: {0}",hash); Console.WriteLine("PBKDF2: {0}",Convert.ToHexString(pb)); Console.WriteLine("PBKDF2: {0}",Convert.ToBase64String(pb)); } catch (Exception e) { Console.WriteLine("Error: {0}",e.Message); } } } }
A sample run shows:
Password: Qwerty123 Salt: 00 Iterations: 1000 Hash: SHA256 PBKDF2: 91BABF4E480231AEA3AFE1DF286D08A412D06961E8DD02196B9B8FA6BD6CC86F PBKDF2: kbq/TkgCMa6jr+HfKG0IpBLQaWHo3QIZa5uPpr1syG8=