RSA block by block encryption produces blank output for files larger than 1kb

风流意气都作罢 提交于 2019-12-04 05:18:26

问题


I'm not opening this thread for AES or other encryptions because this is what I'm going to use to encrypt the keys of AES and other encryptions. I've gathered several codes from StackOverflow and a few other sites and edited it to fit my program however while trying to do a block by block encryption using RSA problem is I can only encrypt small text files of size 1 kilobyte. Encryption and Decryption of text files are working fine. However, encrypting pictures and ANY files larger than 1 kilobyte will produce a blank encrypted file.

I would like to ask help if anyone can help me point out where the piece of code causes files greater than 1 kilobyte causes a blank output file/encrypted file. This piece of code works for AES but I'm not sure why it doesn't work on RSA encryption. The problem starts at Encrypt_File somewhere around the loop where it reads it.

Code:

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

回答1:


Although the algorithm RSA is internally expanded to mean "RSA/ECB/PKCS1Padding" by the Sun provider. However, ECB mode is not actually used. It should have been named "RSA/None/PKCS1Padding". So RSA cannot be used for larger data by CipherOutputStream as it doesn't implement any block cipher mode.

Unfortunately CipherOutputStream has the nasty habit of "eating" exceptions. This is why you get an empty string instead of a clear warning.


One of the problems with Java are checked exceptions. Although they certainly have their benefits, exceptional circumstances should not be part of a function definition. Checked exceptions are a well-meant but wrong method to produce reliable / error free code.

This is made perfectly clear by the IOException that may be thrown by streams. Not all streams do actually perform I/O so the exception is wrong. But because IOException is a checked exception it must be part of the write method definition. On the other hand, security exceptions have a checked base class called GeneralSecurityException. These exceptions cannot be thrown by a stream because they are not defined in the method definition.


Now there are ways around this, for instance creating an additional secureWrite method that does throw the exception. The usual write method could throw a new RuntimeException derived exception carrying the original GeneralSecurityException. These solutions seem to have escaped the designer of the cipher streams though; instead they opted remove the exceptions altogether or possibly throw an IOException where another exception is expected.

As a result the cipher streams should be used with extreme caution. Replacing them with a proprietary implementation seems best.




回答2:


RSA is not ment for bulk encryption as it's quit slow compared to symmetric algorithms like AES (it's more than a factor of 1000 slower). The amount of data that can be encrypted with RSA in one block is dependent on the key size and any data that might be used by padding.

When you need the two keys of RSA and at the same time need to encrypt more than what can be in one RSA block, you would usually use Hybrid encryption, where you encrypt the data part with a random symmetric key, and then encrypt the encryption key with RSA. This way you get the speed of the symmetric key, and the two keys of the RSA. But if you don't really need to have different keys for encryption and decryption then you properly should not use RSA at all.

As RSA is not ment for bulk encryption, the standard implementation's can't handle it when you try to encrypt more than what can be in one block - no logic is implemented for chaining of encryption blocks with RSA. This would be handle for symmetric ciphers out of the box.



来源:https://stackoverflow.com/questions/44840831/rsa-block-by-block-encryption-produces-blank-output-for-files-larger-than-1kb

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!