I'm trying to encrypt a text with XOR using pseudorandom numbers and then write the encrypted text to a file. It then reads the file again and if the user enters the correct key it decrypts the text.
However all I get is ?????
. What am I doing wrong?
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Random;
import java.util.Scanner;
public class MainTest
{
public static void main(String[] args)
{
long seed = 754; // key
Random random = new Random(seed);
String message = "hello";
try
{
OutputStream os = new FileOutputStream("test.txt");
OutputStream bos = new BufferedOutputStream(os);
EncryptOutputStream eos = new EncryptOutputStream(bos);
for(int i = 0; i < message.length(); i++)
{
int z = random.nextInt();
eos.write(message.charAt(i), z);
}
eos.close();
InputStream is = new FileInputStream("test.txt");
InputStream bis = new BufferedInputStream(is);
DecryptInputStream dis = new DecryptInputStream(bis);
Scanner scanner = new Scanner(System.in);
System.out.print("Enter key: ");
long key = scanner.nextLong();
scanner.close();
random = new Random(key);
int c;
while((c = dis.read(random.nextInt())) != -1)
{
System.out.print((char)c);
}
dis.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class DecryptInputStream extends FilterInputStream
{
protected DecryptInputStream(InputStream in)
{
super(in);
}
public int read(int z) throws IOException
{
int c = super.read();
if(c == -1) return -1;
return c^z;
}
}
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class EncryptOutputStream extends FilterOutputStream
{
public EncryptOutputStream(OutputStream o)
{
super(o);
}
public void write(int c, int z) throws IOException
{
super.write(c^z);
}
}
When decrypting, you need to mask out the higher order bits after XOR-ing.
public int read(int z) throws IOException
{
int c = super.read();
if(c == -1) return -1;
return (c^z) & 0xFF; /* Only use lowest 8 bits */
}
When you encrypt, you are producing a 32-bit int
. The most significant 16 bits contain the upper bits of the key stream, while the lower 16 contain the ciphertext. However, only the lowest 8 bits are written to the output file, because OutputStream
writes bytes. For the message "hello," this happens to be okay, since all of those characters are encoded in the lowest 7 bits anyway.
When you decrypt, however, you are again creating a 32-bit int
where the upper 24-bits are filled with bits from the keystream. When you cast to a char
, bits 31-16 are discarded, but bits 15-8 are filled with junk, and lowest bits, 0-8, should contain the original character. Because the upper bits of the char are garbage, you get garbled output.