Last block incomplete in decryption while decrypting a file - java

I have few AES encrypted videos which I am required to decrypt before playing with ExoPlayer. These videos are going to be included in assets folder of the app while few of them needs to be on SD Card.
I have been using a Utility Class provided to decrypt the videos, but it seems that it's not working properly.
static String key = "xxx"; // key should be exactly 16bit long
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES";
public static void encrypt(File inputFile, File outputFile) throws CryptoException {
doCrypto(Cipher.ENCRYPT_MODE, inputFile, outputFile);
}
public static void decrypt(File inputFile, File outputFile) throws CryptoException {
doCrypto(Cipher.DECRYPT_MODE, inputFile, outputFile);
}
private static void doCrypto(int cipherMode, File inputFile, File outputFile) throws CryptoException {
try {
Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(cipherMode, secretKey);
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()];
inputStream.read(inputBytes);
byte[] outputBytes = cipher.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
} catch (IOException | NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
throw new CryptoException("Error encrypting/decrypting file", e);
}
}
For the videos in assets folder, I tried to directly pass the InputStream returned from getAssets().open(filePath), however it gave some error related to padding. So I instead copied the video file to Internal Storage using the following code first
public static void copyFromAssets(Context context, String filePath, File outputFile) {
InputStream in = null;
OutputStream out = null;
try {
in = context.getAssets().open(filePath);
out = new FileOutputStream(outputFile);
copyFile(in, out);
} catch (IOException e) {
e.printStackTrace();
LumberJack.e("tag", "Failed to copy asset file: " + filePath);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
// NOOP
}
}
}
}
private static void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
The video was there, but since it's an encrypted video I couldn't play it. when I tried to decrypt the extracted video file, I got the following exception -
javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:853)
at javax.crypto.Cipher.doFinal(Cipher.java:1502)
at com.example.utilities.CryptoUtils.doCrypto(CryptoUtils.java:42)
at com.example.utilities.CryptoUtils.decrypt(CryptoUtils.java:29)
at com.example.activities.HomeActivity.onVideoPlayButtonClick(HomeActivity.java:107)
at java.lang.reflect.Method.invoke(Native Method)
at org.greenrobot.eventbus.EventBus.invokeSubscriber(EventBus.java:485)
at org.greenrobot.eventbus.EventBus.postToSubscription(EventBus.java:420)
at org.greenrobot.eventbus.EventBus.postSingleEventForEventType(EventBus.java:397)
at org.greenrobot.eventbus.EventBus.postSingleEvent(EventBus.java:370)
at org.greenrobot.eventbus.EventBus.post(EventBus.java:251)
at com.example.viewmodels.BaseDataLevelItemView$1.onClick(BaseDataLevelItemView.java:65)
at android.view.View.performClick(View.java:5210)
at android.view.View$PerformClick.run(View.java:21169)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5451)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
I am not sure what am I doing wrong here. The same decryption code have been in use in the earlier version of the app with same videos, but giving issues now. I have checked with some other answers on Stackoverflow related to the same exception, but most of them was related to encoding issue in the cipher text. I don't have a string here, but a file.
How can I find out, what's wrong here and how can I correct it, if you've already found out the problem?

Thanks to the suggestions from Ebbe M. Pedersen, I actually tried to encrypt a file and tried decrypting with the same code which worked.
So, it was confirmed that there was some other algorithm or something else was used to encrypt the files. Luckily I found it in the old commits.

Related

android decrypt file using fingerprint

With the code below I can easily encrypt a file and decrypt it, using the password that the user enters. I am now imploding the use of the fingerprint to enter my app. When the fingerprint is correct, I want to decrypt the file. But how do I do if the user has not entered the password? is there a relatively safe way to do this? Thanks
public class CryptoUtils {
private static final String ALGORITHM = "AES";
private static final String ENCRYPTION_IV = "4e5Wa71fYoT7MFEX";
private static String ENCRYPTION_KEY = "";
public static void encrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
}
public static void decrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
}
private static void doCrypto(int cipherMode, String key, File inputFile, File outputFile)
throws CryptoException {
try {
ENCRYPTION_KEY = key;
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(cipherMode, makeKey(), makeIv());
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()];
inputStream.read(inputBytes);
byte[] outputBytes = cipher.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
//Log.d("cur__", "Encryption/Decryption Completed Succesfully");
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException | IOException ex) {
throw new CryptoException("Error encrypting/decrypting file " + ex.getMessage(), ex);
} catch (InvalidAlgorithmParameterException ex) {
Logger.getLogger(CryptoUtils.class.getName()).log(Level.SEVERE, null, ex);
}
}
private static AlgorithmParameterSpec makeIv() {
return new IvParameterSpec(ENCRYPTION_IV.getBytes(StandardCharsets.UTF_8));
}
private static Key makeKey() {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] key = md.digest(ENCRYPTION_KEY.getBytes(StandardCharsets.UTF_8));
//Log.d("Lenghtis", new SecretKeySpec(key, ALGORITHM).getEncoded().length + "");
return new SecretKeySpec(key, ALGORITHM);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
You first need to generate a Key. You can use the following function
fun generateKey(storeKey: StoreKey): SecretKey {
val keyGenerator =
KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "MyKeyStore")
val keyGenParameterSpec = KeyGenParameterSpec.Builder(storeKey.Alias,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.setRandomizedEncryptionRequired(false)
.build()
keyGenerator.init(keyGenParameterSpec)
return keyGenerator.generateKey()
}
You need setUserAuthenticationRequired(true) for biometric authentication.
Then you can provide this key to your biometric authenticator to authenticate the user and in return you will receive cipher from it.
Then you can use that cipher (encrypt cipher OR decrypt cipher) to perform encrypt/decrypt data directly using this cipher.
androidx.biometric:biometric:1.0.0-beta01
like this
val biometricCallbacks = object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
result.cryptoObject?.cipher?.let { cipher ->
val encryptedData = cipherUtils.encryptAES(dataToEncrypt, cipher)
dismiss()
}
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
if (errorCode == BiometricPrompt.ERROR_CANCELED || errorCode == BiometricPrompt.ERROR_USER_CANCELED) {
dismiss()
}
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Log.e("BIOMETRIC", "FAILED")
}
}
val biometricPrompt = BiometricPrompt(this, biometricExecutor, biometricCallbacks)
val biometricPromptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("APP")
.setSubtitle("Enable Biometric for Encryption")
.setDescription("")
.setNegativeButtonText("Cancel")
.build()
biometricPrompt.authenticate(
biometricPromptInfo,
BiometricPrompt.CryptoObject(
cipherUtils.getBioEncryptCipher(keyStoreUtils.generateNewKey(StoreKey.Pass, true))
)
)
You can encrypt users password and keep it securely in database somewhere and use the biometric authentication. Since the key you are generating is strictly bind to users biometrics, users encrypted password will not get compromised.
Here's the androidx.biometric demo app for using biometric-bound keys (key can only be unlocked by strong biometric sensors).
Basically
Generate a key that's only usable after being authenticated via biometrics, with the associated capabilities you'd like the key to be able to perform (encrypt, decrypt, etc)
Wrap the cryptographic operation you wish to perform in a CryptoObject. The key will only become usable after a user has authenticated with biometrics
Invoke BiometricPrompt#authenticate(CryptoObject)
Use the key after onAuthenticationSucceeded
For completeness, there's also authentication-bound keys (key can be unlocked by biometrics OR device credential), which become available for use by your app whenever the user has authenticated within t seconds, where t is specified in KeyGenParameterSpec.Builder#setUserAuthenticationValidityDurationSeconds. You can see an example usage here.

Android java.io.IOException: write failed: EBADF (Bad file number)

I have read several posts about the EBADF error that does not resolve my issue. What makes my case unique is that I am trying to write to the getFilesDir, that should be writable by the application.
I am getting the EBADF error when executing the encrypt method. The file argument is created using:
new File(mContext.getFilesDir(), "file.dat")
Where is a listing of the encrypt method:
public static void encrypt(File file, String password, List<Object> objects) {
byte[] salt = generateSalt();
byte[] iv = generateIV();
Cipher c = createCipher(password, salt, Cipher.ENCRYPT_MODE, iv);
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(salt);
fos.write(iv);
try (CipherOutputStream cos = new CipherOutputStream(fos, c);
ObjectOutputStream oos = new ObjectOutputStream(cos)) {
for (Object o : objects) {
oos.writeObject(o);
}
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Can anyone see why I am getting the exception?
Here is the exception log:
Caused by: java.lang.RuntimeException: java.io.IOException: write failed: EBADF (Bad file number)
at za.co.lot24media.password.util.EncryptUtil.encrypt(EncryptUtil.java:69)
at za.co.lot24media.password.store.Store.save(Store.java:94)
at za.co.lot24media.password.store.Store.createSamples(Store.java:179)
at za.co.lot24media.password.store.Store.load(Store.java:76)
at za.co.lot24media.password.activity.login.LoginAction$2.doInBackground(LoginAction.java:62)
at za.co.lot24media.password.activity.login.LoginAction$2.doInBackground(LoginAction.java:55)
at android.os.AsyncTask$2.call(AsyncTask.java:292)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 
at java.lang.Thread.run(Thread.java:818) 
Caused by: java.io.IOException: write failed: EBADF (Bad file number)
at libcore.io.IoBridge.write(IoBridge.java:502)
at java.io.FileOutputStream.write(FileOutputStream.java:186)
at java.io.OutputStream.write(OutputStream.java:82)
at javax.crypto.CipherOutputStream.close(CipherOutputStream.java:129)
at za.co.lot24media.password.util.EncryptUtil.encrypt(EncryptUtil.java:64)
** EDITED **
The issue seems to be with the ObjectOutputStream writing to the CipherOutputStream. When I remove the ObjectOutputStream from the encrypt() method, the method succeeds. The code below works:
public static void encrypt(File file, String password, StoreDataRecord storeDataRecord) {
byte[] salt = generateSalt();
byte[] iv = generateIV();
Cipher c = createCipher(password, salt, Cipher.ENCRYPT_MODE, iv);
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(salt);
fos.write(iv);
try (CipherOutputStream cos = new CipherOutputStream(fos, c)) {
cos.write(new byte[10]);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
I used byte[10] just to write arbitrary data to the stream.
** EDIT 2 **
The following solution also works, writing the data to a ByteArrayOutputStream first:
public static void encrypt(File file, String password, StoreDataRecord storeDataRecord) {
byte[] salt = generateSalt();
byte[] iv = generateIV();
Cipher c = createCipher(password, salt, Cipher.ENCRYPT_MODE, iv);
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(salt);
fos.write(iv);
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
CipherOutputStream cos = new CipherOutputStream(fos, c)) {
oos.writeObject(storeDataRecord.getVersion());
oos.writeObject(storeDataRecord.getItems());
cos.write(bos.toByteArray());
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
As i see your encrypt method call on doInBackground so it can produce some complicated situation like move to another fragment or create two instance of fragment and this make android confuse. Best way is you ignore encrypt method when onDestroy() called.
And make sure your streams not closed before you use them.
Hope it's helps

java encrypting large file cause java.lang.OutOfMemoryError: Java heap space,possible solutions

I am writing java program to encrypt video files,with small video files it performs as expected however when i try on a huge file approximately 800 Megabyte,it throws an exception
java.lang.OutOfMemoryError: Java heap space.
I have researched on the topic getting more information about the garbage collector from here and reviewing similar problems here. According to the best chosen answer how can you "Encrypt a byte at a time": With block ciphers encryption is by the block (AES: 16-bytes)`.
The contents of my source code
main.java
public static void main(String[] args) {
String key = "Mary has one cat";
File inputFile = new File("C:\\Users\\xyz\\Music\\test\\-.mp4");
File encryptedFile = new File("C:\\Users\\xyz\\Music\\test\\-.mp4.encrypted");
File decryptedFile = new File("C:\\Users\\xyz\\Music\\test\\decrypted.mp4");
try {
CryptoUtils.encrypt(key, inputFile, encryptedFile);
CryptoUtils.decrypt(key, encryptedFile, decryptedFile);
} catch (CryptoException ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
CryptoUtils.java
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES";
public static void encrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
}
public static void decrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
}
private static void doCrypto(int cipherMode, String key, File inputFile,
File outputFile) throws CryptoException {
try {
Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(cipherMode, secretKey);
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()];
inputStream.read(inputBytes);
byte[] outputBytes = cipher.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
} catch (NoSuchPaddingException | NoSuchAlgorithmException
| InvalidKeyException | BadPaddingException
| IllegalBlockSizeException | IOException ex) {
throw new CryptoException("Error encrypting/decrypting file", ex);
}
}
CryptoException.java
public class CryptoException extends Exception {
public CryptoException() {
}
public CryptoException(String message, Throwable throwable) {
super(message, throwable);
}}
I think you should look into using CipherOutputStream. This way you won't need to load the file into memory:
https://docs.oracle.com/javase/9/docs/api/javax/crypto/CipherOutputStream.html
Don't process the whole file at once: use a buffer:
try (FileInputStream inputStream = new FileInputStream(inputFile);
FileOutputStream outputStream = new FileOutputStream(outputFile)) {
byte[] inputBytes = new byte[4096];
for (int n = inputStream.read(inputBytes); n > 0; n = inputStream.read(inputBytes)) {
byte[] outputBytes = cipher.update(inputBytes, 0, n);
outputStream.write(outputBytes);
}
byte[] outputBytes = cipher.doFinal();
outputStream.write(outputBytes);
}

Java File Encryption AES

PROBLEM: Upload method is working the problem I'm presenting is **encrypt(inputFile, new File("Users/myname/Desktop/example.txt"),key);** instead of writing to my desktop I want it to be stored as a file or var I can use so I can upload to dropbox server. I just do not know how to code this part and do not know what to put so this will execute appropriately.
private byte[] getKeyBytes(final byte[] key) throws Exception {
byte[] keyBytes = new byte[16];
System.arraycopy(key, 0, keyBytes, 0, Math.min(key.length, keyBytes.length));
return keyBytes;
}
public Cipher getCipherEncrypt(final byte[] key) throws Exception {
byte[] keyBytes = getKeyBytes(key);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
return cipher;
}
public void encrypt(File in, File output, byte[] key) throws Exception {
Cipher cipher = getCipherEncrypt(key);
FileOutputStream fos = null;
CipherOutputStream cos = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(in);
fos = new FileOutputStream(output);
cos = new CipherOutputStream(fos, cipher);
byte[] data = new byte[1024];
int read = fis.read(data);
while (read != -1) {
cos.write(data, 0, read);
read = fis.read(data);
System.out.println(new String(data, "UTF-8").trim());
}
cos.flush();
System.out.println("name"+ output);
} finally {
System.out.println("performed encrypt method now closing streams");
cos.close();
fos.close();
fis.close();
}
}
// Working Upload Method To DropBox Cloud
public void uploadFile () throws DbxException, IOException, FileLoadException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException, InvalidParameterSpecException, ClassNotFoundException, NoSuchProviderException, InvalidAlgorithmParameterException, UnsupportedAudioFileException, LineUnavailableException, InterruptedException, ShortBufferException, Exception {
// autheticate if there is a accessToken here if not prompt to login by activating the drop method re-auth..
try{
phoneHome();
}catch(IOException e){
System.out.println("not saving accessToken");
JOptionPane.showMessageDialog(null, "Your Access Information Does Not Exist,\n Please Login"+
"Please Login By Clicking 'OK'");
drop(); // will run auth method for user to login
}
// user picks file to upload with JFileChooser
fc = new JFileChooser();
fc.setMultiSelectionEnabled(true);
fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
int dialog = fc.showSaveDialog(this);
if (dialog == JFileChooser.APPROVE_OPTION) {
inputFile = fc.getSelectedFile();
inputFile.getName();
inputFile.getAbsoluteFile();
String nameOf = inputFile.getName();
System.out.println(" File: " + inputFile);
/* calling out inputFIle from out JFileChooser and now passing it into
our method and encrytping and outputing it.
*/
encrypt(inputFile, new File("Users/myname/Desktop/example.txt"),key);
File selected = new File ("?");
inputStream = new FileInputStream (selected);
uploadedFile = client.uploadFile( "/" +selected ,DbxWriteMode.add(), inputFile.length(), inputStream);
/*
Original works without encryption
File selectedFile = new File(nameOf+inputFile);
inputStream = new FileInputStream(inputFile);
*/
// Original uploadedFile = client.uploadFile( "/" +selectedFile ,DbxWriteMode.add(), inputFile.length(), inputStream);
setTitle("Uploading File..");
System.out.println("Uploaded: " + uploadedFile.toString());
JOptionPane.showMessageDialog(null,"File Upload:" + uploadedFile.toString(),
"Success!", JOptionPane.WARNING_MESSAGE);
}
}
Solved... all it was is simply over complicating something that didn't need to be complicated furthermore and have settled for a more simplicit way.
ORIGINAL: encrypt(inputFile, new File("Users/myname/Desktop/example.txt"),key);
WORKING: encrypt(inputFile, new File(inputFile.getName),key);
Eazy Peezy... if anyone has any other suggestions I would like to hear them, thanks.

Decrypting an encrypted file corrupts the file? Android - Java

I am using the following code to encrypt/decrypt:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
public class CryptoUtils {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES";
public static void encrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
}
public static void decrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
}
private static void doCrypto(int cipherMode, String key, File inputFile,
File outputFile) throws CryptoException {
try {
Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PAdding");
cipher.init(cipherMode, secretKey);
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()];
inputStream.read(inputBytes);
byte[] outputBytes = cipher.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException
| InvalidKeyException | BadPaddingException
| IllegalBlockSizeException | IOException ex) {
throw new CryptoException("Error encrypting/decrypting file", ex);
}
}
public static class CryptoException extends Exception {
public CryptoException() {
}
public CryptoException(String message, Throwable throwable) {
super(message, throwable);
}
}
}
I can both encrypt and decrypt a video file.
However, I am not able to play the decrypted file, encrypted by the same code.
The original file: http://www.megafileupload.com/hSTZ/a.mp4
The encrypted file: http://www.megafileupload.com/hSU2/encrypted-a.mp4
The decrypted file: http://www.megafileupload.com/hSU1/decrypted-final-a.mp4
The first 16 bytes of the decrypted file are different from the original file. The length is the same.
You're assuming that:
The file fits into memory.
The file size fits into an int.
read() fills the buffer.
The result of encryption or decryption fits into memory along with the source content.
There's no need for any of these assumptions.
FileInputStream inputStream = new FileInputStream(inputFile);
FileOutputStream outputStream = new FileOutputStream(outputFile);
byte[] buffer = new byte[8192];
int count;
while ((count = in.read(buffer)) > 0)
{
byte[] output = cipher.update(buffer, 0, count);
outputStream.write(output);
}
outputStream.write(cipher.doFinal());
outputStream.close();
inputStream.close();
E&OE but you get the idea. You could make your life even simpler by using CipherInput/OutputStreams, but you lose touch with the crypto exceptions a bit that way.

Categories