Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: ChaCha20 w/ Poly1305 as MAC for general purpose file encryption

I need to use ChaCha20/Poly1305 for general purpose encryption and decryption of any data from any source (the same way we use I/O streams, ex. CipherInputStream).

This question already asks if it's possible to use Bouncy Castle's ChaCha20Poly1305 class to process data but only TLS transactions seem to be supported.

So now I'm left with pure ChaCha20 (ChaCha20Engine). I would like to know if writing my own encrypt-then-MAC scheme is a good idea or not, so that I get exactly what I need.

tl;dr Is it OK to write my own ChaCha20/Poly1305 encrypt-then-MAC mode of operation ?

like image 778
Dreadlockyx Avatar asked Dec 09 '25 16:12

Dreadlockyx


1 Answers

Java 11 adds a "ChaCha20-Poly1305/None/NoPadding Cipher It is now possible to use without any third party libraries or some other magic -->

Here’s a reference Implementation

ATTENTION: this Implementation uses a non-random Nonce and should NOT be used except for Testing purposes:

package chaCha20Poly1305Encryption;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;

public class ChaCha20Poly1305 {

    public static byte[] encrypt(byte[] data, SecretKey key) throws NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        if(key == null) throw new InvalidKeyException("SecretKey must NOT be NULL");
    
        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
        return cipher.doFinal(data);
    }
    
    public static byte[] decrypt(byte[] cipherText, SecretKey key) throws Exception {
        if(key == null) throw new InvalidKeyException("SecretKey must NOT be NULL");
        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
        return cipher.doFinal(cipherText);
    }
    
    public static void main(String[] args) throws Exception {
        SecretKey key = ChaCha20Poly1305KeyGenerator.generateKey();
    
        String testMessage = "hallo!";
        byte[] encryptedBytes = encrypt(testMessage.getBytes(), key);
        String decryptedMessage = new String(decrypt(encryptedBytes,key));
        System.out.println("testMessage: " + testMessage);
        System.out.println(key.getAlgorithm() + " SecretKey: " + Base64.getEncoder().encodeToString(key.getEncoded()));
        System.out.println("encryptedBytes: " + Base64.getEncoder().encodeToString(encryptedBytes));
        System.out.println("decryptedMessage: "+ decryptedMessage);
    
    }
}

And the corresponding Key-Generator:

package chaCha20Poly1305Encryption;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

public class ChaCha20Poly1305KeyGenerator {
    public static SecretKey generateKey() throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("ChaCha20");
        //Keysize MUST be 256 bit - as of Java11 only 256Bit is supported
        keyGenerator.init(256);
        return keyGenerator.generateKey();
    }
    public static void main(String[] args) throws NoSuchAlgorithmException {
        SecretKey key = generateKey();
        System.out.println(key.getAlgorithm() + " SecretKey: " + Base64.getEncoder().encodeToString(key.getEncoded()));
    }
}

Code used from my Github Gist can be found here.

like image 54
Philipp Ensinger Avatar answered Dec 11 '25 05:12

Philipp Ensinger