I need to encrypt a file line by line using the RC4 algorithm without encoding the resulting byte array from the encryption. I saw a post on here about how to encrypt line by line and it works fine but when I attempt to do it skipping the encoding step only the first line gets decrypted successfully . Is it possible to just write the byte array to file without encoding it and be able to successfully decrypt that file?
This is what I have tried:
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);
Yes, of course that is possible, but in that case you need to keep the line endings present. Then you can create a method decryptLine
which will end when the plaintext consists of a line ending. This will probably require you to decrypt byte by byte though.
If you remove any indication of lines, as you currently do for the plaintext message, then there is no way to see the lines anymore. The stream cipher will encrypt the lines but as a stream cipher does not pad or otherwise alter the plaintext, the line endings are gone, and there are no other indicators that show where the lines should be.
RC4 is an old insecure cipher. Much worse, using RC4 by re-using doFinal
over a plaintext is so insecure that anybody should be able to retrieve the plaintext. Basically, you start over the encryption phase which is just XOR with the key stream generated by RC4, which will allow attacks like that on a reused one-time-pad.
Besides that, if the RC4 encryption over the lines with line encodings is just advanced using update
rather than restarted using doFinal
then the file will just be the same as the binary encoding of the file. In other words, you might as well simply encrypt the entire file.
So whoever asked you to perform the task and the person that wrote this particular example do not seem to have a clue about crypto.
But yeah, sometimes you just want to see some code for learning purposes. The following code uses functionality in the Java stream API to read and write the lines, while using CipherOutputStream
and CipherInputStream
and to encrypt and decrypt the binary data.
Note that:
BufferedReader
you are able to extract the lines from the ciphertext after decryption but note that the reader may decrypt more than just the line in the underlying buffer;"%n"
format string - which should really be stored in a constant, but yeah);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);
}
}