ChaCha20 Poly1305 is an AEAD [Authenticated Encryption with Additional Data] cipher.In the ChaCha20-Poly1305 algorithm, ChaCha20 Stream cipher performs the Encryption and Poly1305 performs the Authentication. ChaCha20 encrypts the data using Key and IV (Initialization Vector), Poly1305 will be used on the encrypted text, and a MAC [Message Authentication Code] is calculated and appended to the output.
What is Poly1305?
Poly1305 is a cryptographic Message Authentication Code (MAC) published in 2004. Compared to the more widely used HMAC, Poly1305 is extremely faster. Poly1305 can be used on both Encrypted and Decrypted messages, it generates the authentication token and the token guarantees the integrity of the message.
ChaCha20 running in AEAD mode with the Poly1305 authenticator will require only the nonce as the counter value is set to 1 (RFC 7539). When running in AEAD mode, output sizes will be different from inputs due to the addition of the authentication tag during Encryption or consumption of the authentication tag during Decryption.
Java ChaCha20 Poly1305 Encryption and Decryption Example
package com.javainterviewpoint; import java.security.spec.AlgorithmParameterSpec; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class ChaCha20Poly1305Example { static String plainText = "This is a plain text which will be encrypted by ChaCha20 Poly1305 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]; // Get Cipher Instance Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding"); // Create IvParamterSpec AlgorithmParameterSpec ivParameterSpec = new IvParameterSpec(nonceBytes); // Create SecretKeySpec SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "ChaCha20"); // Initialize Cipher for ENCRYPT_MODE cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec); // Perform Encryption byte[] cipherText = cipher.doFinal(plaintext); return cipherText; } public static String decrypt(byte[] cipherText, SecretKey key) throws Exception { byte[] nonceBytes = new byte[12]; // Get Cipher Instance Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding"); // Create IvParamterSpec AlgorithmParameterSpec ivParameterSpec = new IvParameterSpec(nonceBytes); // Create SecretKeySpec SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "ChaCha20"); // Initialize Cipher for DECRYPT_MODE cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec); // Perform Decryption byte[] decryptedText = cipher.doFinal(cipherText); return new String(decryptedText); } }
- KeyGenerator Class instance is obtained by passing the encryption algorithm to the getInstance() method, in our case it is ChaCha20. The KeyGenerator class generates the symmetric encryption keys
KeyGenerator keyGenerator = KeyGenerator.getInstance("ChaCha20");
- Now we need to initialize the keyGenerator instance, we can do so by calling the init() method. Java 11 has added support to generate a 256-bit key only, so no other key size can be passed.
keyGenerator.init(256);
- 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 we don’t have the counter value here as it is set to 1 by default. The nonce value must be 96 bits in length (12 bytes), any other length cannot be used as it results in an exception.
byte[] nonceBytes = new byte[12];
- Cipher class handles the actual 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-Poly1305 which is the name of the encryption algorithm, the mode is None and padding is NoPadding.
Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305/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”.
- Instead of using ChaCha20ParameterSpec, we have used IvParameterSpec to which the12-byte nonce is passed,this allows ChaCha20-Poly1305 to be backported to earlier releases without making any API changes.
AlgorithmParameterSpec ivParameterSpec = new IvParameterSpec(nonceBytes);
- The SecretKeySpec converts the secret key suitable to be passed to init() method of the Cipher class.
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "ChaCha20");
- The Cipher class can be initialized by calling the init() method by passing the below three parameters
- Mode – Cipher.ENCRYPT_MODE for encryption or Cipher.DECRYPT_MODE for decryption.
- Key – we need to pass the SecretKeySpec
- AlgorithmParameterSpec – we need to pass the ivParameterSpec [IvParameterSpec class implements AlgorithmParameterSpec]
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);
(or)
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
- In order to perform the encryption, initialize the cipher in ENCRYPT_MODE and call the doFinal() method on top of the cipher instance passing the plainText as the parameter.
byte[] cipherText = cipher.doFinal(plaintext);
- 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, initialize the cipher in DECRYPT_MODE and call the doFinal() method on top of the cipher instance passing the cipherText as the parameter.
byte[] decryptedText = cipher.doFinal(cipherText);
Output:
Original Text : This is a plain text which will be encrypted by ChaCha20 Poly1305 Algorithm Encrypted Text : +s9gE+j8jijJE6OKZgvo3+YzoA4Hwylv77Xiftm+REOKtnPVjJ4RpcVzP6+vpfeDLvVOpaGqYK5l5djJM3Kzbttdjm3NkZPSn6Pfe6E9J5tVpmxHTIy2pquF4w== DeCrypted Text : This is a plain text which will be encrypted by ChaCha20 Poly1305 Algorithm
Leave a Reply