ChaCha20 is a stream cipher designed by Daniel J. Bernstein, ChaCha20 is a variant of the Salsa20 family of stream ciphers and widely used as an alternative to AES Encryption Algorithm. The 20 round stream cipher ChaCha20 is consistently faster and not sensitive to timing attacks as AES Algorithm. Java 11 has added support to ChaCha20 and ChaCha20 Poly1305 [AEAD]
What is ChaCha20 Stream Cipher?
Chacha20 is mainly used for encryption, its core is a pseudo-random number generator. ChaCha20 is based upon Add-Rotate-XOR (ARX) Operations, which are CPU friendly instructions. The ciphertext is obtained by XOR’ing the plain text with a pseudo-random stream. Both the ChaCha20 and ChaCha20-Poly1305 will implement the CipherSpi API within the SunJCE provider
Currently, JDK supports the key length of 256 bit only, if we try to use a different key length we will be getting an error like “Key length for ChaCha20 must be 256 bits”
Exception in thread "main" java.security.InvalidParameterException: Key length for ChaCha20 must be 256 bits at java.base/com.sun.crypto.provider.KeyGeneratorCore$ChaCha20KeyGenerator.engineInit(KeyGeneratorCore.java:230) at java.base/javax.crypto.KeyGenerator.init(KeyGenerator.java:540) at java.base/javax.crypto.KeyGenerator.init(KeyGenerator.java:517) at com.javainterviewpoint.ChaCha20_Encrytpion.main(ChaCha20Example.java:18)
Java ChaCha20 Encryption and Decryption Example
package com.javainterviewpoint; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.ChaCha20ParameterSpec; import javax.crypto.spec.SecretKeySpec; public class ChaCha20_Encrytpion { static String plainText = "This is a plain text which will be encrypted by ChaCha20 Algorithm"; public static void main(String[] args) throws Exception { KeyGenerator keyGenerator = KeyGenerator.getInstance("ChaCha20"); keyGenerator.init(256); // Generate Key SecretKey key = keyGenerator.generateKey(); System.out.println("Original Text : " + plainText); byte[] cipherText = encrypt(plainText.getBytes(), key); System.out.println("Encrypted Text : " + Base64.getEncoder().encodeToString(cipherText)); String decryptedText = decrypt(cipherText, key); System.out.println("DeCrypted Text : " + decryptedText); } public static byte[] encrypt(byte[] plaintext, SecretKey key) throws Exception { byte[] nonceBytes = new byte[12]; int counter = 5; // Get Cipher Instance Cipher cipher = Cipher.getInstance("ChaCha20"); // Create ChaCha20ParameterSpec ChaCha20ParameterSpec paramSpec = new ChaCha20ParameterSpec(nonceBytes, counter); // Create SecretKeySpec SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "ChaCha20"); // Initialize Cipher for ENCRYPT_MODE cipher.init(Cipher.ENCRYPT_MODE, keySpec, paramSpec); // Perform Encryption byte[] cipherText = cipher.doFinal(plaintext); return cipherText; } public static String decrypt(byte[] cipherText, SecretKey key) throws Exception { byte[] nonceBytes = new byte[12]; int counter = 5; // Get Cipher Instance Cipher cipher = Cipher.getInstance("ChaCha20"); // Create ChaCha20ParameterSpec ChaCha20ParameterSpec paramSpec = new ChaCha20ParameterSpec(nonceBytes, counter); // Create SecretKeySpec SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "ChaCha20"); // Initialize Cipher for DECRYPT_MODE cipher.init(Cipher.DECRYPT_MODE, keySpec, paramSpec); // Perform Decryption byte[] decryptedText = cipher.doFinal(cipherText); return new String(decryptedText); } }
- KeyGenerator Class generates the symmetric encryption keys, we will obtain the KeyGenerator class instance by calling the getInstance() method passing the name of the algorithm as a parameter, in our case it is ChaCha20
KeyGenerator keyGenerator = KeyGenerator.getInstance("ChaCha20");
- Once the KeyGenerator instance is created, we need to initialize it by calling its init() method, we need to pass the key size in bits to generate. Currently, Java 11 supports 256-bit key only.
keyGenerator.init(256);
- Once the KeyGenerator is initialized, we can generate the symmetric SecretKey by calling the generateKey() method on top of the KeyGenerator instance.
SecretKey key = keyGenerator.generateKey();
- We have a declared 12-byte nonce and initialized the counter value to 5. The nonce value must be 96 bits in length (12 bytes), any other length cannot be used as it results in an exception. The counter value can be any positive or negative integer.
byte[] nonceBytes = new byte[12]; int counter = 5;
- Cipher class is the actual class which handles the encryption and decryption. Cipher class instance is created by calling the getInstance() method by passing the Cipher name as the parameter, in our case, it is ChaCha20 which is the name of the encryption algorithm.
Cipher cipher = Cipher.getInstance("ChaCha20");
- We can also pass in parameter like “ChaCha20/None/NoPadding”. No other modes or padding values other than “None” and “NoPadding” will be accepted. If we try to use any other modes or padding we will be getting an error like “Mode must be None” or “Padding must be NoPadding”.
- ChaCha20ParameterSpec specifies the parameter which should be used with the ChaCha20 algorithm, the parameters are 12-byte nonce and a 32-bit counter integer.
ChaCha20ParameterSpec paramSpec = new ChaCha20ParameterSpec(nonceBytes, counter);
- The SecretKeySpec converts byte data into a secret key which is suitable to be passed to init() method of the Cipher class.
-
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "ChaCha20");
- Once the Cipher class instance is created we need to initialize it by calling the init() method. The init() method requires three parameters.
- Mode – Cipher.ENCRYPT_MODE for encryption or Cipher.DECRYPT_MODE for decryption.
- Key – SecretKeySpec is the key here
- AlgorithmParameterSpec – we will be passing ChaCha20ParameterSpec [ChaCha20ParameterSpec implements AlgorithmParameterSpec]
cipher.init(Cipher.DECRYPT_MODE, keySpec, paramSpec);
- In order to perform encryption, we need to call the doFinal() method on top of the cipher instance passing the plainText as the parameter.
byte[] cipherText = cipher.doFinal(plaintext);
- We need to encode the cipherText with Base64 encoding, in order to prevent modification when the cipherText is transferred.
Base64.getEncoder().encodeToString(cipherText)
- In order to perform decryption, we need to pass the cipherText to the doFinal() method of the Cipher instance
byte[] decryptedText = cipher.doFinal(cipherText);
Output:
Original Text : This is a plain text which will be encrypted by ChaCha20 Algorithm Encrypted Text : 0aljGw8VOeU+eMyRC4eQekYOIUehWSUEpHHc23OHcjlZnAU5fTSKZln1hgbA2iJADwR+D1ktypILYiOCXxDLJSg+ DeCrypted Text : This is a plain text which will be encrypted by ChaCha20 Algorithm
Leave a Reply