问题
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