Using BouncyCastle's ChaCha for file encryption

不羁的心 提交于 2019-12-29 06:30:11

问题


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's not designed for file encryption? The methods inside, e.g., encodePlaintext() and decodeCiphertext() appear to work with text rather than binary files.

If that's the case, does anyone know how to work with BC's ChaCha implementation for file encryption?


回答1:


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.




回答2:


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);
        }
    }
}



回答3:


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



来源:https://stackoverflow.com/questions/32672241/using-bouncycastles-chacha-for-file-encryption

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!