是否可以逐行加密文件而不在写入文件之前对结果字节数组进行编码?

Is it possible to encrypt a file line by line without encoding the resulting byte array before writing to file?

我需要使用 RC4 算法逐行加密文件,而不对加密生成的字节数组进行编码。我在这里看到了关于如何逐行加密的 post,它工作正常,但是当我尝试跳过编码步骤时,只有第一行成功解密。是否可以只将字节数组写入文件而不对其进行编码并能够成功解密该文件?

这是我试过的:

while ((line = br.readLine()) != null) 
{

        Cipher rc4 = Cipher.getInstance("RC4");                             
        SecretKeySpec rc4Key = new SecretKeySpec(pwd.getBytes(), "RC4");                                    
        rc4.init(Cipher.ENCRYPT_MODE, rc4Key);
        byte [] cipherText = rc4.doFinal(line.getBytes());                                 
        fos.write(cipherText);                                
        fos.flush();
}

//decrypt file
    byte [] decrypt = Files.readAllBytes(Paths.get(outputFile));
    Cipher rc4d = Cipher.getInstance("RC4");
    SecretKeySpec rc4dKey = new SecretKeySpec(pwd.getBytes(), "RC4");
    rc4d.init(Cipher.DECRYPT_MODE, rc4dKey);
    byte [] decrypted = rc4d.doFinal(decrypt);
    String results = new String(decrypted);
    System.out.println("Decrypted : " + results);

是的,这当然是可能的,但在那种情况下,您需要保留行尾。然后你可以创建一个方法 decryptLine ,当明文包含一个行结束时,该方法将结束。不过,这可能需要您逐字节解密。

如果像您当前对纯文本消息所做的那样删除任何行指示,则将无法再看到这些行。流密码将对行进行加密,但由于流密码不会填充或以其他方式更改明文,因此行结尾消失了,并且没有其他指示符显示行的位置。

RC4 是一种古老的不安全密码。更糟糕的是,re-using doFinal 在明文上使用 RC4 非常不安全,任何人都应该能够检索明文。基本上,您从加密阶段开始,它只是与 RC4 生成的密钥流进行 XOR,这将允许对重用的 one-time-pad.

进行类似的攻击。

除此之外,如果使用 update 仅使用行编码对行进行 RC4 加密而不是使用 doFinal 重新启动,则文件将与二进制编码相同文件。换句话说,您还不如简单地加密整个文件。

因此,无论是谁要求您执行任务 编写此特定示例的人似乎都没有关于加密的线索。


但是,是的,有时您只是想查看一些用于学习目的的代码。以下代码使用 Java 流 API 中的功能来读取和写入行,同时使用 CipherOutputStreamCipherInputStream 以及加密和解密二进制数据。

注意:

  • 每个文件需要一个 class 实例,重复使用 class 实例(或密钥)会使代码不安全,因为 RC4 不使用 IV;
  • 使用 BufferedReader 您可以在解密后从密文中提取行,但请注意 reader 可能解密 不仅仅是底层缓冲区中的行 ;
  • 这段代码不能很好地处理异常(参见here如何正确处理加密异常);
  • 这会将行结尾转换为平台默认编码("%n" 格式字符串——实际上应该存储在常量中,但是是的);
  • 它假定文件采用 UTF-8/兼容编码。
package com.stackexchange.so;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class LineStreamRC4 {

    private SecretKey rc4Key;

    public LineStreamRC4(SecretKey rc4Key) {
        this.rc4Key = rc4Key;

    }

    public void encryptLineByLine(File in, File out) throws Exception {
        Cipher c = Cipher.getInstance("RC4");
        c.init(Cipher.ENCRYPT_MODE, rc4Key);

        try (BufferedReader reader = new BufferedReader(
                new FileReader(in, UTF_8));
                CipherOutputStream cryptWriter = new CipherOutputStream(
                        new FileOutputStream(out), c)) {

            String line;
            while ((line = reader.readLine()) != null) {
                line += String.format("%n");
                cryptWriter.write(line.getBytes(UTF_8));
            }
        }
    }

    public void decryptLineByLine(File in, File out) throws Exception {
        Cipher c = Cipher.getInstance("RC4");
        c.init(Cipher.DECRYPT_MODE, rc4Key);

        try (BufferedReader cryptReader = new BufferedReader(
                new InputStreamReader(
                        new CipherInputStream(new FileInputStream(in), c), UTF_8));
                FileWriter writer = new FileWriter(out, UTF_8)) {

            String line;
            while ((line = cryptReader.readLine()) != null) {
                line += String.format("%n");
                writer.write(line);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        File pt = new File("src/com/stackexchange/so/LineStreamRC4.java");
        File ct = new File("bla.ct");
        LineStreamRC4 rc4LineStream = new LineStreamRC4(new SecretKeySpec(new byte[16], "RC4"));
        rc4LineStream.encryptLineByLine(pt, ct);
        File pt2 = new File("bla.pt");
        rc4LineStream.decryptLineByLine(ct, pt2);
    }
}