简体   繁体   中英

Why is RC4 not able to handle large amount of encrypted data?

I have following code that decrypts a file.

package encryption;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class Decrypter {

    private static final String PASSWORD = "t_9Y#i@eT[h3}-7!";
    private static final String KEY_ALGORITHM = "PBEWithMD5AndDES";
    private static final String CIPHER_ALGORITHM = "RC4"; //Using Salsa20 or HC256 solves the problem
    private static final String PROVIDER = "BC";

    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        File inputFile = new File(args[0]);
        File outputFile = new File(args[1]);

        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
        SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD.toCharArray()));

        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, key);

        InputStream inputStream = new FileInputStream(inputFile);
        OutputStream outputStream = new FileOutputStream(outputFile);

        CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);

        byte []byteBuffer = new byte[(int)inputFile.length()]; 
        cipherInputStream.read(byteBuffer);
        outputStream.write(byteBuffer); //Only 512bytes of decrypted data is written to file, the rest becomes null
        outputStream.close();
    }

}

My question is what am I doing wrong? Why doesn't RC4 decrypt a block of size more than 512 bytes.

RC4 is a stream cipher, so it can decode any amount of data. Your issue is that InputStreams are not read in massive chunks. Normally you loop around the read call until there is no more data left to read and use a small buffer. See the documentation of read() .

This could be implemented as

while(true) {
    int numRead = cipherInputStream.read(byteBuffer);
    if(numRead == -1)
        break;
    outputStream.write(byteBuffer, 0, numRead);
}

@Michael Lowman has the correct answer, but I'd thought I'd show another way just to advertise a feature of the DataInputStream class.

You can the read-it-all-in-one-go behavior, like a perl slurp, by using the DataInputStream.readFully() method. In your example, you can read in the bytes with this method, then write them out and decrypt by using as CipherOutputStream instead of a CipherInputStream.

Consider the following fragment as an example:

    byte[] byteBuffer = new byte[(int) inputFile.length()];
    DataInputStream dis = new DataInputStream(inputStream);
    dis.readFully(byteBuffer);
    dis.close();
    CipherOutputStream cos = new CipherOutputStream(outputStream, cipher);
    cos.write(byteBuffer);
    cos.close();

InputStream.read only returns a certain amount of data, you are supposed to loop until the stream is empty. However I suggest you use commons-io's org.apache.commons.io.FileUtils.copyInputStreamToFile(InputStream, File) to copy the streams rather than rolling your own...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM