是否可以在不写入结果字节数组的情况下逐行加密文件,然后再写入文件?

时间:2019-05-13 08:13:32

标签: java encryption rc4-cipher

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

这是我尝试过的:

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

1 个答案:

答案 0 :(得分:0)

是的,当然可以,但是在这种情况下,您需要保持行尾。然后,您可以创建方法decryptLine,该方法将在明文由行尾组成时结束。但这可能需要您逐字节解密。

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

RC4是旧的不安全密码。更糟糕的是,通过在明文上重用doFinal来使用RC4的方法是如此不安全,以至于任何人都应该能够检索明文。基本上,您将从与RC4生成的密钥流进行XOR的加密阶段开始,这将允许像在重复使用的一次性键盘上那样进行攻击。

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

因此,无论是谁要求您执行任务 ,撰写此示例示例的人似乎都不了解加密技术。


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

请注意:

  • 每个文件需要一个类实例,重新使用一个类实例(或键)会使代码不安全,因为RC4不使用IV;
  • 使用BufferedReader,您可以在解密后从密文中提取行,但是请注意,读取者可以解密不仅仅是对基础缓冲区中的行;
  • 此代码不能很好地处理异常(请参见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);
    }
}