【问题标题】:RSA block by block encryption produces blank output for files larger than 1kbRSA 逐块加密为大于 1kb 的文件生成空白输出
【发布时间】:2017-06-30 07:22:07
【问题描述】:

我不会为 AES 或其他加密打开此线程,因为这是我将用来加密 AES 和其他加密的密钥。我从 StackOverflow 和其他几个站点收集了几个代码并对其进行了编辑以适合我的程序,但是在尝试使用 RSA 进行逐块加密时,我只能加密大小为 1 KB 的小文本文件。文本文件的加密和解密工作正常。但是,加密图片和大于 1 KB 的任何文件都会产生空白 加密文件。

如果有人可以帮助我指出这段代码导致文件大于 1 KB 导致空白输出文件/加密文件的位置,我想寻求帮助。这段代码适用于 AES,但我不确定为什么它不适用于 RSA 加密。问题从Encrypt_File 开始在它读取它的循环周围的某个地方。

代码:

public static final String ALGORITHM = "RSA";
private static final short KEY_LENGTH = 1024;
private static final short KEY_BYTES = 32;
private static final String CIPHER_EXTENSION = ".cgfile";

public void Encrypt_File(Path path_fileToEncrypt) {
    //get file name and the new file name
    String fileName = path_fileToEncrypt.getFileName().toString();
    String encryptedFileName = fileName+CIPHER_EXTENSION;
    String pathToEncryptedFile = path_fileToEncrypt.getParent()+File.separator+encryptedFileName;

    //attempt to open the public key
    try (ObjectInputStream publicKeyFile = new ObjectInputStream(new FileInputStream(PUBLIC_KEY_FILE))) {
        //load the key
        PublicKey publicKey = (PublicKey) publicKeyFile.readObject();

        // load file to encrypt and inputstream
        FileInputStream fileInputStream = new FileInputStream(new File(path_fileToEncrypt.toString()));

        // init the cipher
        final Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);

        CipherOutputStream cos = new CipherOutputStream(new FileOutputStream(pathToEncryptedFile),cipher);
        byte[] buffer = new byte[KEY_BYTES];
        int count;
        while((count = fileInputStream.read(buffer))>0){
            cos.write(buffer, 0, count);
        }

        //close
        fileInputStream.close();
        cos.close();
        //delete fileToEncrypt since we have the encrypted file now
        DeleteFile(new File(path_fileToEncrypt.toString()));
        System.out.println("Finished encrypting "+ fileName +" It's new file name is "+ encryptedFileName);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public static void main(String[] args) {
    try {

        CGcipher rsaencrypt = new CGcipher();
        Path pathTosecret = Paths.get(System.getProperty("user.dir"), "pic.png");
        // Encrypt the string using the public key
        rsaencrypt.Encrypt_File(pathTosecret);
        //rsaencrypt.Decrypt_File(pathTosecret);

    } catch (Exception e) {
        System.out.println(e.toString());
    }
}

【问题讨论】:

  • @LukePark 好吧,它仍然可以保护机密性。完整性和当然真实性不会受到保护,但 AES/CBC 也不是这种情况。当然,这在空间和 CPU 时间上都是低效的,防止定时攻击可能是一个问题。但最糟糕的 5 件事是什么?不。
  • 请注意,代码格式(标识符大小写,在运算符前后不使用空格),尤其是异常处理让我有点畏缩。您可能想研究一下(您是否知道可以在 try-with-resources 中放置多个语句,并且您可以在 try-with-resources 之前初始化密码?我还将分开读取公钥并使用getEncoded 和密钥工厂而不是对象序列化。
  • @MaartenBodewes 我确实使用了多个 try-with-resources 但不在本节中,因为我仍在尝试找出导致问题的原因,是的,代码看起来很糟糕。关于运算符之前/之后的空间,我会注意到这一点。显然,我习惯用 C++ 做所有的空格。对于异常处理,我把它做成了一个简单的 catch 块,这样我就知道这部分代码中发生了什么,虽然我知道我可以让它独立,但现在我只想让它工作,然后让它看起来更好。这更多的是个人喜好,但我会再次注意到这一点
  • @MaartenBodewes 只是为了清除一些东西。通过将公钥和getEncoded 和密钥工厂分开,您的意思是制作一个单独的函数来处理它们吗?
  • 是的,我就是这个意思。如果您使用getEncoded() 存储密钥,它将被存储为二进制SubjectPublicKeyInfo 结构,该结构应该与许多软件兼容(当然除了Mickeysoft,让他们使用专有结构代替)。

标签: java file encryption cryptography output


【解决方案1】:

RSA 不适用于批量加密,因为与 AES 等对称算法相比,它的速度要慢得多(速度要慢 1000 倍以上)。一个块中可以使用 RSA 加密的数据量取决于密钥大小和填充可能使用的任何数据。

当您需要 RSA 的两个密钥,同时需要加密超过一个 RSA 块中的内容时,您通常会使用Hybrid encryption ,您使用随机对称密钥加密数据部分,然后使用 RSA 加密加密密钥。这样你就可以得到对称密钥的速度,以及 RSA 的两个密钥。但是,如果您真的不需要使用不同的密钥进行加密和解密,那么您根本不应该使用 RSA

由于 RSA 不适用于批量加密,因此当您尝试加密超过一个块中的内容时,标准实现无法处理它 - 没有为 chaining of encryption blocks 实现逻辑RSA。这将是开箱即用的对称密码的句柄。

【讨论】:

    【解决方案2】:

    虽然算法 RSA 在内部被 Sun 提供商扩展为表示 "RSA/ECB/PKCS1Padding"。但是,实际上并未使用 ECB 模式。它应该被命名为"RSA/None/PKCS1Padding"。所以 CipherOutputStream 不能将 RSA 用于更大的数据,因为它没有实现任何分组密码模式。

    不幸的是CipherOutputStream 有“吃”异常的讨厌习惯。这就是为什么你会得到一个空字符串而不是一个明确的警告。


    Java 的一个问题是检查异常。尽管它们当然有其好处,但特殊情况不应成为函数定义的一部分。检查异常是生成可靠/无错误代码的好方法但错误的方法。

    IOException 可能会被流抛出,这一点非常清楚。并非所有流都确实执行 I/O,因此异常是错误的。但是因为IOException 是一个已检查异常,它必须成为write 方法定义的一部分。另一方面,安全异常有一个名为GeneralSecurityExceptionchecked 基类。这些异常不能被流抛出,因为它们没有在方法定义中定义。


    现在有一些方法可以解决这个问题,例如创建一个额外的 secureWrite 方法,它确实会引发异常。通常的write 方法可能会抛出一个新的RuntimeException 派生异常,该异常携带原始GeneralSecurityException。不过,这些解决方案似乎已经逃脱了密码流的设计者;相反,他们选择完全删除异常,或者可能在预期另一个异常的地方抛出 IOException

    因此,应极其谨慎地使用密码流。用专有实现替换它们似乎是最好的。

    【讨论】:

    • 感谢您的接受。请注意,我还赞成Ebbe's answer,这完美地解释了为什么使用 RSA 进行批量加密是一个坏主意。我建议坚持这个建议——当然也支持答案:)
    猜你喜欢
    • 2014-04-29
    • 2014-10-31
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 2016-11-15
    • 1970-01-01
    • 1970-01-01
    • 2017-04-05
    相关资源
    最近更新 更多