Class BotanBaseAsymmetricCipher

java.lang.Object
javax.crypto.CipherSpi
net.randombit.botan.seckey.BotanBaseAsymmetricCipher
Direct Known Subclasses:
BotanBlockCipher, BotanStreamCipher

public abstract class BotanBaseAsymmetricCipher extends CipherSpi
Abstract base class for symmetric cipher implementations using the Botan cryptography library.

This class provides a JCE-compliant Cipher implementation that delegates cryptographic operations to native Botan library functions via JNR-FFI. It implements automatic native resource management using the Java Cleaner API to ensure native cipher objects are properly destroyed when no longer needed.

The class name "BaseAsymmetricCipher" is a misnomer - it actually implements symmetric ciphers (block ciphers, stream ciphers, and AEAD modes). The name predates the current architecture and is retained for compatibility.

Cipher Categories

This base class supports three main categories of symmetric ciphers:

  • Block Ciphers - Traditional block cipher modes (CBC, CFB) with padding support
  • Stream Ciphers - Stream modes (CTR, OFB) and native stream ciphers (ChaCha20, Salsa20)
  • AEAD Ciphers - Authenticated encryption modes (GCM, CCM, EAX, OCB, SIV)

Lifecycle and Resource Management

Native Botan cipher objects are created during initialization and destroyed either:

  • Explicitly when re-initializing with a new key (old object destroyed before creating new one)
  • Automatically by the Cleaner when the Java object becomes unreachable (garbage collection)

Thread Safety

This implementation is NOT thread-safe. Each thread should use its own Cipher instance. The JCE API does not require Cipher implementations to be thread-safe.

Initialization and IV/Nonce Management

Ciphers require initialization with a key and optional IV (Initialization Vector) or nonce:

  • The IV/nonce must be provided via IvParameterSpec during initialization
  • IV/nonce sizes are validated by isValidNonceLength(int)
  • The IV is stored and can be retrieved via engineGetIV()
  • Important: Reusing the same IV/nonce with the same key is cryptographically unsafe for most modes

Usage Examples

Basic AES-CBC Encryption with Padding


 // Get cipher instance from the Botan provider
 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7", "Botan");

 // Generate key and IV
 SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");  // 16, 24, or 32 bytes
 IvParameterSpec iv = new IvParameterSpec(ivBytes);        // 16 bytes for AES

 // Initialize for encryption
 cipher.init(Cipher.ENCRYPT_MODE, key, iv);

 // Encrypt data
 byte[] plaintext = "Secret message".getBytes();
 byte[] ciphertext = cipher.doFinal(plaintext);

 // Decrypt
 cipher.init(Cipher.DECRYPT_MODE, key, iv);
 byte[] decrypted = cipher.doFinal(ciphertext);
 

Stream Cipher (ChaCha20)


 Cipher cipher = Cipher.getInstance("ChaCha20/None/NoPadding", "Botan");

 SecretKeySpec key = new SecretKeySpec(keyBytes, "ChaCha20");  // 32 bytes
 IvParameterSpec nonce = new IvParameterSpec(nonceBytes);      // 8 bytes

 // Encryption
 cipher.init(Cipher.ENCRYPT_MODE, key, nonce);
 byte[] ciphertext = cipher.doFinal(plaintext);

 // Decryption (same nonce required)
 cipher.init(Cipher.DECRYPT_MODE, key, nonce);
 byte[] plaintext = cipher.doFinal(ciphertext);
 

AEAD Mode (AES-GCM) with Authentication


 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "Botan");

 SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
 GCMParameterSpec params = new GCMParameterSpec(128, nonceBytes);  // 128-bit tag, 12-byte nonce

 // Encryption with authentication
 cipher.init(Cipher.ENCRYPT_MODE, key, params);
 cipher.updateAAD(additionalData);  // Optional authenticated data
 byte[] ciphertext = cipher.doFinal(plaintext);  // Includes authentication tag

 // Decryption and verification
 cipher.init(Cipher.DECRYPT_MODE, key, params);
 cipher.updateAAD(additionalData);  // Must match encryption
 byte[] plaintext = cipher.doFinal(ciphertext);  // Throws exception if authentication fails
 

Incremental Processing with Update


 Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "Botan");
 cipher.init(Cipher.ENCRYPT_MODE, key, iv);

 // Process data incrementally
 byte[] part1 = cipher.update(data1);
 byte[] part2 = cipher.update(data2);
 byte[] part3 = cipher.update(data3);
 byte[] finalPart = cipher.doFinal();

 // Combine all parts
 byte[] complete = concatenate(part1, part2, part3, finalPart);
 

Re-initialization with Different Key


 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7", "Botan");

 // First encryption
 SecretKeySpec key1 = new SecretKeySpec(keyBytes1, "AES");
 IvParameterSpec iv1 = new IvParameterSpec(ivBytes1);
 cipher.init(Cipher.ENCRYPT_MODE, key1, iv1);
 byte[] ciphertext1 = cipher.doFinal(plaintext1);

 // Re-initialize with different key (automatically destroys old native object)
 SecretKeySpec key2 = new SecretKeySpec(keyBytes2, "AES");
 IvParameterSpec iv2 = new IvParameterSpec(ivBytes2);
 cipher.init(Cipher.ENCRYPT_MODE, key2, iv2);  // Old Botan cipher destroyed, new one created
 byte[] ciphertext2 = cipher.doFinal(plaintext2);
 

Different Padding Modes


 // PKCS#7 padding (most common)
 Cipher pkcs7 = Cipher.getInstance("AES/CBC/PKCS7", "Botan");

 // No padding (plaintext must be multiple of block size)
 Cipher noPad = Cipher.getInstance("AES/CBC/NoPadding", "Botan");

 // Other padding schemes
 Cipher x923 = Cipher.getInstance("AES/CBC/X9.23", "Botan");
 Cipher oneZero = Cipher.getInstance("AES/CBC/OneAndZeros", "Botan");
 

Cipher Mode Categories and Padding

Block Cipher Modes (CBC, CFB):

  • Support multiple padding schemes: PKCS7, PKCS5, X9.23, OneAndZeros, ESP, NoPadding
  • IV size must match the block size of the underlying algorithm (e.g., 16 bytes for AES)
  • With NoPadding, plaintext length must be a multiple of block size

Stream Modes (CTR, OFB, ChaCha20, Salsa20):

  • Use "/None/NoPadding" or just "/NoPadding" (no padding needed for stream modes)
  • Can process any length of plaintext without padding
  • Nonce/IV sizes vary by algorithm (e.g., 8 bytes for ChaCha20, 16 bytes for AES-CTR)

AEAD Modes (GCM, CCM, EAX, OCB, SIV):

  • Always use "/NoPadding" (AEAD modes don't use padding)
  • Provide built-in authentication - no separate MAC needed
  • Support Additional Authenticated Data (AAD) via updateAAD()
  • Nonce sizes vary by mode (typically 12 bytes for GCM)

Implementation Notes

  • Cloning Not Supported - Calling clone() throws CloneNotSupportedException because native cipher state cannot be safely cloned
  • Key Size Validation - Key sizes are validated against Botan's key specification during initialization
  • Nonce Reuse Warning - The implementation includes a FIXME comment about preventing nonce reuse, which is a critical security concern for most cipher modes
  • Memory Safety - Native resources are guaranteed to be freed even if explicit cleanup is not called, thanks to the Cleaner API
  • Mode Setting - The JCE API method setMode() is not supported because the mode is specified in the transformation string during getInstance()

Concrete Implementations

This class has three main subclass hierarchies:

  • BotanBlockCipher - Block cipher modes (CBC, CFB) with padding
  • BotanStreamCipher - Stream modes and stream ciphers
  • BotanAeadCipher - Authenticated encryption modes
Since:
0.1.0
Author:
Yasser Aziza
See Also: