Okay, so when I encrypt anything, lets say in this case a file, with my computer. It works fine encrypting and decrypting said file. However if encrypt the file with my computer then send it too my android and run the same decryption code I wrote it wont decrypt the file properly. It doesn't show any errors, however the file is clearly not decrypted properly.
So I've learned that Bouncy Castle is quite different than JVM/JDK standard encryption library, so I'm not 100% sure if that's the problem. However it would make sense. The issue is that if it is I don't know how to use the JVM/JDK library for encryption on my android. I don't want too use the Bouncy Castle library on my computer.
public static void encrypt(File source, File dest, String password){
try{
FileInputStream inFile = new FileInputStream(source.getPath());
FileOutputStream outFile = new FileOutputStream(dest.getPath());
byte[] salt = new byte[8], iv = new byte[16];
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
SecretKey secretKey = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] input = new byte[64];
int bytesRead;
while((bytesRead = inFile.read(input)) != -1){
byte[] output = cipher.update(input, 0, bytesRead);
if(output != null){
outFile.write(output);
}
}
byte[] output = cipher.doFinal();
if(output != null){
outFile.write(output);
}
inFile.close();
outFile.flush();
outFile.close();
}catch(Exception e){
e.printStackTrace();
}
}
public static void decrypt(File source, File dest, String password){
try{
byte[] salt = new byte[8], iv = new byte[16];
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
SecretKey tmp = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
FileInputStream fis = new FileInputStream(source.getPath());
FileOutputStream fos = new FileOutputStream(dest.getPath());
byte[] in = new byte[64];
int read;
while((read = fis.read(in)) != -1){
byte[] output = cipher.update(in, 0, read);
if(output != null){
fos.write(output);
}
}
byte[] output = cipher.doFinal();
if(output != null){
fos.write(output);
}
fis.close();
fos.flush();
fos.close();
}catch(Exception e){
e.printStackTrace();
}
}
The outcome would be too be able too encrypt any file with my computer using the standard JVM/JDK encryption library and being able too decrypt the same file with my android.
However if there is a different library other than Bouncy Castle that would work with android I'm open for ideas.
I've faced similar issue. First check if your encryption and decryption is setup properly.
If those are okay, then you should convert the encrypted text to Base64 or similar encoding.
Then decode the Base64 encoding first on the mobile and then decrypt the file.
The issue occurs because, some characters are not suitable for saving or transfering between mediums. They also gets affected by the underlying OS. Even if a bit is changed, your whole decrypted text will be changed, that might be the case here
ANDROID CODE
public static void decrypt(File source, File dest, String password){
try{
Base64InputStream fis = new Base64InputStream(new DataInputStream(new FileInputStream(source.getPath())), Base64.DEFAULT);
FileOutputStream fos = new FileOutputStream(dest.getPath());
byte[] salt = new byte[8], iv = new byte[16];
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
SecretKey tmp = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] in = new byte[64];
int read;
while((read = fis.read(in)) != -1){
byte[] output = cipher.update(in, 0, read);
if(output != null){
fos.write(output);
}
}
byte[] output = cipher.doFinal();
if(output != null){
fos.write(output);
}
fis.close();
fos.flush();
fos.close();
}catch(Exception e){
e.printStackTrace();
}
}
Regular JVM
public static void encrypt(File source, File dest, String password){
try{
FileInputStream inFile = new FileInputStream(source.getPath());
OutputStream outFile = Base64.getEncoder().wrap(new FileOutputStream(dest.getPath()));
byte[] salt = new byte[8], iv = new byte[16];
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
SecretKey secretKey = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] input = new byte[64];
int bytesRead;
while((bytesRead = inFile.read(input)) != -1){
byte[] output = cipher.update(input, 0, bytesRead);
if(output != null){
outFile.write(output);
}
}
byte[] output = cipher.doFinal();
if(output != null){
outFile.write(output);
}
inFile.close();
outFile.flush();
outFile.close();
}catch(Exception e){
e.printStackTrace();
}
}
public static void decrypt(File source, File dest, String password){
try{
InputStream fis = Base64.getDecoder().wrap(new FileInputStream(source.getPath()));
//FileInputStream fis = new FileInputStream(source.getPath());
FileOutputStream fos = new FileOutputStream(dest.getPath());
byte[] salt = new byte[8], iv = new byte[16];
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
SecretKey tmp = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] in = new byte[64];
int read;
while((read = fis.read(in)) != -1){
byte[] output = cipher.update(in, 0, read);
if(output != null){
fos.write(output);
}
}
byte[] output = cipher.doFinal();
if(output != null){
fos.write(output);
}
fis.close();
fos.flush();
fos.close();
}catch(Exception e){
e.printStackTrace();
}
}
Related
I referred this and trying to do file decryption with base64 decoding
My requirement is to encode data with base64 during encryption and decode data with base64 during decryption.
But im facing below error:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at java.base/com.sun.crypto.provider.CipherCore.doFinal(Unknown Source)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(Unknown Source)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(Unknown Source)
at java.base/javax.crypto.Cipher.doFinal(Unknown Source)
at aes.DecryptNew.decryptNew(DecryptNew.java:124)
at aes.DecryptNew.main(DecryptNew.java:32)
Also im confused on how to perform decryption in chunks.
Please suggest me issues in this code.
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.Arrays;
import java.util.Base64;
public class DecryptNew {
public static void main(String[] args) {
String plaintextFilename = "D:\\\\plaintext.txt";
String ciphertextFilename = "D:\\\\plaintext.txt.crypt";
String decryptedtextFilename = "D:\\\\plaintextDecrypted.txt";
String password = "testpass";
writeToFile();
String ciphertext = encryptfile(plaintextFilename, password);
System.out.println("ciphertext: " + ciphertext);
decryptNew(ciphertextFilename, password, decryptedtextFilename);
}
static void writeToFile() {
BufferedWriter writer = null;
try
{
writer = new BufferedWriter( new FileWriter("D:\\\\plaintext.txt"));
byte[] data = Base64.getEncoder().encode("hello\r\nhello".getBytes(StandardCharsets.UTF_8));
writer.write(new String(data));
}
catch ( IOException e)
{
}
finally
{
try
{
if ( writer != null)
writer.close( );
}
catch ( IOException e)
{
}
}
}
public static String encryptfile(String path, String password) {
try {
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream(path.concat(".crypt"));
final byte[] pass = Base64.getEncoder().encode(password.getBytes());
final byte[] salt = (new SecureRandom()).generateSeed(8);
fos.write(Base64.getEncoder().encode("Salted__".getBytes()));
fos.write(salt);
final byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = concatenateByteArrays(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("SHA-1");
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
int b;
byte[] d = new byte[8];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
cos.flush();
cos.close();
fis.close();
System.out.println("encrypt done " + path);
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return path;
}
static void decryptNew(String path,String password, String outPath) {
byte[] SALTED_MAGIC = Base64.getEncoder().encode("Salted__".getBytes());
try{
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream(outPath);
final byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
final byte[] inBytes = Files.readAllBytes(Paths.get(path));
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);
final byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1");
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
String contentDecoded = new String(Base64.getDecoder().decode(clear));
fos.write(contentDecoded.getBytes());
fos.close();
System.out.println("Decrypt is completed");
}catch(Exception e){
e.printStackTrace();
}
}
public static byte[] concatenateByteArrays(byte[] a, byte[] b) {
return ByteBuffer
.allocate(a.length + b.length)
.put(a).put(b)
.array();
}
}
As mentioned in my first comment: Encryption and decryption use different encodings for the password (Base64 in encryption, ASCII in decryption).
In addition the prefix is Base64 encoded in both encryption and decryption, so prefix plus salt are larger than one block (16 bytes), and therefore the subsequent length determination of the ciphertext during decryption fails, as it is assumed that the ciphertext starts with the 2nd block.
Both issues can be solved if the same encoding is used for the password in encryption and decryption (e.g. ASCII) and the prefix is ASCII encoded, e.g. for encryption:
...
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] salt = (new SecureRandom()).generateSeed(8);
fos.write(SALTED_MAGIC);
fos.write(salt);
...
and for decryption:
...
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] prefix = fis.readNBytes(8);
byte[] salt = fis.readNBytes(8);
...
However the current encryption method does not Base64 encode (only the prefix is Base64 encoded which is rather counterproductive, s. above).
Even a Base64 encoded plaintext does not change this, as the ciphertext itself is not Base64 encoded.
Considering your statement My requirement is to encode data with base64 during encryption and decode data with base64 during decryption and the OpenSSL format used, I assume that you want to decrypt a ciphertext that has been encrypted with the -base64 option, analogous to OpenSSL, i.e. with
openssl enc -aes-256-cbc -base64 -pass pass:testpass -p -in sample.txt -out sample.crypt
Here, prefix, salt and ciphertext are concatenated on byte level and the result is then Base64 encoded. To achieve this, your implementation posted in the question could be changed as follows:
static void decrypt(String path, String password, String outPath) {
try (FileInputStream fis = new FileInputStream(path);
Base64InputStream bis = new Base64InputStream(fis, false, 64, "\r\n".getBytes(StandardCharsets.US_ASCII))) { // from Apache Commons Codec
// Read prefix and salt
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] prefix = bis.readNBytes(8);
if (!Arrays.equals(prefix, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
byte[] salt = bis.readNBytes(8);
// Derive key and IV
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1"); // Use digest from encryption
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
// Decrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
try (CipherInputStream cis = new CipherInputStream(bis, cipher);
FileOutputStream fos = new FileOutputStream(outPath)) {
int length;
byte[] buffer = new byte[1024];
while ((length = cis.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
}
System.out.println("Decrypt is completed");
} catch (Exception e) {
e.printStackTrace();
}
}
As I already mentioned in the comments to the linked question, the processing in chunks can easily be implemented with the class CipherInputStream. The Base64 decoding can be achieved with the class Base64InputStream of the Apache Commons Codec, as also addressed by Michael Fehr. This is a convenient way to perform Base64 decoding and decryption together. If the data does not need to be Base64 decoded (e.g. if the -base64 option was not used during encryption) the Base64InputStream class can simply be omitted.
As previously stated in the comments, small files do not need to be processed in chunks. This is only necessary when the files become large relative to the memory. However, since your encryption method processes the data in chunks, it is consistent that the decryption does the same.
Note that the encryption posted in the question is not compatible with the result of the above OpenSSL statement, i.e. the encryption would have to be adapted if necessary (analogous to the decryption posted above).
Edit: The encryptfile() method posted in the question creates a file containing the Base64 encoded prefix, the raw salt and the raw ciphertext. For key derivation the Base64 encoded password is applied. The method is used to encrypt a Base64 encoded plaintext. The following method is the counterpart of encryptfile() and allows the decryption and Base64 decoding of the plaintext:
static void decryptfile(String path, String password, String outPath) {
try (FileInputStream fis = new FileInputStream(path)) {
// Read prefix and salt
byte[] SALTED_MAGIC = Base64.getEncoder().encode("Salted__".getBytes());
byte[] prefix = new byte[SALTED_MAGIC.length];
fis.readNBytes(prefix, 0, prefix.length);
if (!Arrays.equals(prefix, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
byte[] salt = new byte[8];
fis.readNBytes(salt, 0, salt.length);
// Derive key and IV
final byte[] pass = Base64.getEncoder().encode(password.getBytes());
byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1"); // Use digest from encryption
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
// Decrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
try (CipherInputStream cis = new CipherInputStream(fis, cipher);
Base64InputStream bis = new Base64InputStream(cis, false, -1, null); // from Apache Commons Codec
FileOutputStream fos = new FileOutputStream(outPath)) {
int length;
byte[] buffer = new byte[1024];
while ((length = bis.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
}
System.out.println("Decrypt is completed");
} catch (Exception e) {
e.printStackTrace();
}
}
The decryptfile()-method writes the Base64 decoded plaintext to the file in outPath. To get the Base64 encoded plaintext simply remove the Base64InputStream instance from the stream hierarchy.
As already explained in the comment, this method is incompatible to OpenSSL, i.e. it can't decrypt ciphertexts generated with the OpenSSL statement posted above (with or without -base64 option). To decrypt a ciphertext generated with OpenSSL with -base64 option use the decrypt() method.
I am trying to decrypt files by Java and below codes work. However it will have OutOfMemory exception when decrypting large files. I tried to change to cipher.update but the program will freeze without any responding.
How can I change this from doFinal to update?
public File decryptDataFile(File inputFile, File outputFile, File keyFile, String correlationId) {
try {
Security.addProvider(new BouncyCastleProvider());
String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
byte[] byteInput = this.getFileInBytes(inputFile);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
byte[] salt = new byte[8];
System.arraycopy(byteInput, 8, salt, 0, 8);
SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
cipher.init(Cipher.DECRYPT_MODE, fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100)));
byte[] data = cipher.doFinal(byteInput, 16, byteInput.length-16);
OutputStream os = new FileOutputStream(outputFile);
os.write(data);
os.close();
if(outputFile.exists()) {
return outputFile;
} else {
return null;
}
} catch (IOException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException | InvalidKeyException | InvalidKeySpecException | IllegalBlockSizeException | BadPaddingException e) {
logger.WriteLog(appConfig.getPlatform(), "INFO", alsConfig.getProjectCode(), correlationId, alsConfig.getFunctionId(), "SCAN_DECRYPT", e.getClass().getCanonicalName() + " - " + e.getMessage() );
return null;
}
}
My non-working version:
Security.addProvider(new BouncyCastleProvider());
String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
byte[] byteInput = this.getFileInBytes(inputFile);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
byte[] salt = new byte[8];
System.arraycopy(byteInput, 8, salt, 0, 8);
SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
cipher.init(Cipher.DECRYPT_MODE, fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100)));
FileInputStream fis = new FileInputStream(inputFile);
FileOutputStream fos = new FileOutputStream(outputFile);
CipherInputStream cis = new CipherInputStream(fis, cipher);
int b;
byte[] d = new byte[8];
while((b = cis.read(d)) != -1) {
fos.write(d, 0, b);
}
fos.flush();
fos.close();
cis.close();
if(outputFile.exists()) {
return outputFile;
} else {
return null;
}
Foreword: I couldn't decrypt with your original method a file that was encrypted with your openssl-command
openssl enc -aes-256-cbc -e -salt -in ${tarFile} -out ${encTarFile} -pass file:./${KEY_RANDOM}
but the following method should decode even large files similar to your original method - I tested files up to 1 GB size.
Edit: Regarding the OpenSSL statement, it's worth mentioning that since v1.1.0 the default digest has changed from MD5 to SHA256,
so for higher versions the -md MD5 option must be set explicitly for compatibility with the Java code. (thanks to #Topaco).
Please keep in mind that I don't care about correct file paths for
new FileInputStream(inputFile.toPath().toString())
and
new FileOutputStream(outputFile.toPath().toString())
as I'm working locally and with my folder, maybe you have to change the code to "find" your files. As well there is no exception handling in this example.
The code line
byte[] ibuf = new byte[8096];
is defining the buffer that is used - a larger buffer makes the decryption faster but consumes more memory (8096 means 8096 byte compared to 1 Gbyte when reading the complete file into memory and causing the out of memory error).
public static File decryptDataFileBuffered(File inputFile, File outputFile, File keyFile, String correlationId) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException {
Security.addProvider(new BouncyCastleProvider());
String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
byte[] salt = new byte[8];
byte[] salted = new byte[8]; // text SALTED__
try (FileInputStream in = new FileInputStream(inputFile.toPath().toString()); // i don't care about the path as all is lokal
FileOutputStream out = new FileOutputStream(outputFile.toPath().toString())) // i don't care about the path as all is lokal
{
byte[] ibuf = new byte[8096]; // thats the buffer used - larger is faster
int len;
in.read(salted);
in.read(salt);
SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
SecretKey secretKey = fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100));
System.out.println("secretKey length: " + secretKey.getEncoded().length + " data: " + bytesToHex(secretKey.getEncoded()));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
while ((len = in.read(ibuf)) != -1) {
byte[] obuf = cipher.update(ibuf, 0, len);
if (obuf != null)
out.write(obuf);
}
byte[] obuf = cipher.doFinal();
if (obuf != null)
out.write(obuf);
} catch (IOException | BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
if (outputFile.exists()) {
return outputFile;
} else {
return null;
}
}
Edit2: As commented by #Topaco the usage of CipherInput/OutputStream shortens the code and makes it better readable, so here is the code:
public static File decryptDataFileBufferedCipherInputStream (File inputFile, File outputFile, File keyFile, String correlationId) throws
IOException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, InvalidKeyException
{
Security.addProvider(new BouncyCastleProvider());
String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
byte[] salt = new byte[8];
byte[] salted = new byte[8]; // text SALTED__
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
try (FileInputStream in = new FileInputStream(inputFile.toPath().toString()); // i don't care about the path as all is lokal
CipherInputStream cipherInputStream = new CipherInputStream(in, cipher);
FileOutputStream out = new FileOutputStream(outputFile.toPath().toString())) // i don't care about the path as all is lokal
{
byte[] buffer = new byte[8192];
in.read(salted);
in.read(salt);
SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
SecretKey secretKey = fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100));
System.out.println("secretKey length: " + secretKey.getEncoded().length + " data: " + bytesToHex(secretKey.getEncoded()));
cipher.init(Cipher.DECRYPT_MODE, secretKey);
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
}
if (outputFile.exists()) {
return outputFile;
} else {
return null;
}
}
I have an encrypted video with the key and I want to decrypt that video file with Key but I am not able to decrypt the video file. I try 2 3 methods but Non of these works for me.
Crypto.Class
public class Crypto {
static void fileProcessor(int cipherMode,String key,File inputFile,File outputFile){
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(key.getBytes("UTF-8"));
keyGenerator.init(128, secureRandom);
SecretKey secretKey = keyGenerator.generateKey();
// Key secretKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(cipherMode, secretKey);
FileInputStream inputStream = null;
byte[] inputBytes = new byte[0];
try {
inputStream = new FileInputStream(inputFile);
inputBytes = new byte[(int) inputFile.length()];
inputStream.read(inputBytes);
} catch (IOException e) {
e.printStackTrace();
}
byte[] outputBytes = cipher.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
} catch (NoSuchPaddingException | NoSuchAlgorithmException
| InvalidKeyException | BadPaddingException
| IllegalBlockSizeException | IOException e) {
e.printStackTrace();
}
}
public static String Decrypt(String filePath) {
String key = "my_encrypted_key";
// File inputFile = new File("text.txt");
File encryptedFile = new File(filePath);
String decryptedFilePath="";
//check special guid folder if not exist create
//get the selected video file path with settings path if exist decrypt if not exist show message
File decryptedFile = new File(decryptedFilePath);
try {
//Crypto.fileProcessor(Cipher.ENCRYPT_MODE,key,inputFile,encryptedFile);
Crypto.fileProcessor(Cipher.DECRYPT_MODE,key,encryptedFile,decryptedFile);
Log.d("success", "Decrypt: success");
return decryptedFile.getAbsolutePath();
} catch (Exception ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
return "";
}
}
But this does not work for me and I try another one.
public static void TEST2(String orgFile) {
try {
String password = "estudies741852963#123";
File outFile_dec = new File(Environment.getExternalStorageDirectory()+"/testVideo/abc.mp4");
if(!outFile_dec.isDirectory()) {
outFile_dec.mkdir();
}
if(!outFile_dec.exists()){
outFile_dec.createNewFile();
}
// reading the salt
// user should have secure mechanism to transfer the
// salt, iv and password to the recipient
FileInputStream saltFis = new FileInputStream(orgFile);
byte[] salt = new byte[8];
saltFis.read(salt);
saltFis.close();
// reading the iv
FileInputStream ivFis = new FileInputStream(orgFile);
byte[] iv = new byte[16];
ivFis.read(iv);
ivFis.close();
SecretKeyFactory factory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 1000,
256);
SecretKey tmp = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
// file decryption
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
InputStream fis = new FileInputStream(orgFile);
OutputStream fos = new FileOutputStream(outFile_dec);
// byte[] in = new byte[1024];
// int read;
// while ((read = fis.read(in)) != -1) {
// byte[] output = cipher.update(in, 0, read);
// if (output != null)
// fos.write(output);
// }
//
// byte[] output = cipher.doFinal();
// if (output != null)
// fos.write(output);
// fis.close();
// fos.flush();
// fos.close();
// System.out.println("File Decrypted.");
fos = new CipherOutputStream(fos, cipher);
int count = 0;
byte[] buffer = new byte[1024];
while ((count = fis.read(buffer)) >= 0) {
fos.write(buffer, 0, count);
}
fis.close();
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
In this method, its always shows on exception saying no such directory. And I try another method also But none of these works for me.
Tried this solution to decrypt my video
Java 256-bit AES Password-Based Encryption
How do i decrypt a file in Android with AES?
I'm able to cipher using PBEWithHmacSHA256AndAES_128 , but when i try to decipher it doesnt do anything, my encrypt code is:
PBEKeySpec keySpec = new PBEKeySpec(passwordServer.toCharArray());
SecretKeyFactory kf;
kf = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
secretKey = kf.generateSecret(keySpec);
byte[] ivBytes = {0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20};
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
PBEParameterSpec spec = new PBEParameterSpec(salt, 20, ivSpec);
c = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
c.init(Cipher.ENCRYPT_MODE, secretKey, spec);
params = c.getParameters();
and my decrypt code is the following:
Cipher c = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
c.init(Cipher.DECRYPT_MODE, secretKey, params);
FileInputStream fis = new FileInputStream("repositorios/login.cif");
FileOutputStream fos = new FileOutputStream("repositorios/login2.txt");
CipherOutputStream cos = new CipherOutputStream(fos, c);
byte[] b = new byte[16];
int i = fis.read(b);
while (i != -1) {
cos.write(b, 0, i);
i = fis.read(b);
}
cos.close();
fis.close();
After i run the code, the file "login2.txt" is empty.
Am I doing something wrong while using this algorithm?
Thank you!
EDIT:
1. where i store the salt:
BufferedWriter out = new BufferedWriter
(new FileWriter("repositorios/salt.txt",true));
BufferedReader in=new BufferedReader
(new FileReader("repositorios/salt.txt"));
byte[]salt;
String l = in.readLine();
if(l != null){
salt = l.getBytes();
} else {
salt = new byte[16];
RANDOM.nextBytes(salt);
out.write(new String (salt, "UTF-8"));
}
my chiper code:
FileOutputStream fos = new FileOutputStream("repositorios/login.cif");
CipherOutputStream cos = new CipherOutputStream(fos, c);
out = new BufferedWriter(new FileWriter("repositorios/login.txt",true));
String aEncriptar = user + ":" + passwd +"\n";
byte[] b= aEncriptar.getBytes();
cos.write(b, 0, b.length);
cos.close();
I am trying to make a encryption and decryption app but in decryption method I got javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
byte[] key = ("Sh").getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKeySpec = new SecretKeySpec(key, "AES");
cipher = Cipher.getInstance("AES");
public void dec (String dir)
{
try{
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
String cleartextFile = dir;
FileInputStream fis = new FileInputStream(cleartextFile);
CipherInputStream cis = new CipherInputStream(fis, cipher);
FileOutputStream fos = new FileOutputStream(dir);
block = new byte[6];
while ((i = cis.read(block)) != -1) {
fos.write(block, 0, i);
}
fos.close();
}
catch(Exception ex)
{
Toast.makeText(MainActivity.this, "Chiper Error"+ex, Toast.LENGTH_LONG).show();
}
}