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.
Related
I am facing the following issue while working with Java cryptography.
error decrjavax.crypto.BadPaddingException: Given final block not
properly padded. Such issues can arise if a bad key is used during
decryption.
I have checked all possible answers, but couldn't find the exact reason behind this.
One observation that when i use AES/CBC/NoPadding in place of AES/CBC/PKCS5Padding, i can execute it successfully.
here is my code snippet.
package demo;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
public class TestEncryption {
private static final int BUFFER_SIZE = 32;
private static final int KEY_ITERATIONS = 65535;
private static final int DEFAULT_KEY_BITS = 128;
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String TRANSFORMATION = "AES";
private static final String PBKDF_2_WITH_HMAC_SHA_256 = "PBKDF2WithHmacSHA256";
private static final int IV_SIZE = 16;
private final Cipher ecipher;
private final Cipher dcipher;
private SecretKey secretKey;
/**
* Initialize the ciphers using the given key.
* #param key
* #param keyBits
*/
public TestEncryption(String key, int keyBits) {
byte[] salt = new byte[8];
if (key.length() < 8) {
throw new IllegalArgumentException("key must contain 8 characters or more");
}
for (int i = 0; i < 8; i = i + 1) {
salt[i] = ((byte) key.charAt(i));
}
char[] password = key.toCharArray();
int keyLength = DEFAULT_KEY_BITS;
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF_2_WITH_HMAC_SHA_256);
if (keyBits == 256) {
keyLength = 256;
}
KeySpec spec = new PBEKeySpec(password, salt, KEY_ITERATIONS, keyLength);
secretKey = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), TRANSFORMATION);
ecipher = Cipher.getInstance(ALGORITHM);
dcipher = Cipher.getInstance(ALGORITHM);
} catch (InvalidKeySpecException | NoSuchPaddingException | NoSuchAlgorithmException e) {
throw new RuntimeException("Failed to initialize encryption.", e);
}
}
public void encryptFile(File src, File dest){
try {
InputStream inputStream = new FileInputStream(src);
OutputStream outputStream = new FileOutputStream(dest);
CipherOutputStream cipherOutputStream= new CipherOutputStream(outputStream, ecipher);
// Generating IV.
byte[] iv = new byte[IV_SIZE];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// First write the IV at the beginning of the encrypted file.
outputStream.write(iv, 0, IV_SIZE);
System.out.println("key " + secretKey);
// Initialize cipher with IV
ecipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
// Encrypt input file and write in to output
while ((bytesRead = inputStream.read(buffer)) > 0) {
cipherOutputStream.write(buffer, 0, bytesRead);
}
} catch (InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {
System.out.println("error encryption" + e.getMessage());
e.printStackTrace();
}
}
public void decryptFile(File srcFile, File destFile) {
try (
InputStream is = new FileInputStream(srcFile);
OutputStream out = new FileOutputStream(destFile);
CipherInputStream cis = new CipherInputStream(is, dcipher)
) {
// Extract IV
byte[] iv = new byte[IV_SIZE];
is.read(iv, 0, IV_SIZE);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Initialize cypher with IV
dcipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = cis.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
} catch ( InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {
System.out.println("error decr" + e.getMessage());
e.printStackTrace();
}
}
}
package demo;
import java.io.*;
public class Client {
public static void main(String [] args){
File tempFile =null, src = null, dest = null;
try {
tempFile = new File("temp.txt");
src = new File("C:\\Users\\x\\Desktop\\test.txt");
dest = new File("C:\\Users\\x\\Desktop\\out.txt");
TestEncryption encryption = new TestEncryption("helloworld", 256);
encryption.encryptFile(src, tempFile);
encryption.decryptFile(tempFile, dest);
}
finally {
tempFile.delete();
//src.delete();
//dest.delete();
}
}
}
Your error is the way to use your streams when encrypting:
For a CipherOutputStream it is essential to be closed at the end because only when it is closed the final padding can be written.
In your code however the cipherOutputStream instance is never closed. hence the padding is never written to the encrypted file.
Of course when decrypting the file there is no padding where a padding should be and you are getting the BadPaddingException.
Therefore you should change the encyrption to this:
public void encryptFile(File src, File dest) {
try (InputStream inputStream = new FileInputStream(src);
OutputStream outputStream = new FileOutputStream(dest)) {
try (CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, ecipher)) {
// Generating IV.
byte[] iv = new byte[IV_SIZE];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// First write the IV at the beginning of the encrypted file.
outputStream.write(iv, 0, IV_SIZE);
System.out.println("key 0x" + new BigInteger(1, secretKey.getEncoded()).toString(16));
// Initialize cipher with IV
ecipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
// Encrypt input file and write in to output
while ((bytesRead = inputStream.read(buffer)) >= 0) {
cipherOutputStream.write(buffer, 0, bytesRead);
}
}
} catch (InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {
System.out.println("error encryption" + e.getMessage());
e.printStackTrace();
}
}
public void decryptFile(File srcFile, File destFile) {
try (InputStream is = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile)) {
try (CipherInputStream cis = new CipherInputStream(is, dcipher)) {
// Extract IV
byte[] iv = is.readNBytes(IV_SIZE);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Initialize cypher with IV
dcipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = cis.read(buffer)) >= 0) {
out.write(buffer, 0, bytesRead);
}
}
} catch (InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {
System.out.println("error decr" + e.getMessage());
e.printStackTrace();
}
}
There is another reason that probably the content which you need to description, but the arguments is null when passed to the mathod, it will be throw you a badpaddedException as well.
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);
}
I wanted to try out the code from Java In A Nutshell book (3rd edition), but when I'm trying to run it, I'm getting java.lang.ArrayIndexOutOfBoundsException: 1 error. I found that info:
Thrown to indicate that an array has been accessed with an illegal index. The index is either negative or greater than or equal to the size of the array.
but unfortunately I am still not able to find and fix it
package tripledes;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.spec.*;
import java.io.*;
public class TripleDES {
public static void main(String[] args) {
try {
try {
Cipher c = Cipher.getInstance("DESede");
} catch (Exception e) {
System.err.println("Installing SunJCE provicer");
Provider sunjce = new com.sun.crypto.provider.SunJCE();
Security.addProvider(sunjce);
}
File keyfile = new File(args[1]);
if (args[0].equals("-g")) {
System.out.println("Generating key. This may take some time...");
System.out.flush();
SecretKey key = generateKey();
writeKey(key, keyfile);
System.out.println("Done");
System.out.println("Secret key written to " + args[1] + ". Protect that file!");
} else if (args[0].equals("-e")) {
SecretKey key = readKey(keyfile);
encrypt(key, System.in, System.out);
} else if (args[0].equals("-d")) {
SecretKey key = readKey(keyfile);
decrypt(key, System.in, System.out);
}
} catch (Exception e) {
System.err.println(e);
System.err.println("Usage: java " + TripleDES.class.getName() + "-d|-e|-g <keyfile>");
}
}
public static SecretKey generateKey() throws NoSuchAlgorithmException {
KeyGenerator keygen = KeyGenerator.getInstance("DESede");
return keygen.generateKey();
}
public static void writeKey(SecretKey key, File f) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede");
DESedeKeySpec keyspec = (DESedeKeySpec) keyfactory.getKeySpec(key, DESedeKeySpec.class);
byte[] rawkey = keyspec.getKey();
FileOutputStream out = new FileOutputStream(f);
out.write(rawkey);
out.close();
}
public static SecretKey readKey(File f) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException {
DataInputStream in = new DataInputStream(new FileInputStream(f));
byte[] rawkey = new byte[(int) f.length()];
in.readFully(rawkey);
in.close();
DESedeKeySpec keyspec = new DESedeKeySpec(rawkey);
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede");
SecretKey key = keyfactory.generateSecret(keyspec);
return key;
}
public static void encrypt(SecretKey key, InputStream in, OutputStream out)
throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IOException {
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.ENCRYPT_MODE, key);
CipherOutputStream cos = new CipherOutputStream(out, cipher);
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
cos.write(buffer, 0, bytesRead);
}
cos.close();
java.util.Arrays.fill(buffer, (byte) 0);
}
public static void decrypt(SecretKey key, InputStream in, OutputStream out)
throws NoSuchAlgorithmException, InvalidKeyException, IOException, IllegalBlockSizeException,
NoSuchPaddingException, BadPaddingException {
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(cipher.update(buffer, 0, bytesRead));
}
out.write(cipher.doFinal());
out.flush();
}
}
Could be when you access the args array. Such as:
File keyfile = new File(args[1]);
If you have no arguments to your program then this would be an ArrayIndexOutOfBoundsException.
I have been trying for two days to decrypt a file whith the private key containing 'lolilol' after having encrypted it with the public key rsa 4096. I then get padding errors, I tried everything to do, I get the error Javax.crypto.BadPaddingException: Decryption error.
Even after reading the doc on the padding I did not succeed: encryption works fine, but decryption contains an error.
Here is my code:
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import javax.crypto.Cipher;
import sun.misc.BASE64Encoder;
public class GenerateRSAKeys{
private Key pubKey;
private Key privKey;
public static void main(String[] args)
{
String input = "C:\\Users\\toto\\Desktop\\nomFichier_entrant.ext";
String output = "C:\\Users\\toto\\Desktop\\nomFichier_entrant.ext.enc";
String dec = "C:\\Users\\toto\\Desktop\\nomFichier_entrant.ext.dec";
String publicKeyFilename = "C:\\Users\\toto\\Desktop\\HR_pubkey_prd.pem";
String privateKeyFilename = "C:\\Users\\toto\\Desktop\\PE_privkey_prd.pem";
GenerateRSAKeys generateRSAKeys = new GenerateRSAKeys();
/* if (args.length < 2)
{
System.err.println("Usage: java "+ generateRSAKeys.getClass().getName()+
" Public_Key_Filename Private_Key_Filename");
System.exit(1);
}
publicKeyFilename = args[0].trim();
privateKeyFilename = args[1].trim(); */
generateRSAKeys.generate(publicKeyFilename, privateKeyFilename);
//generateRSAKeys.encrypt(input, output);
generateRSAKeys.encrypt(input, output);
generateRSAKeys.decrypt(output, dec);
}
private void generate (String publicKeyFilename, String privateFilename){
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// Create the public and private keys
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
BASE64Encoder b64 = new BASE64Encoder();
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
/* SecureRandom random = new SecureRandom();
keyGen.initialize(4096, random);
KeyPair pair = keyGen.generateKeyPair();
pubKey = pair.getPublic();
privKey = pair.getPrivate(); */
SecureRandom random = createFixedRandom();
generator.initialize(4096, random);
KeyPair pair = generator.generateKeyPair();
pubKey = pair.getPublic();
privKey = pair.getPrivate();
System.out.println("publicKey : " + b64.encode(pubKey.getEncoded()));
System.out.println("privateKey : " + b64.encode(privKey.getEncoded()));
BufferedWriter out = new BufferedWriter(new FileWriter(publicKeyFilename));
out.write(b64.encode(pubKey.getEncoded()));
out.close();
out = new BufferedWriter(new FileWriter(privateFilename));
out.write(b64.encode(privKey.getEncoded()));
out.close();
}
catch (Exception e) {
System.out.println(e);
}
}
public static SecureRandom createFixedRandom()
{
return new FixedRand();
}
private static class FixedRand extends SecureRandom {
MessageDigest sha;
byte[] state;
FixedRand() {
try
{
this.sha = MessageDigest.getInstance("SHA-1");
this.state = sha.digest();
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException("can't find SHA-1!");
}
}
public void nextBytes(byte[] bytes){
int off = 0;
sha.update(state);
while (off < bytes.length)
{
state = sha.digest();
if (bytes.length - off > state.length)
{
System.arraycopy(state, 0, bytes, off, state.length);
}
else
{
System.arraycopy(state, 0, bytes, off, bytes.length - off);
}
off += state.length;
sha.update(state);
}
}
}
public void encrypt(String input, String output) {
File outputFile;
FileInputStream inputStream;
FileOutputStream outputStream;
Cipher cipher;
byte[] inputBytes;
byte[] outputBytes;
try {
outputFile = new File(output);
inputStream = new FileInputStream(input);
outputStream = new FileOutputStream(outputFile);
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
inputBytes = new byte[(int) input.length()];
inputStream.read(inputBytes);
outputBytes = cipher.doFinal(inputBytes);
outputStream.write(outputBytes);
// System.out.println(new String(inputBytes, "UTF-8"));
System.out.println("encrypt");
System.out.println(new String(outputBytes, "UTF-8"));
System.out.println("fin encrypt");
} catch (Exception e) {
e.printStackTrace();
}
}
public void decrypt(String input, String output) {
File outputFile;
FileInputStream inputStream;
FileOutputStream outputStream;
Cipher cipher;
byte[] inputBytes;
byte[] outputBytes;
try {
outputFile = new File(output);
inputStream = new FileInputStream(input);
outputStream = new FileOutputStream(outputFile);
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privKey);
inputBytes = new byte[(int) input.length()];
inputStream.read(inputBytes);
outputBytes = cipher.doFinal(inputBytes);
outputStream.write(outputBytes);
// System.out.println(new String(inputBytes, "UTF-8"));
System.out.println("decrypt");
System.out.println(new String(outputBytes, "UTF-8"));
System.out.println("fin decrypt");
} catch (Exception e) {
e.printStackTrace();
}
}
}
You have multiple issues in your code:
You're never closing the file streams. If you don't do that, it might happen that no data is actually written. If the ciphertext is not (fully) written then the decryption will obviously fail.
A FileInputStream doesn't give an accurate measure of how much data the underlying file contains. You have to use the File class for that:
File inputFile = new File(input);
FileInputStream inputStream = new FileInputStream(inputFile);
FileOutputStream outputStream = new FileOutputStream(output);
byte[] inputBytes = new byte[(int) inputFile.length()];
When you're reading file contents, you must always check how much you read and use exactly that amount:
int readBytes = inputStream.read(inputBytes);
byte[] outputBytes = cipher.doFinal(inputBytes, 0, readBytes);
Always use a fully qualified Cipher string. Cipher.getInstance("RSA"); may result in different ciphers depending on the default security provider. In OpenJDK it defaults to Cipher.getInstance("RSA/ECB/PKCS1Padding");. Nowadays, you should use OAEP instead of the default PKCS#1 v1.5 padding. So you should probably use Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");.
Here is the full code of those two methods:
public void encrypt(String input, String output) {
File inputFile;
FileInputStream inputStream;
FileOutputStream outputStream;
Cipher cipher;
byte[] inputBytes;
byte[] outputBytes;
try {
System.out.println("encrypt");
cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
inputFile = new File(input);
inputStream = new FileInputStream(inputFile);
outputStream = new FileOutputStream(output);
inputBytes = new byte[(int) input.length()];
int readBytes = inputStream.read(inputBytes);
outputBytes = cipher.doFinal(inputBytes, 0, readBytes);
outputStream.write(outputBytes);
System.out.println("fin encrypt");
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void decrypt(String input, String output) {
File inputFile;
FileInputStream inputStream;
FileOutputStream outputStream;
Cipher cipher;
byte[] inputBytes;
byte[] outputBytes;
try {
System.out.println("decrypt");
cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privKey);
inputFile = new File(input);
inputStream = new FileInputStream(inputFile);
outputStream = new FileOutputStream(output);
inputBytes = new byte[(int) inputFile.length()];
int readBytes = inputStream.read(inputBytes);
outputBytes = cipher.doFinal(inputBytes, 0, readBytes);
outputStream.write(outputBytes);
System.out.println("Decryption result: " + new String(outputBytes, "UTF-8"));
System.out.println("fin decrypt");
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
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.