为什么RC4无法处理大量的加密数据?

问题描述:

我有以下解密文件的代码。为什么RC4无法处理大量的加密数据?

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#[email protected][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(); 
    } 

} 

我的问题是我做错了什么?为什么RC4不能解密超过512字节的数据块?

@迈克尔Lowman的具有正确的答案,但我想我会表现出另一种方式只是为了做广告DataInputStream类的功能。

通过使用DataInputStream.readFully()方法,您可以像“perl slurp”这样的“全读一遍”行为。在您的示例中,您可以使用此方法读取字节,然后将它们写出来并使用CipherOutputStream而不是CipherInputStream进行解密。

考虑以下片段为例:

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只返回一定数量的数据,你应该循环,直到流为空。不过我建议你使用commons-io的org.apache.commons.io.FileUtils.copyInputStreamToFile(InputStream, File)复制流而不是滚动你自己的...

+0

我不认为这是问题。您能否指出任何说明FileInputStream将读取任意数量数据的Oracle文档?这是一个阻塞流。即读取方法调用将不会返回,直到所需的数据量被读取,在这种情况下恰好是整个文件的大小。 – 2011-06-18 06:38:44

+1

@Monika Michael:javadocs不会这么说。他们说该方法阻塞,直到有*数据可用。无论如何,你正在读取CipherInputStream,而不是FileInputStream。 – 2011-06-18 14:58:53

+0

@GregS CipherInputStream链接到FileInputStream。 :-)阻塞直到数据变为可用只有在通过网络而不是从磁盘读取数据时才成立。 – 2011-06-19 04:19:12

RC4是一个流密码,所以它可以解码任何数量的数据。您的问题是InputStreams不能以大块读取。通常情况下,您会循环读取调用,直到没有更多数据要读取并使用小缓冲区。请参阅documentation of read()

这可以被实现为

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