Why is RC4 not able to handle large amount of encrypted data? - java

I have following code that decrypts a file.
package encryption;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class Decrypter {
private static final String PASSWORD = "t_9Y#i#eT[h3}-7!";
private static final String KEY_ALGORITHM = "PBEWithMD5AndDES";
private static final String CIPHER_ALGORITHM = "RC4"; //Using Salsa20 or HC256 solves the problem
private static final String PROVIDER = "BC";
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
File inputFile = new File(args[0]);
File outputFile = new File(args[1]);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD.toCharArray()));
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
InputStream inputStream = new FileInputStream(inputFile);
OutputStream outputStream = new FileOutputStream(outputFile);
CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
byte []byteBuffer = new byte[(int)inputFile.length()];
cipherInputStream.read(byteBuffer);
outputStream.write(byteBuffer); //Only 512bytes of decrypted data is written to file, the rest becomes null
outputStream.close();
}
}
My question is what am I doing wrong? Why doesn't RC4 decrypt a block of size more than 512 bytes.

RC4 is a stream cipher, so it can decode any amount of data. Your issue is that InputStreams are not read in massive chunks. Normally you loop around the read call until there is no more data left to read and use a small buffer. See the documentation of read().
This could be implemented as
while(true) {
int numRead = cipherInputStream.read(byteBuffer);
if(numRead == -1)
break;
outputStream.write(byteBuffer, 0, numRead);
}

#Michael Lowman has the correct answer, but I'd thought I'd show another way just to advertise a feature of the DataInputStream class.
You can the read-it-all-in-one-go behavior, like a perl slurp, by using the DataInputStream.readFully() method. In your example, you can read in the bytes with this method, then write them out and decrypt by using as CipherOutputStream instead of a CipherInputStream.
Consider the following fragment as an example:
byte[] byteBuffer = new byte[(int) inputFile.length()];
DataInputStream dis = new DataInputStream(inputStream);
dis.readFully(byteBuffer);
dis.close();
CipherOutputStream cos = new CipherOutputStream(outputStream, cipher);
cos.write(byteBuffer);
cos.close();

InputStream.read only returns a certain amount of data, you are supposed to loop until the stream is empty. However I suggest you use commons-io's org.apache.commons.io.FileUtils.copyInputStreamToFile(InputStream, File) to copy the streams rather than rolling your own...

Related

Java large file AES Encryption is very slow

I'm trying to encrypt a 512 Mb file with AES/CBC algorithm. It is taking around 7 sec which is too much. How to reduce the encryption time and make it faster.
I'm using a fixed key and tried using CipherOutStream as well as cipher.update() instead of cipher.dofinal(). Still, it is taking around 7 secs.
How much time generally it would take to encrypt a 512 MB file with the below encryption. It is taking 6 seconds for me on a Mac with 16 GB memory and 2 GHz Quad-Core Intel Core i5 processor. I'm using JDK 11 for execution. Is it normal or my code is responding slow. Should I be concerned? How to improve the time for encryption.
package com.encrypterdecrypter;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.ClassPathResource;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
#SpringBootApplication
public class DemoApplication {
private static final String SECRET_KEY = "aesencryptionKey";
private static final String initVector = "encryptionIntVec";
public static void main(String[] args) throws Exception {
SpringApplication.run(DemoApplication.class, args);
File inputFile = new ClassPathResource("fivetwelvemb.zip").getFile();
InputStream inputStream = new FileInputStream(inputFile);
SecretKeySpec secretkey = new SecretKeySpec(SECRET_KEY.getBytes("UTF-8"), "AES");
long startTime = System.currentTimeMillis();
OutputStream encryptStream = encryptDecryptBinary(inputStream, Cipher.ENCRYPT_MODE, secretkey);
long endTime = System.currentTimeMillis();
System.out.println("Encryption Time in ms : " + (endTime - startTime));
}
public static OutputStream encryptDecryptBinary(InputStream inputStream, int encryptMode, SecretKeySpec secretkey) throws Exception {
Cipher aesCipher = Cipher.getInstance("AES/CBC/NoPadding");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
aesCipher.init(encryptMode, secretkey);
OutputStream out = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(out, aesCipher);
/* byte[] encryptedData = aesCipher.doFinal(inputStream.readAllBytes()); */
byte[] buf = new byte[8192];
int numRead = 0;
while ((numRead = inputStream.read(buf)) >= 0) {
cipherOutputStream.write(buf, 0, numRead);
}
cipherOutputStream.close();
return out;
}
}
```

Cipher's doFinal() doesn't write bytes

This is my full code:
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Test {
public static void main(String[] args) throws Exception {
encrypt();
decrypt();
}
void encrypt() throws Exception {
Path file = Paths.get("path/to/file");
Path backupFile = file.getParent().resolve(file.getFileName().toString() + ".bak");
Files.deleteIfExists(backupFile);
Files.copy(file, backupFile);
SecureRandom secureRandom = new SecureRandom();
byte[] initializeVector = new byte[96 / Byte.SIZE];
secureRandom.nextBytes(initializeVector);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec p = new GCMParameterSpec(128, initializeVector);
try (FileChannel src = FileChannel.open(backupFile, READ);
FileChannel dest = FileChannel.open(file, WRITE, TRUNCATE_EXISTING)) {
SecretKeySpec secretKeySpec =
new SecretKeySpec(MessageDigest.getInstance("MD5").digest(new byte[]{0x00}), "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, p);
ByteBuffer ivBuffer = ByteBuffer.allocate(Integer.BYTES + cipher.getIV().length);
ivBuffer.putInt(cipher.getIV().length);
ivBuffer.put(cipher.getIV());
ivBuffer.flip();
dest.write(ivBuffer);
ByteBuffer readBuf = ByteBuffer.allocateDirect(8192);
ByteBuffer writeBuf = ByteBuffer.allocateDirect(cipher.getOutputSize(8192));
while (src.read(readBuf) >= 0) {
if (cipher.getOutputSize(8192) > writeBuf.capacity()) {
writeBuf = ByteBuffer.allocateDirect(cipher.getOutputSize(8192));
}
readBuf.flip();
cipher.update(readBuf, writeBuf);
writeBuf.flip();
dest.write(writeBuf);
readBuf.clear();
writeBuf.clear();
}
if (cipher.getOutputSize(0) > writeBuf.capacity()) {
writeBuf = ByteBuffer.allocateDirect(cipher.getOutputSize(0));
}
cipher.doFinal(ByteBuffer.allocate(0), writeBuf);
writeBuf.flip();
dest.write(writeBuf);
Files.delete(backupFile);
} catch (ShortBufferException e) {
//Should not happen!
throw new RuntimeException(e);
}
}
void decrypt() throws Exception {
Path file = Paths.get("path/to/file");
Path backupFile = file.getParent().resolve(file.getFileName().toString() + ".bak");
Files.deleteIfExists(backupFile);
Files.copy(file, backupFile);
try (FileChannel src = FileChannel.open(backupFile, READ);
FileChannel dest = FileChannel.open(file, WRITE, TRUNCATE_EXISTING)) {
ByteBuffer ivLengthBuffer = ByteBuffer.allocate(Integer.BYTES);
src.read(ivLengthBuffer);
ivLengthBuffer.flip();
int ivLength = ivLengthBuffer.getInt();
ByteBuffer ivBuffer = ByteBuffer.allocate(ivLength);
src.read(ivBuffer);
ivBuffer.flip();
byte[] iv = new byte[ivBuffer.limit()];
ivBuffer.get(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec p = new GCMParameterSpec(128, iv);
SecretKeySpec secretKeySpec =
new SecretKeySpec(MessageDigest.getInstance("MD5").digest(new byte[]{0x00}), "AES");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, p);
ByteBuffer readBuf = ByteBuffer.allocateDirect(8192);
ByteBuffer writeBuf = ByteBuffer.allocateDirect(cipher.getOutputSize(8192));
while (src.read(readBuf) >= 0) {
if (cipher.getOutputSize(8192) > writeBuf.capacity()) {
writeBuf = ByteBuffer.allocateDirect(cipher.getOutputSize(8192));
}
readBuf.flip();
cipher.update(readBuf, writeBuf);
writeBuf.flip();
dest.write(writeBuf);
readBuf.clear();
writeBuf.clear();
}
if (cipher.getOutputSize(0) > writeBuf.capacity()) {
writeBuf = ByteBuffer.allocateDirect(cipher.getOutputSize(0));
}
cipher.doFinal(ByteBuffer.allocate(0), writeBuf);
writeBuf.flip();
dest.write(writeBuf);
Files.deleteIfExists(backupFile);
}
}
}
I found a strange issue: if the original file (unencrypted) is bigger than 4KB, upon decrypting, cipher.update(readBuf, writeBuf) will write nothing to the buffer, cipher.doFinal(ByteBuffer.allocate(0), writeBuf) also write nothing, and finally I get my data lost. Every calling to cipher.getOutputSize(8192), increases the result, I don't know why it happen but it may help.
Why is it happening and how can I fix it?
.update() is easy; SunJCE implements the GCM (and CCM) requirement that authenticated decryption not release (any) plaintext if the authentication fails; see How come putting the GCM authentication tag at the end of a cipher stream require internal buffering during decryption? and https://moxie.org/blog/the-cryptographic-doom-principle/ . Because the tag is at the end of the ciphertext, this means it must buffer all the ciphertext until (one of the overloads of) doFinal() is called. (This is why for a large file your reallocation of writeBuf to cipher.getOutputSize(8192) keeps growing as you keep reading and buffering more data.)
.doFinal() is harder; it is supposed to work. However, I've narrowed down the failure: it only happens when you use ByteBuffers not raw byte[] arrays -- which is implemented in javax.crypto.CipherSpi.bufferCrypt rather than dispatching to the implementation class; and the output ByteBuffer has no backing array (i.e. was direct-allocated); and the plaintext is more than 4096 bytes. I'll try to look deeper into why this fails, but in the meantime changing either of the first two fixes it (or limiting your data to 4096 bytes, but presumably you don't want that).

Java AES Padding Encryption Error While Transferring Video over TCP

I am currently doing a project in Java to transfer a video file over TCP from a server to a client. The idea is the server will continue running and listen to incoming connection. Once there is an incoming connection from a client, the server will automatically send a video file to the client. (As of now the IP and file name is hard coded). The idea is so that the file can be copied and played at the same time
It is used locally and will automatically turn on VLC from the receiving computer to play the file being transferred. I have done the transfer part with no issues. The only problem comes up when I try to encrypt/decrypt the file. The code I have are below
Runnable Thread FileTransfer Server
public class FileTransferServer {
public static void main(String[] args) throws Exception {
//Initialize Sockets
int i = 0;
ServerSocket ssock = new ServerSocket(6012);
while (true){
ClientConnection CC;
CC = new ClientConnection(ssock.accept());
Thread t = new Thread(CC);
t.start();
}
}
}
Server Java File
import java.io.BufferedInputStream;
import javax.crypto.Cipher;
import java.io.InputStream;
import java.io.OutputStream;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class ClientConnection implements Runnable
{
private Socket socketPort;
public ClientConnection (Socket socketPort)
{
this.socketPort = socketPort;
}
public void run()
{
try {
DataInputStream input = new DataInputStream(socketPort.getInputStream());
String videoName = input.readUTF();
// automatically get local ip
InetAddress IA = InetAddress.getByName("10.0.0.1");
String key = "Maryhadonecat111";
byte[] keyByte = key.getBytes("UTF-8");
System.out.println(keyByte);
System.out.println(keyByte.toString());
//Specify the file
File file = new File("D:\\Temp\\"+videoName);
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
//Get socket's output stream
OutputStream os = socketPort.getOutputStream();
//Read File Contents into contents array
byte[] contents;
long fileLength = file.length();
long current = 0;
long start = System.nanoTime();
while(current!=fileLength){
int size = 1000000;
if(fileLength - current >= size)
current += size;
else{
size = (int)(fileLength - current);
current = fileLength;
}
contents = new byte[size];
bis.read(contents, 0, size);
//os.write(contents);
os.write(CryptoTest1.doEncrypt(contents,keyByte));
System.out.print("Sending file to "+ socketPort.getInetAddress().toString() +" " +(current*100)/fileLength+"% complete!\n");
}
os.flush();
//File transfer done. Close the socket connection!
socketPort.close();
// ssock.close();
System.out.println("File sent succesfully!");
} catch (Exception e)
{
System.out.println(e);
}
}
}
Client Java File
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import javax.crypto.Cipher;
import java.io.InputStream;
import java.io.OutputStream;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
public class FileTransferClient {
public static void main(String[] args) throws Exception{
requestFile("10.0.0.1", "papa.avi");
}
public static void requestFile(String IP, String videoName) throws Exception{
//Initialize socket
Socket socket = new Socket(InetAddress.getByName(IP), 6012);
DataOutputStream output = new DataOutputStream( socket.getOutputStream());
output.writeUTF(videoName);
String key = "Maryhadonecat111";
byte[] keyByte = key.getBytes("UTF-8");
byte[] contents = new byte[1000000];
//Initialize the FileOutputStream to the output file's full path.
FileOutputStream fos = new FileOutputStream("D:\\Temp2\\"+videoName);
BufferedOutputStream bos = new BufferedOutputStream(fos);
InputStream is = socket.getInputStream();
System.out.println("Receiving File");
ProcessBuilder pb = new ProcessBuilder("C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", "D:\\Temp2\\"+videoName);
Process start = pb.start();
//No of bytes read in one read() call
int bytesRead = 0;
while((bytesRead=is.read(contents))!=-1){
System.out.println("Bytes Received: " + bytesRead);
contents = (CryptoTest1.doDecrypt(contents,keyByte));
bos.write(contents, 0, bytesRead);
}
bos.flush();
socket.close();
System.out.println("File saved successfully!");
}
}
CryptoTest1 Java File
public class CryptoTest1
{
public static byte[] doEncrypt(byte[] msg, byte[] key) throws Exception {
//prepare key
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
//prepare cipher
String cipherALG = "AES/CBC/PKCS5Padding"; // use your preferred algorithm
Cipher cipher = Cipher.getInstance(cipherALG);
String string = cipher.getAlgorithm();
//as iv (Initial Vector) is only required for CBC mode
if (string.contains("CBC")) {
//IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
IvParameterSpec ivParameterSpec = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
} else {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
}
byte[] encMessage = cipher.doFinal(msg);
return encMessage;
}
public static byte[] doDecrypt(byte[] encMsgtoDec, byte[] key) throws Exception {
//prepare key
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
//prepare cipher
String cipherALG = "AES/CBC/PKCS5Padding"; // use your preferred algorithm
Cipher cipher = Cipher.getInstance(cipherALG);
String string = cipher.getAlgorithm();
//as iv (Initial Vector) is only required for CBC mode
if (string.contains("CBC")) {
//IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
IvParameterSpec ivParameterSpec = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
} else {
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
}
byte[] decMsg = cipher.doFinal(encMsgtoDec);
return decMsg;
}
}
Issue
I seem to have no issue encrypting the file and it is being sent over. The issue is decrypting the file. I can't seem to get it working. I've tried a lot of times and most of the errors comes down to "Padding Exception"
I am currently using AES/CBC/PKCS5Padding but I have tried the following
AES/CBC/PKCS5Padding
AES/CBC/NoPadding
AES/ECB/PKCS5Padding
AES/ECB/NoPadding
If I use padding, i will get an exception
javax.crypto.BadPaddingException: Given final block not properly padded
If I do not use padding, I will get an exception of
javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes.
Some other exception I have encountered while I was tinkering
Missing Parameter
java.security.InvalidKeyException: Illegal key size
java.security.InvalidKeyException: Invalid AES key length: 64 bytes
I would like to ask if anyone of you would be willing to point me in a proper direction as to what I am doing wrong. I am still very new to Java, so please assume that I have very little knowledge.
I have searched Stackoverflow for a long time and most encryption questions here are on text files, not on an actual video. If the encryption method I am using is not suitable for video, please let me know if there is a better one.
The IV must be the same for both encryption and decryption. Generally a random IV is created on encryption and this must be provided to the decryption method. One way to handle this is to prefix the encrypted data with with the IV so on decryption it will be available.
Notes:
AES is a block cipher so the input must be an exact multiple of the block size, this is generally accomplished with padding.
A bad padding error generally means that either the key, IV or encrypted data is incorrect, not that the padding is incorrect. The padding is incorrect because the decryption failed.

Keep jumping out java.io.FileNotFoundException

I'm trying out someone else AES file encryption/decryption program, but the problem is it keeps prompt and said that file is not found, but I did put the file in my netbean and put a directory but still not found. What the problem? Here is the code
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Scanner;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
public class AESEncryptor {
public void encrypt(String fname) throws Exception{
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); //using AES-256
SecretKey key = keyGen.generateKey(); //generating key
Cipher aesCipher = Cipher.getInstance("AES"); //getting cipher for AES
aesCipher.init(Cipher.ENCRYPT_MODE, key); //initializing cipher for encryption with key
//creating file output stream to write to file
try(FileOutputStream fos = new FileOutputStream(fname+".aes")){
//creating object output stream to write objects to file
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(key); //saving key to file for use during decryption
//creating file input stream to read contents for encryption
try(FileInputStream fis = new FileInputStream(fname)){
//creating cipher output stream to write encrypted contents
try(CipherOutputStream cos = new CipherOutputStream(fos, aesCipher)){
int read;
byte buf[] = new byte[4096];
while((read = fis.read(buf)) != -1) //reading from file
cos.write(buf, 0, read); //encrypting and writing to file
}
}
}
}
public void decrypt(String fname)throws Exception{
SecretKey key =null;
//creating file input stream to read from file
try(FileInputStream fis = new FileInputStream(fname)){
//creating object input stream to read objects from file
ObjectInputStream ois = new ObjectInputStream(fis);
key = (SecretKey)ois.readObject(); //reading key used for encryption
Cipher aesCipher = Cipher.getInstance("AES"); //getting cipher for AES
aesCipher.init(Cipher.DECRYPT_MODE, key); //initializing cipher for decryption with key
//creating file output stream to write back original contents
try(FileOutputStream fos = new FileOutputStream(fname+".dec")){
//creating cipher input stream to read encrypted contents
try(CipherInputStream cis = new CipherInputStream(fis, aesCipher)){
int read;
byte buf[] = new byte[4096];
while((read = cis.read(buf)) != -1) //reading from file
fos.write(buf, 0, read); //decrypting and writing to file
}
}
}
}
public static void main(String[] args) throws Exception {
AESEncryptor obj = new AESEncryptor();
File file = new File("C://Users/Jasmine/Documents/NetBeansProjects/testing/clear.txt");
obj.encrypt(file.getName());
obj.decrypt("clear.txt.aes");
}
}
You are passing just the filename as argument to encrypt method, but as per the encrypt method, its expecting the complete path.
To be precise this line of code:
FileInputStream fis = new FileInputStream(fname)
Instead of :
obj.encrypt(file.getName());
Use this :
obj.encrypt(file.getAbsolutePath());

Determining that decryption has occurred

I am developing a GUI based encryptor/decryptor based on AES-128 bit symmetric encryption.
My problem is that how to determine that decryption has not occurred and show a dialog box "Decryption Failed". The code I have written would always generate a file without a .enc extension regardless of the fact that it is still encrypted !
Hoping to get a answer as always from Stack Overflow's top notch programmers :)
Do note that the decryption process doesn't fail or throws exception ! It's just the fact that it generates a file that's still not decrypted. That we have to stop and that's what I meant !
Code here: (Sorry for bad indentation !)
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.security.MessageDigest;
public class FileEncryptor{
private String algo;
private String path;
private String password;
public FileEncryptor(String algo,String path, String password) {
this.algo = algo; //setting algo
this.path = path;//setting file path
this.password = password;
}
public void encrypt() throws Exception{
SecureRandom padding = new SecureRandom();
byte[] salt = new byte[16];
padding.nextBytes(salt);
//generating key
byte k[] = password.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-1");
k = sha.digest(k);
k = Arrays.copyOf(k, 16);
SecretKeySpec key = new SecretKeySpec(k,algo);
//creating and initialising cipher and cipher streams
Cipher encrypt = Cipher.getInstance(algo);
encrypt.init(Cipher.ENCRYPT_MODE, key);
//opening streams
FileOutputStream fos =new FileOutputStream(path+".enc");
try(FileInputStream fis =new FileInputStream(path)){
try(CipherOutputStream cout=new CipherOutputStream(fos, encrypt)){
copy(fis,cout);
}
}
}
public void decrypt() throws Exception{
SecureRandom padding = new SecureRandom();
byte[] salt = new byte[16];
padding.nextBytes(salt);
//generating same key
byte k[] = password.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-1");
k = sha.digest(k);
k = Arrays.copyOf(k, 16);
SecretKeySpec key = new SecretKeySpec(k,algo);
//creating and initialising cipher and cipher streams
Cipher decrypt = Cipher.getInstance(algo);
decrypt.init(Cipher.DECRYPT_MODE, key);
//opening streams
FileInputStream fis = new FileInputStream(path);
try(CipherInputStream cin=new CipherInputStream(fis, decrypt)){
try(FileOutputStream fos =new FileOutputStream(path.substring(0,path.lastIndexOf(".")))){
copy(cin,fos);
}
}
}
private void copy(InputStream is,OutputStream os) throws Exception{
byte buf[] = new byte[4096]; //4K buffer set
int read = 0;
while((read = is.read(buf)) != -1) //reading
os.write(buf,0,read); //writing
}
public static void main (String[] args)throws Exception {
System.out.println("Enter Password: ");
new FileEncryptor("AES","sample.txt",new java.util.Scanner(System.in).nextLine()).encrypt();
new FileEncryptor("AES","sample.txt.enc",new java.util.Scanner(System.in).nextLine()).decrypt();
}
}
Without looking at the API calls, the decrypt methods should throw an exception if an error occurs. In your exception handler, you can set a flag that will allow you to display an error message. You can also delay the decrypted file creation till after successful decryption (or at least till after the first block has been successfully decrypted). If decryption then fails further along the line, you can delete the (essentially temporary) decrypted output file and display the error message.
[edit]
I slightly misunderstood the original post, so some suggestions to check for failed decryption (note that these are higher level than AES, so it might be specific to your application only):
Add a checksum to the plaintext data before encryption
Append other metadata (file size, user, date, etc) to the plaintext, and check for these when decrypting
Usually, a padding exception would occur on decryption - check for these (and any other giveaways)
Use PKI (public key infrastructure) functionality such as signatures (this is outside the scope of this answer, and possibly outside the scope of the problem you're trying to solve)
I suggest appending a constant, rather than a checksum, to your data before encryption, and verifying it after encryption.
And the encryption algorithm should use chaining, that means avoid ECB (see here why: http://bobnalice.wordpress.com/2009/01/28/friends-don%E2%80%99t-let-friends-use-ecb-mode-encryption).
Using a constant with chaining, is nearly as good as a checksum and much simpler.

Categories