Cryptography remains an ever-important topic in building modern security-minded applications. Bouncy Castle is a cryptography library for .NET that allows users to build robust cryptography features. In this article, we will cover just a few important capabilities Bouncy Castle offers us and how to implement them.

To download the source code for this article, you can visit our GitHub repository.

Let’s get started!

What is Bouncy Castle?

Bouncy Castle is a free open-source .NET library offering a wide cryptography feature set for building security into applications. Bouncy Castle is developed by the Legion of the Bouncy Castle, an Australian Charity. This library offers a wide array of basic and advanced cryptographic algorithms. Additionally, it offers capabilities to generate and verify cryptographic certificates.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

To learn more about Bouncy Castle and the Legion of the Bouncy Castle visit their website here. For a refresher on cryptography refer to this article on CodeMaze.

Installing Bouncy Castle

We can install Bouncy Castle via the .NET command line:

dotnet add package BouncyCastle.Cryptography

Hashing With Bouncy Castle

Hashing functions are deterministic cryptographic algorithms that can map a data set down to a fixed-size byte array. This process is irreversible which lends itself well to validating data has not been changed without the risk of divulging the data itself. We can use Bouncy Castle to hash data using various algorithms such as MD5 and SHA.

One real-world use of hashing is a publisher providing the hashed value of the binary of an application. The user downloading these binaries can then verify the file(s) have not been altered in any way by verifying the hash provided by the publisher. Because hashing is deterministic we expect that performing the same hashing algorithm on these files will produce a matching hash to the one the publisher has provided.

Md5 Hashing

Let’s begin with performing an MD5 hash with Bouncy Castle:

var secretValue = "This is my pasword! Dont read me!";

var hash = Md5Hasher.Md5Hash(secretValue);

public static byte[] Md5Hash(string secret)
{
    var hash = new MD5Digest();

    var result = new byte[hash.GetDigestSize()];

    var data = Encoding.UTF8.GetBytes(secret);

    hash.BlockUpdate(data, 0, data.Length);

    hash.DoFinal(result, 0);

    return result;
}

To perform an MD5 hash, first, we create an MD5Digest object that we will use to create the hash. Next, we create a byte array, result, the size of the digest. We then convert the secret value to UTF-8 encoded byte array. Next, we update the hash digest object with a byte array. We do this by calling BlockUpdate() with blocks of bytes the size of our byte array. Finally, we call DoFinal() on the digest object to produce the final hash.

This will produce a hash of our secret:

U0e8NZgYzVdAFWH0+/Wwvw==

SHA Hashing

Let’s move on to SHA hashing. 

We can use Bouncy Castle to perform a SHA256 hash:

var secretValue = "This is my pasword! Dont read me!";

var hash = ShaHasher.ShaHash(secretValue);

public static byte[] ShaHash(string secret)
{
    var hash = new Sha256Digest();

    var result = new byte[hash.GetDigestSize()];

    var data = Encoding.UTF8.GetBytes(secret);

    hash.BlockUpdate(data, 0, data.Length);

    hash.DoFinal(result, 0);

    return result;
}

As we are using Bouncy Castle the code is very similar to creating an MD5 hash although they are different algorithms altogether. In this case, we create a Sha256Digest object to perform the hashing. Similarly, we convert the secret into a byte array. We then load the byte array in the hash block. Lastly, like before, we call DoFinal() on the digest object to produce our final hash.

This is an example of what a SHA256 hashed value would look like:

n0xnEbCTlQ66Un8K2G8XxvZPlWo0GizTISE/BTYkV3Q=

Symmetric Encryption with Bouncy Castle

In the following sections we will discuss how to perform specific symmetric encryption algorithms, but let’s discuss what options Bouncy Castle offers. Symmetric encryption always begins by creating a cipher object. We use the CipherUtilities.GetCipher("AlgorithmName/OperationMode/PaddingStyle") method to make this cipher object. This method accepts a string with three sections that are delimited by a forward slash. The first section describes the algorithm the cipher should use. The second section describes the operation mode for the cipher. Last, the final section describes which padding style the cipher should use.

Let’s take a look at the options we can choose from for each of these sections:

  1. Algorithm:

    • AES: "AES"
    • DES: "DES"
    • Triple DES (DESede): "DESede", "3DES"
    • Blowfish: "Blowfish"
    • CAST-5: "CAST5"
    • IDEA: "IDEA"
    • Camellia: "Camellia"
    • SEED: "SEED"
  2. Mode of Operation:

    • Electronic Codebook (ECB): "ECB"
    • Cipher Block Chaining (CBC): "CBC"
    • Cipher Feedback (CFB): "CFB"
    • Output Feedback (OFB): "OFB"
    • Counter (CTR): "CTR"
    • Galois/Counter Mode (GCM): "GCM"
  3. Padding Style:

    • NoPadding: "NoPadding"
    • PKCS5Padding: "PKCS5Padding"
    • PKCS7Padding: "PKCS7Padding"
    • ISO10126Padding: "ISO10126Padding"
    • ISO7816d4Padding: "ISO7816-4Padding"
    • TBCPadding: "TBCPadding"
    • X923Padding: "X9.23Padding"
    • ZeroBytePadding: "ZeroBytePadding"

Symmetric encryption, also known as private key encryption, is a class of encryption algorithms that use a single private key to perform encryption and decryption of a data set. This means the party that wants to decrypt data can only do so if they have obtained this key from whoever encrypted the data. Bouncy Castle allows us to easily use various symmetric encryption algorithms to protect data. In this section, we will discuss two well-known symmetric encryption algorithms: AES, and Blowfish.

AES Encryption

First, let’s take a look at a code example of how to perform AES encryption:

string input = "Hello, Bouncy Castle!";

var encryptedData = AesEncryptor.AesEncrypt(input, out byte[] iv, out byte[] key);

public static byte[] AesEncrypt(string input, out byte[] iv, out byte[] key)
{
    var inputBytes = Encoding.UTF8.GetBytes(input);

    // Generate a random 128-bit key
    key = new byte[16];
    var random = new SecureRandom();
    random.NextBytes(key);

    // Generate a random 128-bit initialization vector (IV)
    iv = new byte[16];
    random.NextBytes(iv);

    // Create AES cipher with CBC mode
    var cipher = CipherUtilities.GetCipher("AES/CBC/PKCS7Padding");

    cipher.Init(true, new ParametersWithIV(new KeyParameter(key), iv));

    return cipher.DoFinal(inputBytes);
}

We begin by transforming our input data set into a byte array using UTF8.GetBytes(). Next, we create two randomly generated byte arrays for the key and initialization vector (IV). Now that we have a key and IV, we generate a cipher object and initialize it with our key and IV. The first parameter of Init() is a boolean that indicates if we are encrypting. Finally, we call the DoFinal() method on the cipher object to produce the encrypted data.

Now that we have encrypted data, let’s take a look at how we can perform decryption using Bouncy Castle to recover the original data:

public static string AesDecrypt(byte[] key, byte[] iv, byte[] encryptedBytes)
{
    var cipher = CipherUtilities.GetCipher("AES/CBC/PKCS7Padding");
    cipher.Init(false, new ParametersWithIV(new KeyParameter(key), iv));

    // Decrypt the encrypted data
    var decryptedBytes = cipher.DoFinal(encryptedBytes);

    return Encoding.UTF8.GetString(decryptedBytes);
}

We start by creating a cipher object with the same algorithm and mode used to encrypt the data. This is very important because if the cipher is not the same the decryption would not work. Next, we initialize the cipher with the same IV and key as the encryption. Additionally, we provide a false value in the Init() method to indicate we will be decrypting.

Blowfish Encryption

Moving on, let’s take a look at how we can encrypt data using Blowfish:

var input = "Hello, Bouncy Castle!";
var password = "mysecretpassword";

var encryptedBytes = BlowfishEncryptor.BlowfishEncrypt(input, password, out byte[] iv);

public static byte[] BlowfishEncrypt(string input, string password, out byte[] iv)
{
    var inputBytes = Encoding.UTF8.GetBytes(input);

    var random = new SecureRandom();
    iv = new byte[8]; // Blowfish uses an 8-byte (64-bit) IV
    random.NextBytes(iv);

    var cipher = CipherUtilities.GetCipher("Blowfish/CBC/PKCS7Padding");

    var keyBytes = Encoding.UTF8.GetBytes(password);
    var keyParam = new KeyParameter(keyBytes);

    cipher.Init(true, new ParametersWithIV(keyParam, iv));

    return cipher.DoFinal(inputBytes);
}

Blowfish uses password-based encryption which means we use a password to generate a key. In the example, we generate a random IV and a key using the password as an input to the KeyParameter() constructor. Next, we create a cipher object using GetCipher() and we specify we want to use Blowfish with CBC mode and PKCS7 padding. Again, we initialize the cipher set to encryption. Finally, we produce the final encryption by calling DoFinal().

Lastly, let’s take a look at how we can decrypt a data set we encrypted using Blowfish:

public static string BlowfishDecrypt(byte[] encryptedBytes, string password, byte[] iv)
{
    var cipher = CipherUtilities.GetCipher("Blowfish/CBC/PKCS7Padding");

    var keyBytes = Encoding.UTF8.GetBytes(password);
    var keyParam = new KeyParameter(keyBytes);

    cipher.Init(false, new ParametersWithIV(keyParam, iv));

    var inputBytes = cipher.DoFinal(encryptedBytes);

    return Encoding.UTF8.GetString(inputBytes);
}

To perform the decryption, we initialize a new cipher with the same settings as we did when it was encrypted and set it to decryption mode. Next, we generate another KeyParameter object using the password byte array. Finally, we call DoFinal() to receive our original data back.

Asymmetric Encryption With Bouncy Castle

Asymmetric encryption is also known as public key encryption. They are a class of encryption algorithms that use a public key to encrypt data and a private key to perform decryption of a data set. The public and private keys are mathematically related. Only a private key related to the public key can decrypt the data set. Bouncy Castle enables us to perform asymmetric encryptions. In the following section, we will cover two asymmetric encryption algorithms: RSA, and DSA. It should be noted that DSA is typically used to digitally sign files as a security measure to prove files have not been tempered while exchanging between two parties.

RSA Encryption

Here is how we can perform RSA encryption using Bouncy Castle:

string input = "Hello, Bouncy Castle!";

// Generate RSA key pair
var keyPair = RsaEncryptor.GenerateRsaKeyPair();

var encryptedBytes = RsaEncryptor.RsaEncrypt(input, keyPair.Public);

public static AsymmetricCipherKeyPair GenerateRsaKeyPair()
{
    var rsaKeyPairGen = new RsaKeyPairGenerator();
    rsaKeyPairGen.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
    return rsaKeyPairGen.GenerateKeyPair();
}

public static byte[] RsaEncrypt(string input, AsymmetricKeyParameter publicKey)
{
    var inputBytes = Encoding.UTF8.GetBytes(input);

    var cipher = new Pkcs1Encoding(new RsaEngine());

    cipher.Init(true, publicKey);

    return cipher.ProcessBlock(inputBytes, 0, inputBytes.Length);
}

Here we see the pattern for asymmetric encryption is different from symmetric encryption. First, we generate a key pair by creating a RsaKeyPairGenerator object. We initialize it with a random number generator of type SecureRandom and an int indicating the strength of the encryption using a bit length number. Lastly, we call GenerateKeyPair() to produce the private and public keys.

Now that we have a key pair, we create a cipher object by providing an RsaEngine object to a PKCS1Encoding object. Next, we initialize the cipher with the encryption mode as true and the public key we generated. Finally, we call ProcessBlock() with our input byte array to encrypt, an offset value for where to start reading our input, and the length of the input. This will produce an encrypted byte array representation of our input data.

Now, let’s discuss how we can recover our data through decryption:

string decryptedString = RsaEncryptor.RsaDecrypt(encryptedBytes, keyPair.Private);

public static string RsaDecrypt(byte[] encryptedBytes, AsymmetricKeyParameter privateKey)
{
    var cipher = new Pkcs1Encoding(new RsaEngine());

    // Initialize the cipher for decryption with the private key
    cipher.Init(false, privateKey);

    // Decrypt the encrypted data
    var decryptedBytes = cipher.ProcessBlock(encryptedBytes, 0, encryptedBytes.Length);

    return Encoding.UTF8.GetString(decryptedBytes);
}

Again, we create a cipher with the same algorithm engine object and padding style as used in encryption. Next, we initialize the cipher in decryption mode and pass in the private key this time. Now, we call the ProcessBlock() method again to produce the original byte array data.

Creating Digital Signatures

Another application of asymmetric encryption is digital signatures used for secure file transfers. In this section, we will use the DSA algorithm to achieve this goal.

Let’s start by looking at an example of how we can digitally sign a byte array:

string input = "Hello, Bouncy Castle!";

var keyPair = DsaEncryptor.GenerateDsaKeyPair();

var signature = DsaEncryptor.DsaSign(input, keyPair.Private);

public static AsymmetricCipherKeyPair GenerateDsaKeyPair()
{
    // Create DSA parameters
    var dsaParamsGenerator = new DsaParametersGenerator();
    var random = new SecureRandom();
    dsaParamsGenerator.Init(1024, 80, random); // key size: 1024 bits, certainty: 80

    // Generate DSA key pair
    var dsaParams = dsaParamsGenerator.GenerateParameters();
    var dsaKeyParams = new DsaKeyGenerationParameters(random, dsaParams);
    var dsaKeyPairGen = new DsaKeyPairGenerator();
    dsaKeyPairGen.Init(dsaKeyParams);

    return dsaKeyPairGen.GenerateKeyPair();
}

public static byte[] DsaSign(string message, AsymmetricKeyParameter privateKey)
{
    var messageBytes = Encoding.UTF8.GetBytes(message);

    var signer = SignerUtilities.GetSigner("SHA256withDSA");

    signer.Init(true, privateKey);

    signer.BlockUpdate(messageBytes, 0, messageBytes.Length);

    return signer.GenerateSignature();
}

In this example, we are signing a byte array produced from a string, but in a practical example, we would read in a file as a byte array and perform all the same operations on it.

Firstly, we start by creating a key pair we can use for signing. Create a DsaParametersGenerator object and initialize it with a secure random number generator, key size, and a certainty value. Next, we generate the parameters by calling GenerateParameters() and pass them into the constructor of DsaKeyGenerationParameters. We follow this up by creating a DsaKeyPairGenerator object which we initialize with the previously created DsaKeyGenerationParameters. Lastly, we generate the keys by calling GenerateKeyPair().

Now that we have the DSA keys we can begin the signing process. Starting off, we create an ISigner implementation by calling SignerUtilities.GetSigner("SHA256withDSA"). We initialize the signer by calling Init() and indicating we are signing with the true value and our private key. Once we have initialized the signer, we call BlockUpdate() to pass in our data, an offset value, and the data array length. We produce the signature by calling GenerateSignature().  It is important to note that the original data has not been obscured.

Validating Digital Signatures

We can also verify the signature on the data using Bouncy Castle:

var isSignatureValid = DsaEncryptor.DsaVerify(input, signature, keyPair.Public);

public static bool DsaVerify(string message, byte[] signature, AsymmetricKeyParameter publicKey)
{
    var messageBytes = Encoding.UTF8.GetBytes(message);

    var signer = SignerUtilities.GetSigner("SHA256withDSA");

    signer.Init(false, publicKey);

    signer.BlockUpdate(messageBytes, 0, messageBytes.Length);

    return signer.VerifySignature(signature);
}

Begin by creating another ISigner implementation with the same parameters as the one used to produce the signature. We initialize it with a false value and pass in the public key we generated before. We call BlockUpdate() on the signed passing in the input data, an offset, and the data array length. Lastly, we verify the signature by calling VerifySignature() and passing in the signature we produced.

Conclusion

Bouncy Castle allows us to perform a variety of cryptographic operations with an easy-to-use and consistent syntax. Data security remains an ever-important aspect of development. This is a great library to enable data security in applications!

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!