【问题标题】:Using BouncyCastle's ChaCha for file encryption使用 BouncyCastle 的 ChaCha 进行文件加密
【发布时间】:2015-12-16 19:07:17
【问题描述】:

我希望使用ChaCha加密一些文件,所以我想知道使用Chacha20Poly1305是否合适。似乎这个类是为 TLS 设计的,那么这是否意味着它不是为文件加密而设计的?内部的方法,例如 encodePlaintext()decodeCiphertext() 似乎适用于文本而不是二进制文件。

如果是这样,有谁知道如何使用 BC 的 ChaCha 实现进行文件加密?

【问题讨论】:

  • 您看过examples 包吗?而你不使用java's standard crypto API
  • 是的,有,但是您知道是否使用相同的代码,但将DESedeEngine 换成ChachaEngine? Java 的标准加密 API 不支持 ChaCha - 对吧?
  • ChaCha20 是一种流密码,Poly1305 是一种设计用于与 128 位分组密码结合使用的 mac。是的,如果您知道自己在做什么,您可以假装流密码是用于 TLS 的分组密码。但是,除了 TLS 的特殊情况,bouncycastle 并不真正支持这种组合。
  • @JamesKPolk 那么这是否意味着 BC 的 ChaCha20 只能保护静态数据?
  • 不,这不是那个意思。这意味着您必须编写比使用其他密码和 mac 组合更多的代码。

标签: java encryption bouncycastle


【解决方案1】:

你可以试试这个:

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

请注意,ChaChaEngine 仅支持长度为 128 或 256 位且 IV 为 64 位的密钥。

这里有一些测试:

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

编辑:编码/解码字符串

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

【讨论】:

  • A4L 你共享了 fileEncryption 的代码,而我需要使用 chacha20 进行简单的字符串加密。虽然感谢您的反馈
  • @Nepster 嗨,我添加了一个示例,说明如何使用相同的方法对字符串进行编码/解码。
  • A4L 非常感谢您的帮助。它运行良好。我只是将 StandardCharsets.UTF_8 更改为 Charset.forName("UTF-8") 以获取低于 kitkat 的 API。并且还将 SecureRandome.getInstanceStrong 更改为 getInstance("SHA1PRNG");因为那个方法需要 Java 7,我现在没有。
  • A4L ,我想知道。我可以在您的代码中使用我自己的密钥吗?如果是怎么办?并且 chachaEngine 必须需要 ByteStream 和 InputStream。或者它可以在没有它们的情况下完成。请参阅我的问题stackoverflow.com/questions/38007478/…
  • @Nepster 当然,您可以使用任何您想要的密钥,只要确保它具有所需的长度,即 128 位或 256 位。 Chacha 引擎不需要输入/输出流,您也可以使用它 qith 纯字节数组,我在示例中使用它们让一个方法处理所有内容,然后一切都归结为流,正如您在我的编辑中看到的那样, conde/decode String 的示例使用了相同的方法。
【解决方案2】:

您可以简单地使用Chacha20Poly1305 类引用的ChaChaEngine 类。 Engine 类包含各种密码类的实现。

除此之外,JCA 还提供了更高级别的 API 来处理各种密码。所以你也可以使用:

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

之后普通的 Java CipherInputStreamCipherOutputStream 将可用。

请注意,使用 Poly1305确实提供额外的身份验证。这通常不是文件加密的要求,但它确实提供了额外的安全层。如果您想进行经过身份验证的加密,但您不知道如何进行,请提出一个单独的问题。

【讨论】:

  • 也看看我的问题。 stackoverflow.com/questions/38007478/…
  • Caused by: java.security.NoSuchAlgorithmException: No provider found for ChaChaimplementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.61'
  • 您忘记添加如上的提供者了吗?因为这不会在早期版本的 BouncyCastle 中失败(1.6,我猜是 1.60,因为我当然没有 1.6 - 早期的白痴版本控制使用浮点数:这是无法理解的愚蠢)。
【解决方案3】:

Java 11 添加了“ChaCha20-Poly1305/None/NoPadding”密码 现在可以在没有 BouncyCastle 或其他魔法的情况下使用 -->

这是一个参考实现

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

}
}

以及对应的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()));
    }
}

我的 Github GIST 中使用的代码: https://gist.github.com/eXspir3/7a0821a6cfdb0495ccc3e69b475a61b9

【讨论】:

    猜你喜欢
    • 2013-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-22
    • 1970-01-01
    • 2021-03-22
    • 1970-01-01
    • 2016-01-22
    相关资源
    最近更新 更多