Using BouncyCastle's ChaCha for file encryption

前端 未结 3 551
自闭症患者
自闭症患者 2020-12-17 05:43

I\'m hoping to encrypt a few files using ChaCha, so I wonder if it\'s appropriate to use Chacha20Poly1305. It seems that this class is designed for TLS, so does that mean it

相关标签:
3条回答
  • 2020-12-17 06:31

    You can simply use the ChaChaEngine class that is referenced by the Chacha20Poly1305 class. The Engine classes contain implementations of the various cipher classes.

    Besides that, the JCA provides a higher level API to work with the various ciphers. So you can also use:

    Security.addProvider(new BouncyCastleProvider());
    Cipher c = Cipher.getInstance("ChaCha");
    

    After which the normal Java CipherInputStream and CipherOutputStream will become available.

    Note that using Poly1305 does provide additional authentication. This is often not a requirement for file encryption, but it does provide an additional layer of security. If you want to have authenticated encryption and you cannot work out how, then please ask a separate question.

    0 讨论(0)
  • 2020-12-17 06:42

    You could try something line this:

    public void doChaCha(boolean encrypt, InputStream is, OutputStream os,
            byte[] key, byte[] iv) throws IOException {
        CipherParameters cp = new KeyParameter(key);
        ParametersWithIV params = new ParametersWithIV(cp, iv);
        StreamCipher engine = new ChaChaEngine();
        engine.init(encrypt, params);
    
        byte in[] = new byte[8192];
        byte out[] = new byte[8192];
        int len = 0;
        while(-1 != (len = is.read(in))) {
            len = engine.processBytes(in, 0 , len, out, 0);
            os.write(out, 0, len);
        }
    }
    
    public void encChaCha(InputStream is, OutputStream os, byte[] key,
            byte[] iv) throws IOException {
        doChaCha(true, is, os, key, iv);
    }
    
    public void decChaCha(InputStream is, OutputStream os, byte[] key,
            byte[] iv) throws IOException {
        doChaCha(false, is, os, key, iv);
    }
    

    Note that the ChaChaEngine supports only keys of the length 128 or 256 bits with an IV of 64 bits.

    Here is some tests:

    @Test
    public void chachaText() throws IOException, NoSuchAlgorithmException {
        String text = "chacha.txt";
        Files.write(Paths.get(text), "Hello, World!".getBytes(StandardCharsets.UTF_8), 
            StandardOpenOption.CREATE);
        chacha(text);
    }
    
    @Test
    public void chachaBin() throws IOException, NoSuchAlgorithmException {
        SecureRandom sr = SecureRandom.getInstanceStrong();
        byte[] data = new byte[1024*1024*4];
        sr.nextBytes(data);
        String bin = "chacha.bin";
        Files.write(Paths.get(bin), data, StandardOpenOption.CREATE);
        chacha(bin);
    }
    
    private void chacha(String file) throws NoSuchAlgorithmException, IOException {
        SecureRandom sr = SecureRandom.getInstanceStrong();
        byte[] key = new byte[32]; // 32 for 256 bit key or 16 for 128 bit
        byte[] iv = new byte[8]; // 64 bit IV required by ChaCha20
        sr.nextBytes(key);
        sr.nextBytes(iv);
        String dat = String.format("%s.dat", file);
    
        try(InputStream is = new FileInputStream(file); 
            OutputStream os = new FileOutputStream(dat)) {
            encChaCha(is, os, key, iv);
        }
    
        try(InputStream is = new FileInputStream(dat); 
            ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            decChaCha(is, os, key, iv);
            byte[] actual = os.toByteArray();
            byte[] expected = Files.readAllBytes(Paths.get(file));
            Assert.assertArrayEquals(expected, actual);
        }
    }
    

    EDIT: encode / decode Strings

    @Test
    public void chachaString() throws IOException, NoSuchAlgorithmException
    {
        String test = "Hello, World!";
    
        try (InputStream isEnc = new ByteArrayInputStream(test.getBytes(StandardCharsets.UTF_8));
                ByteArrayOutputStream osEnc = new ByteArrayOutputStream())
        {
            SecureRandom sr = SecureRandom.getInstanceStrong();
            byte[] key = new byte[32]; // 32 for 256 bit key or 16 for 128 bit
            byte[] iv = new byte[8]; // 64 bit IV required by ChaCha20
            sr.nextBytes(key);
            sr.nextBytes(iv);
    
            encChaCha(isEnc, osEnc, key, iv);
    
            byte[] encoded = osEnc.toByteArray();
    
            try (InputStream isDec = new ByteArrayInputStream(encoded);
                    ByteArrayOutputStream osDec = new ByteArrayOutputStream())
            {
                decChaCha(isDec, osDec, key, iv);
    
                byte[] decoded = osDec.toByteArray();
    
                String actual = new String(decoded, StandardCharsets.UTF_8);
    
                Assert.assertEquals(test, actual);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-17 06:48

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

    Here´s a reference Implementation:

    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: https://gist.github.com/eXspir3/7a0821a6cfdb0495ccc3e69b475a61b9

    0 讨论(0)
提交回复
热议问题