Symmetric Cryptography

Block Ciphers

A block cipher transforms the plaintext data represented as input blocks into blocks of the ciphertext. Blocks have a fixed length. The transformation is based on a secret key:

E (encryption), E-1 (decryption) - paired block cipher operations

M - fixed-length blocks of the plaintext

C - blocks of the ciphertext

k - secret key

If an input message does not have enough bytes to build a valid block, it is padded: padding bytes are appended to the message to make a multiple of the required block size.

A mode of operation defines how exactly a block cipher algorithm is applied to the plaintext. The standard modes are

  • ECB - Electronic Code Book; this is the least secure mode: each plaintext block is encrypted separately and independently of the ciphertext;
  • CBC - Cipher Block Chaining: each plaintext block is XORed with the previous ciphertext block and then encrypted; the first operation will require a "seed" value - an initialization vector;
  • CFB - Cipher Feedback: the previous ciphertext is encrypted and the output is XORed with the plaintext to produce the current ciphertext block;
  • OFB - Output Feedback: an initialization vector is used to generate the first output block; the block is XORed with the first plaintext block to produce the ciphertext; the successive output blocks are derived from applying the cipher function to the previous output blocks, and the output blocks are XORed with the plaintext blocks to create the ciphertext;
  • CTR - Counter mode; it is based on applying a secret key to a set of input blocks called counters to produce a sequence of output blocks; such blocks are then XORed with the plaintext to generate the ciphertext;
  • GCM - Galois/Counter mode; GCM provides authenticated encryption of the input message: the plaintext is encrypted into the ciphertext, then an authentication tag is generated on the ciphertext and the additional authenticated data (AAD);
  • CCM - Counter with Cipher Block Chaining-Message Authentication Code; this is another example of authenticated encryption providing both authentication and confidentiality of security-sensitive information: cipher block chaining is applied to the payload, the associated data, and the nonce to generate a MAC, then counter mode encryption is applied to the MAC and the payload to transform them into the ciphertext;
  • CTS - Cipher Text Stealing mode enabling a block cipher to encrypt the plaintext consisting of both complete and partial blocks;
  • XTS - XEX Tweakable Block Cipher with Ciphertext Stealing; the mode is an instance of XEX (XOR Encrypt XOR) block cipher supplemented with CTS; XTS is designed for the cryptographic protection of confidential data on storage devices.

Desktop Tools

Block ciphers are extensively implemented in software. OpenSSL and LibreSSL support AES, Blowfish, Camellia, CAST, DES, Triple DES, IDEA, RC2 and SEED algorithms. The keytool utility for Java developers create keys for AES, Blowfish, DES, Triple DES, RC2 and RC5 ciphers. If keytool is launched with the BouncyCastle provider, secret key entries can be founded on such algorithms as Camellia, CAST, GOST, IDEA, NOEKEON, RC6, SEED, Serpent, SHACAL, Skipjack, TEA, Threefish, Twofish and XTEA.

Besides keytool, Java developers can use KeyczarTool: Keyczar provides both a command-line instrument for keys management and a simple API for data encryption.

Examples in the current article will be based on the AES algorithm. In 1997 the National Institute of Standards and Technology announced the Advanced Encryption Standard initiative to create a modern successor to DES. In 1999 five algorithms were selected as AES finalists. A year later NIST chose the Rijndael algorithm as the basis for AES. Rijndael enables the handling of various block and key sizes; AES, however, is restricted to the block size of 128 bits and key lengths of 128, 192, or 256 bits.

OpenSSL

To perform block encryption, OpenSSL launches the same enc command that is employed with stream ciphers:

openssl enc -AES-256-CBC -in plaintext.pdf -out ciphertext.b64 -pass pass:secret -e -base64 -p

The AES cipher in the example above operates in the CBC mode and uses a 256-bit key. The key is a result of the string-to-key conversion: the user-supplied 'secret' is a passphrase that is combined with a randomly generated salt to produce the secret key. A 16-byte initialization vector is also created. The ciphertext is encoded as a Base64 sequence.

There are a few ways of passing passphrases to the enc command, e.g. the password can be read from a file or obtained from an environment variable:

openssl enc -AES-256-CFB -in plaintext.docx -out ciphertext.b64 -pass file:secret.txt -e -a -p

This time the -pass option uses the file argument: it denotes a path where a file with the password is located. The first line in the file is the passphrase necessary to produce the secret key. The ciphertext is encoded in Base64 again: the -a option has the same effect as the -base64.

Declaring environment variables are platform-specific:

Linux variable
export passphrase=secret

Windows variable
set passphrase=secret

The variable should be passed to the env argument of the -pass option:

openssl enc -AES-256-CFB1 -in plaintext.xlsx -out ciphertext.b64 -pass env:passphrase -e -a -p

A block cipher in CFB and OFB modes can handle data units which lengths are not multiples of the cipher block size. Declaring the amount of bits to be processed actually transforms a block cipher into a bit-oriented stream cipher: AES CFB1 mode in the example above performs encryption of 1 bit at a time.

The -d option decrypts the ciphertext:

openssl enc -AES-256-CBC -in ciphertext.b64 -out plaintext.pdf -pass pass:secret -d -base64

keytool

The -genseckey options for generating an AES secret key are similar to those we have already used with ChaCha stream cipher:

keytool -genseckey -alias AES -keyalg AES -keysize 256 -storetype bks -keystore keys.bks -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider

Key entry details are extracted from the store by launching the -list command:

keytool -list -alias AES -storetype bks -keystore keys.bks -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider -v

keyczar

Keyczar utility is implemented as a JAR file with the org.keyczar.KeyczarTool main class.

An empty keyset is generated by calling the create command:

java -jar KeyczarTool.jar create --location=keys --purpose=crypt --name="Demo AES key"

The --location option specifies a directory where human-readable files with AES keys and metadata will be deployed. The crypt purpose tells Keyczar to prepare the keyset for symmetric encryption.

A real key is appended to the existing keyset by invoking the addkey command:

java -jar KeyczarTool.jar addkey --location=keys --status=primary --size=256

Keyczar keys can be primary, active or inactive. A primary key is designed for generating new cryptographic material: AES primary keys are passed to the encryption engine. Keys of all status values are able to decrypt the ciphertext. This mechanism provides proper key rotation. The default status is active.

If the key is created successfully, the key file is placed in the directory storing the keyset. The file exposes cryptographic parameters as a JSON object:

{
 "aesKeyString": "9-6SNuCA-PGR7Wye_iPi6lotN4Z7700LoqOByJPy2Z4",
 "hmacKey": {
  "hmacKeyString": "bmaqCsVfQC6tvHRIYlixYPGOQ03vosfimwOU-buqQvQ",
  "size": 256
 },
 "mode": "CBC",
 "size": 256
}

The aesKeyString represents key material as a string transformed according to the Base64 encoding with an URL and filename safe alphabet: the 62nd character of the alphabet is - (minus), and the 63rd character is _ (underline). This type of encoding is often referred to as base64url. In Keyczar documentation it is called WebSafeBase64.

Keys can be promoted and demoted:

# the key status value will be ACTIVE
java -jar KeyczarTool.jar demote --location=keys --version=1

# the key will be INACTIVE
java -jar KeyczarTool.jar demote --location=keys --version=1

# the demoted key will be destroyed
java -jar KeyczarTool.jar revoke --location=keys --version=1

# generating a new key which is ACTIVE by default
java -jar KeyczarTool.jar addkey --location=keys --size=256

# the active key is promoted to PRIMARY status
java -jar KeyczarTool.jar promote --location=keys --version=1


Programming Languages

Core libraries of PHP, Java and C# support main block cipher algorithms. A PHP application generally uses Mcrypt and OpenSSL extensions. Java symmetric encryption is based on the Cipher engine class: a Cipher processes cryptographic material directly or is used in combination with cipher streams and sealed objects. The .NET Framework cryptography model follows a three-tier class inheritance: abstract levels are an algorithm type class and an algorithm class; a working implementation of the given algorithm inherits its API from the abstract classes.

PHP

PHP OpenSSL extension only has a few lightweight functions dealing with symmetric encryption. Mcrypt routines are more numerous and elaborate. The extension declares its algorithms and modes as a list of predefined constants: e.g. Rijndael is represented as MCRYPT_RIJNDAEL_128, MCRYPT_RIJNDAEL_192 or MCRYPT_RIJNDAEL_256. Information about all ciphers and their main characteristics is extracted by calling the mcrypt_list_algorithms() function:

<?php
 $ciphers = mcrypt_list_algorithms();
 asort($ciphers);
 foreach ($ciphers as $cipher) {
  if(mcrypt_module_is_block_algorithm($cipher) == TRUE) {
   $blocksize = mcrypt_module_get_algo_block_size($cipher);
   echo "$cipher block size is $blocksize bytes\n";
   $keysizes = mcrypt_module_get_supported_key_sizes($cipher);
   $keysize = mcrypt_module_get_algo_key_size($cipher);
   if(count($keysizes) == 0) {
    echo "the cipher has a variable key length up to $keysize bytes";
   }
   if(count($keysizes) == 1) {
    echo "$cipher key size is $keysize bytes";
   }
   if(count($keysizes) > 1) {
    echo "allowed key sizes in bytes: ";
    foreach ($keysizes as $ks) {
     echo "$ks ";
    }
   }
   echo "\n\n";
  }
 }
?>

MCrypt Functions

The most straightforward way to encipher data is to use the mcrypt_encrypt() function. Its parameters are the name of the block cipher, the secret key, the plaintext as a string, the mode of the cipher, and the initialization vector. The key can be derived from a passphrase by calling the mhash_keygen_s2k():

<?php
 string-to-key conversion
 $salt = openssl_random_pseudo_bytes(8);
 $keysize = mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_OFB);
 $key = mhash_keygen_s2k(MHASH_SHA256, 'passphrase', $salt, $keysize);

 IV generation
 $ivsize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_OFB);
 $iv = mcrypt_create_iv($ivsize);

 getting the plaintext
 $p = fopen('plaintext.pdf', 'rb');
 $plaintext = fread($p, filesize('plaintext.pdf'));
 fclose($p);

 encryption
 $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plaintext, MCRYPT_MODE_OFB, $iv);

 saving the ciphertext to a file
 $c = fopen('ciphertext.dat', 'wb');
 fwrite($c, $ciphertext);
 fclose($c);
?>

Such auxiliary functions as mcrypt_get_key_size(), mcrypt_get_iv_size() and mcrypt_create_iv() allow the developer to choose correct values of cryptographic parameters. The same parameters must be used for decryption:

$plaintext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $ciphertext, MCRYPT_MODE_OFB, $iv);

Another group of Mcrypt functions is based on the mode-based approach to encryption: these are mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb() and mcrypt_ofb().

encryption
$ciphertext = mcrypt_cfb(MCRYPT_RIJNDAEL_256, $key, $plaintext, MCRYPT_ENCRYPT, $iv);

decryption
$plaintext = mcrypt_cfb(MCRYPT_RIJNDAEL_256, $key, $ciphertext, MCRYPT_DECRYPT, $iv);

A more sophisticated way to perform symmetric encryption follows the pattern of the native mcrypt library:

<?php
 opening the module of the Rijndael algorithm
 $descriptor = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CFB,'');

 key and IV generation
 $salt = openssl_random_pseudo_bytes(8);
 $keysize = mcrypt_enc_get_key_size($descriptor);
 $key = mhash_keygen_s2k(MHASH_SHA256, 'passphrase', $salt, $keysize);
 $ivsize = mcrypt_enc_get_iv_size($descriptor);
 $iv = mcrypt_create_iv($ivsize);

 buffers initialization
 mcrypt_generic_init($descriptor, $key, $iv);

 preparing the plaintext
 $p = fopen('plaintext.docx', 'rb');
 $plaintext = fread($p, filesize('plaintext.docx'));
 fclose($p);

 encryption
 $ciphertext = mcrypt_generic($descriptor, $plaintext);

 saving the ciphertext
 $c = fopen('ciphertext.dat', 'wb');
 fwrite($c, $ciphertext);
 fclose($c);

 clearing all buffers
 mcrypt_generic_deinit($descriptor);
 closing the module
 mcrypt_module_close($descriptor);
?>

The mcrypt_module_open() function opens the module containing implementation of the requested algorithm. The algorithm name is the first argument passed to the function. The second argument is the directory where the module is located. Two more arguments define the cipher mode and the mode directory.

If no directory is specified, then the default value is applied: the value is set by the mcrypt.algorithms_dir directive from the PHP initialization file.

Calling mcrypt_module_open() creates an encryption descriptor - a resource that is referred to by other routines. The mcrypt_generic_init() initializes all buffers required for encryption, then the mcrypt_generic() function produces the ciphertext. When the application does not need the module any more, the memory buffers are cleared and the module is closed.

Decipherment depends on the mdecrypt_generic() function. It should be noted that the routine returns the plaintext padded with "\0", so to restore the original data the rtrim() function must be called:

<?php
 $descriptor = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CFB,'');
 $key = base64_decode('. . . key material encoded as Base64 sequence . . .');
 $iv = base64_decode(. . . IV bytes encoded as Base64 sequence . . .');
 mcrypt_generic_init($descriptor, $key, $iv);
 $c = fopen('ciphertext.dat', 'rb');
 $ciphertext = fread($c, filesize('ciphertext.dat'));
 fclose($c);
 $plaintext = mdecrypt_generic($descriptor , $ciphertext);
 $p = fopen('plaintext.docx', 'wb');
 fwrite($p, rtrim($plaintext, "\0"));
 fclose($p);
 mcrypt_generic_deinit($descriptor);
 mcrypt_module_close($descriptor);
?>

OpenSSL

Names of OpenSSL ciphers and their aliases available to a PHP application are retrieved by the openssl_get_cipher_methods() function:

// ciphers without aliases
$ciphers = openssl_get_cipher_methods();

// aliases are included in the returned array
$ciphers_and_aliases = openssl_get_cipher_methods(true);

// algorithm aliases
$aliases = array_diff($ciphers_and_aliases, $ciphers);

The openssl_cipher_iv_length() function returns the length of a block cipher's initialization vector:

echo openssl_cipher_iv_length('AES-256-OFB');

Encipherment of the plaintext is provided by the openssl_encrypt() function:

<?php
 $algorithm = 'AES-256-OFB';
 $key = base64_decode('. . . key material encoded as Base64 sequence . . .');
 $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($algorithm));
 $p = fopen('plaintext.xlsx', 'rb');
 $plaintext = fread($p, filesize('plaintext.xlsx'));
 fclose($p);
 $ciphertext = openssl_encrypt($plaintext, $algorithm, $key, TRUE, $iv);
 $c = fopen('ciphertext.dat', 'wb');
 fwrite($c, $ciphertext);
 fclose($c);
?>

The openssl_decrypt() function takes the raw or Base64-encoded ciphertext and decrypts it:

$recovered = openssl_decrypt ($ciphertext, 'AES-256-OFB', $key, TRUE, $iv);

Java

Similar to stream cipher encryption, the focal point of block-based symmetric cryptography in Java is the Cipher class. Its instantiation for a block algorithm, however, is more complicated: the name of the algorithm passed to the getInstance() method may be followed by a mode and a padding scheme.

Single-Part Encryption

Single-part operations encrypting and decrypting data are performed by calling the doFinal() method. The example below creates a 32-byte AES key, fills a 16-byte IV with random values and enciphers a line of the plaintext:

KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();
SecureRandom secureRandom = new SecureRandom();
byte[] iv = new byte[16];
secureRandom.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
byte[] ciphertext = cipher.doFinal("plaintext".getBytes());

The transformation string passed to the factory method of the Cipher assumes the form of algorithm/mode/padding. The PKCS5Padding is the scheme expounded in the PKCS #5 cryptographic standard. The scheme was originally developed for block ciphers that used a 64-bit block size. PKCS #7, or Cryptographic Message Syntax Standard, extended the padding technique to support ciphers with any block size, so PKCS #5 and PKCS #7 paddings can be employed interchangeably.

Plaintext recovery requires re-initialization of the AES engine:

cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] plaintext = cipher.doFinal(ciphertext);

Multiple-Part Encryption

Single-part operations are convenient for encrypting a string or a small-sized file. Processing a large file, however, should be accompanied with incremental updates of the cipher engine:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);

try(BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("plaintext.pdf"))) {
 try(BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("ciphertext.dat"))) {
  int i;
  byte[] buffer = new byte[8192];
  while((i = inputStream.read(buffer)) != -1) {
   byte[] ciphertext = cipher.update(buffer, 0, i);
   if((ciphertext != null) && (ciphertext.length > 0)) {
    outputStream.write(ciphertext);
   }
  }
  byte[] finalBlock = cipher.doFinal();
  outputStream.write(finalBlock);
 }
}

Encryption With Cipher Streams

Java cipher streams subclassing filter input/output streams are efficiently used to encrypt and decrypt files, network traffic, or database Blobs. Let's suppose a Java application has obtained a banking report with commercial secrets from an HTTPS server. The report was protected by means of TLS while it was being downloaded. However, it may be necessary to maintain confidentiality of the document after the download: if the local system gets infected with a virus, security-sensitive data stored as a plain text can be read by a malware application and sent to the command server of a malware author. Besides, if the local machine is shared by several users, an unauthorized person is able to get access to unprotected information. Finally, the user can lose the physical control of the client computer and thus expose secret data to other people.

The code below will perform network and cryptography operations to download a Web asset and save it locally as the ciphertext. The example will also demonstrate the creation of a new keystore in Java. For brevity, we do not provide the code with exception handling and stream closing routines.

The first step is the creation of a secret key:

KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();

The key will be placed in a new keystore:

String entryName = "secret-key";
File keyStoreFile = new File("keys.bks");
BufferedOutputStream keyStoreStream = new BufferedOutputStream(new FileOutputStream(keyStoreFile));
KeyStore keyStore = KeyStore.getInstance("BKS", "BC");
keyStore.load(null, null);

The load() with null parameters creates an empty keystore. Then a SecretKeyEntry is placed in the store:

KeyStore.SecretKeyEntry keyEntry = new KeyStore.SecretKeyEntry(secretKey);
PasswordProtection protection = new PasswordProtection("entry_password".toCharArray());
keyStore.setEntry(entryName, keyEntry, protection);

Now the keystore can be protected with a passphrase and saved to the keys.bks file:

keyStore.store(keyStoreStream, "keystore_password".toCharArray());

Then an IV of 16 bytes is created:

SecureRandom secureRandom = new SecureRandom();
byte[] iv = new byte[16];
secureRandom.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

Generated cryptographic material is used for initializing a Cipher which is passed to the CipherOutputStream constructor:

File encryptedFile = new File("report.xml.nc");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(encryptedFile));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
CipherOutputStream cipherOutputStream = new CipherOutputStream(bufferedOutputStream, cipher);

A network resource with confidential information is retrieved by using an instance of HttpsURLConnection:

System.setProperty("javax.net.ssl.trustStore", "trusted.jks");
URL url = new URL("https://insiders.example.com/business/report.xml");
URLConnection urlConnection = url.openConnection();
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) urlConnection;
httpsURLConnection.setDoInput(true);
BufferedInputStream bufferedInputStream = new BufferedInputStream(httpsURLConnection.getInputStream());

Network bytes are read by the BufferedInputStream, encrypted by the CipherOutputStream and written to the report.xml.nc file:

int i = 0;
byte[] buffer = new byte[8192];
while((i = bufferedInputStream.read(buffer)) != -1) {
 cipherOutputStream.write(buffer, 0, i);
}

After flushing and closing all streams, the HTTPS connection can be terminated:

httpsURLConnection.disconnect();

Keyczar API

Keyczar API is a simple level of abstraction built upon the standard Java Cryptography Architecture. The org.keyczar package contains two public classes for symmetric encryption: these are Encrypter and Crypter. An Encrypter is only utilized to encipher the plaintext data. Crypter methods enable both encryption and decryption.

An Encrypter object is initialized with a keyset containing a primary AES key:

Encrypter encrypter = new Encrypter("keys");

The toString() method returns the contents of the meta file in the keys directory:

String meta = encrypter.toString();

Enciphering the plaintext is a single-part operation:

byte[] ciphertext = encrypter.encrypt("plaintext".getBytes());

A Crypter object initialized with the same keyset will decipher the encrypted data:

Crypter crypter = new Crypter("keys");
byte[] plaintext = crypter.decrypt(ciphertext);

Cryptographic transformations provided by the encrypt() and decrypt() methods can be performed with byte arrays, UTF-8 strings and byte buffers. The ciphertext produced as a string is encoded according to the base64url notation:

Crypter crypter = new Crypter("keys");
// WebSafeBase64 output
String ciphertext = crypter.encrypt("plaintext");

A keyset created by KeyczarTool can be employed with the standard Java cryptography. In this case the generated AES key is first represented as a stringified JSON:

KeyczarFileReader keyczarFileReader = new KeyczarFileReader("keys");
String keyInfo = keyczarFileReader.getKey();

Keyczar uses a subset of Gson, so Gson API will help to extract the encoded key from the JSON string:

public class EncodedKey {
 private String aesKeyString;
 public String getAesKeyString() {
  return this.aesKeyString;
 }
}
Gson gson = new Gson();
EncodedKey encodedKey = gson.fromJson(keyInfo, EncodedKey.class);
String keyString = encodedKey.getAesKeyString();

The Base64Coder from the org.keyczar.util package produces the key in its raw format:

byte[] rawKey = Base64Coder.decodeWebSafe(keyString);

The key bytes are passed to the constructor of the SecretKeySpec:

SecretKeySpec secretKeySpec = new SecretKeySpec(rawKey, "AES");

A key specification class provides transparent representation of cryptographic material. A key factory is used to convert between transparent and opaque representations:

SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("AES");
SecretKey secretKey = secretKeyFactory.generateSecret(secretKeySpec);

BouncyCastle Cryptography API

The lightweight cryptography API supported by the BouncyCastle library is suitable under circumstances when the integration of the standard JCA is not required. It is also beneficial for mobile applications: low-level interfaces offer faster performance and reduce the memory footprint.

Before requesting an AES engine, key and the IV parameters should be defined:

SecureRandom secureRandom = new SecureRandom();
KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(secureRandom, 256);
CipherKeyGenerator cipherKeyGenerator = new CipherKeyGenerator();
cipherKeyGenerator.init(keyGenerationParameters);
byte[] key = cipherKeyGenerator.generateKey();
KeyParameter keyParameter = new KeyParameter(key);
byte[] iv = new byte[16];
secureRandom.nextBytes(iv);
ParametersWithIV parametersWithIV = new ParametersWithIV(keyParameter, iv);

The AESEngine is passed to the CBCBlockCipher:

AESEngine aesEngine = new AESEngine();
CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(aesEngine);

The final step is the creation of a PaddedBufferedBlockCipher - a wrapper object applying the PKCS5/PKCS7 padding to input blocks:

PaddedBufferedBlockCipher paddedBufferedBlockCipher = new PaddedBufferedBlockCipher(cbcBlockCipher);
paddedBufferedBlockCipher.init(true, parametersWithIV);

Multiple-part encryption will require stream reading/writing operations:

int i;
byte[] buffer = new byte[8192];
try(
 BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("plaintext.pdf"));
 BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("ciphertext.dat"))
) {
 while((i = inputStream.read(buffer)) != -1) {
  byte[] ciphertext = new byte[paddedBufferedBlockCipher.getUpdateOutputSize(i)];
  paddedBufferedBlockCipher.processBytes(buffer, 0, i, ciphertext, 0);
  outputStream.write(ciphertext);
 }
 byte[] ciphertext = new byte[paddedBufferedBlockCipher.getBlockSize()];
 paddedBufferedBlockCipher.doFinal(ciphertext, 0);
 outputStream.write(ciphertext);
} catch(Exception e) {
 System.err.format("Error encountered: %s", e.getMessage());
}

C#

SymmetricAlgorithm is an algorithm type class defining API for such base classes as Aes, Rijndael, or TripleDES. AesManaged and AesCryptoServiceProvider extend abstract classes and provide C# applications with access to working AES engines:

AesCryptoServiceProvider aes = new AesCryptoServiceProvider();

A mode and a padding scheme for AES encryption are selected by assigning respective values to the properties of the AesCryptoServiceProvider:

aes.KeySize = 256;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;

An instance of ICryptoTransform will be necessary for AES cryptographic transformation:

ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

Now streams for reading/writing the plaintext and ciphertext can be created:

BufferedStream inputStream = new BufferedStream(File.Open("plaintext.pdf", FileMode.Open, FileAccess.Read));
BufferedStream outputStream = new BufferedStream(File.Create("ciphertext.dat"));

The .NET Framework uses a stream-oriented model for symmetric cipher algorithms. The pivot point of the model is CryptoStream. An instance of this class transforming bytes from an underlying source of data works in a fashion similar to Java cipher streams:

CryptoStream cryptoStream = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write);
int i;
byte[] buffer = new byte[8192];
while((i = inputStream.Read(buffer, 0, 8192)) > 0) {
 cryptoStream.Write(buffer, 0, i);
}

CryptoStream objects must be disposed of by a call to the Clear() method. The method flushes the underlying stream and causes all remaining blocks of the plaintext to be processed:

cryptoStream.Clear();

An alternative way to work with block ciphers in C# is the use of the BouncyCastle library: for example, to perform AES encryption such classes as SecureRandom, KeyGenerationParameters, CipherKeyGenerator, KeyParameter, ParametersWithIV, AesEngine, CbcBlockCipher and PaddedBufferedBlockCipher should be brought into play.