【问题标题】:Why is RC4 not able to handle large amount of encrypted data?为什么 RC4 无法处理大量加密数据?
【发布时间】:2011-09-17 05:12:03
【问题描述】:

我有以下解密文件的代码。

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

}

我的问题是我做错了什么?为什么 RC4 不解密大小超过 512 字节的块。

【问题讨论】:

    标签: java security encryption bouncycastle jce


    【解决方案1】:

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

    【讨论】:

    • 我认为这不是问题所在。您能否指出任何 Oracle 文档中说 FileInputStream 将读取任意数量的数据?这是一个阻塞流。即 read 方法调用在读取所需的数据量之前不会返回,在这种情况下恰好是整个文件的大小。
    • @Monika Michael:javadocs 没有这么说。他们说该方法会阻塞,直到 一些 数据可用。无论如何,您是从 CipherInputStream 读取,而不是 FileInputStream。
    • @GregS CipherInputStream 链接到 FileInputStream。 :-) 只有在通过网络而不是从磁盘读取数据时,才会在数据可用之前进行阻塞。
    • @MonikaMichael:只有在通过网络而不是从磁盘读取数据时,才会在数据可用之前进行阻塞。这完全是错误的。
    【解决方案2】:

    RC4 是一种流密码,因此它可以解码任意数量的数据。您的问题是 InputStreams 没有被大量读取。通常,您循环读取调用,直到没有更多数据可供读取并使用小缓冲区。请参阅documentation of read()

    这可以实现为

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

    【讨论】:

    • 确实如此。永远不要丢弃 read() 的返回值。
    • 如果问题出在 InputStream 上,为什么更改密码算法可以解决问题。尝试使用 salsa20 运行代码,它运行得很好。
    • 我认为这可能是密码块模式的问题。仍在阅读...
    【解决方案3】:

    @Michael Lowman 有正确的答案,但我想我会展示另一种方式来宣传 DataInputStream 类的功能。

    您可以通过使用DataInputStream.readFully() 方法来实现read-it-all-in-one-go 行为,例如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();
    

    【讨论】:

    • 该程序应该解密数据。通过 CipherOutStream 写入将对其进行加密。其次,DataInputStream 用于读取 Java 原语,如整数或字符。所以我不明白你的意思。为什么读取原始字节不起作用?
    • @Monika Michael:CipherOutputStream 将根据其 Cipher 参数的初始化方式转换数据。在这种情况下,它以解密模式初始化,因此它进行解密。 DataInputStream 不应该做任何比它的方法定义要做的事情更多或更少的事情。 readFully() 方法具有您一直在寻找的语义,并错误地声称存在于 FileInputStream.read() 中。
    • 谢谢 GregS,现在我确实认为问题出在 InputStream 上。与 FileInputStream 读取等于提供给它的缓冲区长度的数据不同,CipherInputStream 只读取一个数据块。 RC4 的默认块大小是 512 字节,这是我得到的。
    • 我知道来晚了,但这怎么能适应大文件(无法完全加载到内存中的文件)?循环的模式应该是什么?
    • @mariosk89:您只需使用相当标准的 I/O 循环,其中有一个小缓冲区,例如8192,从InputStream中填充,写入到outputstream中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 2018-08-30
    • 1970-01-01
    • 1970-01-01
    • 2011-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多