I have the following code. However the files b.xlsx and c.xlsx are of 0 bytes. Why is CipherOuputSteam not working?
public static void main(String[] args) throws Exception {
KeyPair keys = KeyPairGenerator.getInstance("RSA").generateKeyPair();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, keys.getPublic());
FileInputStream fis;
FileOutputStream fos;
CipherOutputStream cos;
fis = new FileInputStream("C:/temp/a.xlsx");
fos = new FileOutputStream("C:/temp/b.xlsx");
cos = new CipherOutputStream (fos, cipher);
byte[] block = new byte[8];
int i;
while ((i = fis.read(block)) != -1) {
cos.write(block, 0, i);
}
cos.close();
fos.close();
cipher.init(Cipher.DECRYPT_MODE, keys.getPrivate());
CipherInputStream cis1, cis2;
fis = new FileInputStream("c:/temp/b.xlsx");
CipherInputStream cis = new CipherInputStream(fis, cipher);
fos = new FileOutputStream("c:/temp/c.xlsx");
while ((i = cis.read(block)) != -1) {
fos.write(block, 0, i);
}
fos.close();
fis.close();
cis.close();
}
The problem lies in your usage - which is incorrect and in the implementation of CipherOutputStream which masks a very important exception - IllegalBlockSizeException.
The problem is that you cannot use an RSA key to encrypt data which is longer than the size of the key (which is 128 bytes in your example).
you should use a symmetric encryption algorithm for large blocks of data - e.g. AES.
If you want to use asymmetric keys for a reason (safe transmition of data for example) - you can find a good example on this SO answer.
Related
I have the following function to encrypt files. I printed the result of both files and everything seems to work properly.
The encrypted file is changed and is the same length then the input file.
public void encrypt(String password, String filePath){
InputStream is = null;
OutputStream os = null;
CipherOutputStream cos = null;
try {
is = new FileInputStream(new File(filePath));
os = new FileOutputStream(new File(filePath + ".enc"));
char[] passwordChars = password.toCharArray();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passwordChars, salt, 1024, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, key, iv);
cos = new CipherOutputStream(os, c);
byte[] b = new byte[1024];
while(is.read(b) != -1)
cos.write(b);
b = new byte[1024];
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (os != null)
os.close();
if (is != null)
is.close();
if (cos != null)
cos.close();
} catch (IOException e) {
}
}
}
The problem happens when I try to decrypt the file afterwards. I use CipherInputStream with the following function:
public void decrypt(String password, String filePath){
InputStream is = null;
OutputStream os = null;
CipherInputStream cis = null;
try {
is = new FileInputStream(new File(filePath));
os = new FileOutputStream(new File(filePath + ".dec"));
char[] passwordChars = password.toCharArray();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passwordChars, salt, 1024, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, key, iv);
cis = new CipherInputStream(is, c);
byte[] b = new byte[1024];
while(cis.read(b) != -1){
os.write(b);
b = new byte[1024];
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (os != null)
os.close();
if (is != null)
is.close();
if (cis != null)
cis.close();
} catch (IOException e) {
}
}
}
The while loop is not entered at all. It acts like the file is empty while I verified and it is clearly not. Encryption seems to work properly but not decryption.
I tried changing the function to use CipherOutputStream but then the same thing happens. I get a decrypted file of length 0.
Why is the decrypt method not working?
There are many things to be fixed
c.init(Cipher.ENCRYPT_MODE, key, iv);
Where is IV defined? If it's static, using cbc mode you break the security almost completely.
Next - you are using CBC mode without any authentication (hmac), try to search for "malleable cipher"
Why is the decrypt method not working?
byte[] b = new byte[1024];
while(is.read(b) != -1)
cos.write(b);
b = new byte[1024];
}
This is completely wrong. You are writing a full array b of 1024 bytes regardless of read input. (is there a reason to recreate the array in the loop?)
try something like
b=byte[1024];
for(int bytesRead=in.read(b); bytesRead>-1; bytesRead=in.read(b))
cos.write(b, 0, bytesRead);
cos.flush()
the same is valid for decryption, please fix this first and then try to debug your app
Note: I'm not really fond of the CipherStream implementation, I'd suggest you to use cipher.update and cipher.doFinal
I'm new to encryption/compression in Java and I'm working on a test project where the goal is to compress and then encrypt files via a buffered input in Java. At no point should the file be stored on disk in a non-encrypted format, therefore I want to do the compression and encryption solely on a buffer until the file is fully written.
So the progression would be: read part of file into memory (buffer, 1024 bytes) -> compress (~32 bytes)-> encrypt -> output to disk -> repeat until entire file is written
The issue I'm facing is that once I perform the reverse operations to read the compressed/encrypted file back, only part of the data is there.
To accomplish my goal, I've been combining the Inflater/Deflater classes and a block cipher with AES 256 encryption.
Encryption setup:
byte[] randomSalt = new byte[8];
SecureRandom secRand = new SecureRandom();
secRand.nextBytes(randomSalt);
String randomPassword = new BigInteger(130, secRand).toString(32);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(randomPassword.toCharArray(), randomSalt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
Getting input / writing output:
BufferedInputStream bufferedInput = new BufferedInputStream(
new FileInputStream("file.txt"));
BufferedOutputStream bufferedOutput = new BufferedOutputStream(
new FileOutputStream("encrypted file"));
byte[] buffer = new byte[1024];
try {
while (bufferedInput.read(buffer) != -1) {
byte[] encryptedBuffer = cipher.doFinal(compress(buffer));
bufferedOutput.write(encryptedBuffer);
bufferedOutput.flush();
}
} catch (Exception e) {
//snip
} finally {
bufferedInput.close();
bufferedOutput.close();
}
Compress method:
public static byte[] compress(byte[] data) throws IOException{
Deflater deflater = new Deflater();
deflater.setInput(data);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
deflater.finish();
byte[] buffer = new byte[1024];
while(!deflater.finished()){
int count = deflater.deflate(buffer);
outputStream.write(buffer, 0, count);
}
outputStream.close();
return outputStream.toByteArray();
}
What can I do to be able to compress and encrypt a file 1KB at a time and get the file back in its entirety when I perform the reverse operations?
I'm getting Given final block not properly padded error while decrypting AES/CBC/PKCS5Padding cipher on large encrypted file.
I think this issue is caused by adding wrong initialization vector in cipher.init() method.
I can't read whole file at runtime, so i need to encrypt fixed-size blocks. At this point I'm creating IV and storing it to .txt file. But in decrypting method I'm using the same IV every decryption cycle. How should I change this?
Encryption:
void encrypt() throws Exception{
char[] password = passwordText.getText().toCharArray();
byte[] salt = new byte[8];
/* Creating and saving salt */
salt = saveSalt(salt);
/* Securing password */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
if (choosedFile != null) {
/* Choosing algorithm for decryption */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
/* Getting plain file */
CipherInputStream fis = new CipherInputStream(new FileInputStream(choosedFile), cipher);
CipherOutputStream fos = new CipherOutputStream(new FileOutputStream(choosedFile+".encrypted"), cipher);
/* Encrypting and Measuring */
long startTime = System.currentTimeMillis();
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] rawText = new byte[128];
int count;
while((count = fis.read(rawText)) > 0) {
System.out.println(count);
byte[] encryptedText = cipher.doFinal(rawText);
fos.write(encryptedText, 0, count);
}
long stopTime = System.currentTimeMillis();
fis.close();
fos.close();
/* Creating initialization vector and storing*/
byte[] iVector = cipher.getIV();
saveIVector(iVector);
text.setText(text.getText() + "File was encrypted in " + (stopTime - startTime) + "ms.\n");
}
}
Decryption:
void decrypt() throws Exception {
/* Getting salt */
byte[] salt = getSalt();
/* Getting initialization vector */
byte[] iVector = getIVector();
/* Getting user password */
char[] password = passwordText.getText().toCharArray();
/* Securing password */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
if (choosedFile != null) {
/* Choosing algorithm for decryption */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
/* Getting ciphered file */
CipherInputStream fis = new CipherInputStream(new FileInputStream(choosedFile), cipher);
CipherOutputStream fos = new CipherOutputStream(new FileOutputStream(choosedFile+".decrypted"), cipher);
/* Decrypting and Measuring */
long startTime = System.currentTimeMillis();
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iVector));
byte[] rawText = new byte[128];
int count;
while((count = fis.read(rawText)) > 0) {
byte[] encryptedText = cipher.doFinal(rawText);
fos.write(encryptedText, 0, count);
}
long stopTime = System.currentTimeMillis();
fis.close();
fos.close();
When using CipherInputStream and CipherOutputStream, the streams handle all the calls to the cipher (that's why you pass the cipher to it on initialization). You just need to initialize it correctly, and stream the data through the stream, and the cipher stream will do the needed calls to update() and doFinal(). Remember to close the steams to trigger the doFinal().
Currently your code passes the data through the cipher several times in an uncontrolled way, and the data is messed up.
Also, you only need a CipherInputStream for decrypt, and a CipherOutputStream for encrypt. In your current code you use both for both encrypt and decrypt.
Encrypt could be something like this (this don't handle the iv ..):
...
cipher.init(Cipher.ENCRYPT_MODE, secret);
InputStream is = new FileInputStream(choosedFile);
OutputStream os = new CipherOutputStream(new FileOutputStream(choosedFile+".encrypted"), cipher);
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
is.close();
os.close();
...
Worried about "until I had to cut large file into fixed-size blocks".
Using "chunk" in place of "block" above because "block"has a specific meaning in block ciphers such as AES.
What are toy doing with the chunks, concatenating them?
With CBC mode, after the first block the the previous encrypted block value is effectively used as the IV for the next block. So when splitting and then concatenating the chunks the value of the last block of the previous chunk is the IV for the next chunk.
See CBC mode.
Or are you doing something completely different?
I have a problem to read the file (for example *.zip) and encrypt it with 3DES, using secretKey which one generated from name of encrypted file.
Then I need to decrypt this file, and write it on HDD.
I tried to resolve thhis problem, but stuck when was decrypting file.
Here is code of encryptor
public class Encryptor {
private static String inputFilePath = "D:/1.txt";
public static void main(String[] args) {
FileOutputStream fos = null;
File file = new File(inputFilePath);
String keyString = "140405PX_0.$88";
String algorithm = "DESede";
try {
FileInputStream fileInputStream = new FileInputStream(file);
byte[] fileByteArray = new byte[fileInputStream.available()];
fileInputStream.read(fileByteArray);
for (byte b : fileByteArray) {
System.out.println(b);
}
SecretKey secretKey = getKey(keyString);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
ObjectOutputStream objectOutputStream = new ObjectOutputStream
(new CipherOutputStream
(new FileOutputStream
("D:/Secret.file"), cipher));
objectOutputStream.writeObject(fileByteArray);
objectOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static SecretKey getKey(String message) throws Exception {
String messageToUpperCase = message.toUpperCase();
byte[] digestOfPassword = messageToUpperCase.getBytes();
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
SecretKey key = new SecretKeySpec(keyBytes, "DESede");
return key;
}
}
And here is code of decryptor
public class Decryptor {
public static void main(String[] args) {
try {
File inputFileNAme = new File("d:/Secret.file");
FileInputStream fileInputStream = new FileInputStream(inputFileNAme);
FileOutputStream fileOutputStream = new FileOutputStream(outputFilePath);
SecretKey secretKey = getKey(keyString);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
ObjectInputStream objectInputStream = new ObjectInputStream
(new CipherInputStream(fileInputStream, cipher));
System.out.println(objectInputStream.available());
while (objectInputStream.available() != 0) {
fileOutputStream.write((Integer) objectInputStream.readObject());
System.out.println(objectInputStream.readObject());
}
fileOutputStream.flush();
fileOutputStream.close();
fileInputStream.close();
objectInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static SecretKey getKey(String message) throws Exception {
String messageToUpperCase = message.toUpperCase();
byte[] digestOfPassword = messageToUpperCase.getBytes();
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
SecretKey key = new SecretKeySpec(keyBytes, "DESede");
return key;
}
}
When i try to decrypt my file, i dont get anything in output file.
I tryed make debug, and saw, that objectInputStream.available() always contains 0.
Please tell me, how can I resolve this problem, and why it happens.
The usage
byte[] fileByteArray = new byte[fileInputStream.available()];
is specifically warned against in the Javadoc: " It is never correct to use the return value of this method to allocate a buffer intended to hold all data in this stream."
Files should be processed a record or a buffer at a time.
The line:
fileInputStream.read(fileByteArray);
isn't guaranteed to fill the buffer. You have to check the return value: for -1, meaning end of file, or > 0, meaning the number of bytes that were actually transferred. See the Javadoc.
Similarly
while (objectInputStream.available() != 0) {
is not a valid test for end of stream. You should call readObject() until it throws EOFException.
As a quickfix, this works :
try {
File inputFileNAme = new File("d:/Secret.file");
FileInputStream fileInputStream = new FileInputStream(inputFileNAme);
FileOutputStream fileOutputStream = new FileOutputStream(outputFilePath);
SecretKey secretKey = getKey(keyString);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
ObjectInputStream objectInputStream = new ObjectInputStream
(new CipherInputStream(fileInputStream, cipher));
System.out.println(objectInputStream.available());
fileOutputStream.write((byte[]) objectInputStream.readObject());
fileOutputStream.flush();
fileOutputStream.close();
fileInputStream.close();
objectInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
What I did is remove the ".available() while loop" and remove the cast to Integer that was wrong.
I agree with EJP answer, especially regarding the use of .available().
You may also use http://www.jasypt.org/ that provides a more simple API to crytpo stuff.
I encrypted a text file in AES algorithm. I am not able to decrypt it. I used the same key and the whole process is running in the same method body.
At first, the input.txt file is being encrypted into encrypted.txt file. Then the decoder, decrypt the encrypted.txt into decrypted.txt
Here is the code. Thank you for your help.
public static void main(String[] args) throws IOException,
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException {
Scanner sc = new Scanner(System.in);
String filename = sc.nextLine();
sc.close();
System.out.println("The file requested is " + filename);
File file = new File(filename);
if (file.exists())
System.out.println("File found");
File to_b_encf = new File("encrypted.txt");
if (!to_b_encf.exists())
to_b_encf.createNewFile();
System.out.println("encrypting");
Cipher encipher = Cipher.getInstance("AES");
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecretKey key = keygen.generateKey();
encipher.init(Cipher.ENCRYPT_MODE, key);
FileOutputStream output = new FileOutputStream(to_b_encf);
FileInputStream input = new FileInputStream(filename);
CipherInputStream cis = new CipherInputStream(input, encipher);
int read;
while ((read = cis.read()) != -1) {
output.write(read);
output.flush();
}
input.close();
output.close();
System.out.println("done");
System.out.println("decrypting");
Cipher decipher = Cipher.getInstance("AES");//initiate a cipher for decryption
decipher.init(Cipher.DECRYPT_MODE, key);//decrypt the file
File sourcefile = new File("encrypted.txt");
File destfile = new File("decrypted.txt");
if (!destfile.exists())
destfile.createNewFile();
FileInputStream decf = new FileInputStream(sourcefile);
CipherInputStream c_decf = new CipherInputStream(decf,decipher);
FileOutputStream destf = new FileOutputStream(destfile);
cout = new CipherOutputStream(destf,decipher);
while ((read = c_decf.read()) != -1) {
cout.write(read);
cout.flush();
}
c_decf.close();
destf.close();
cout.close();
decf.close();
System.out.println("done");
}
You messed with InputStream, OutputStream and whatnot. I made a simplfied version of your code (no files, all in-memory I/O) that illustrates the main concepts:
public class EncDec {
public static void main(String[] args) throws IOException
, InvalidKeyException, NoSuchAlgorithmException
, NoSuchPaddingException {
final String MESSAGE = "I'm a secret message";
final Charset CHARSET = Charset.defaultCharset();
Cipher cipher = Cipher.getInstance("AES");
SecretKey key = KeyGenerator.getInstance("AES").generateKey();
cipher.init(Cipher.ENCRYPT_MODE, key);
// Encrypt the message
InputStream plainIn = new ByteArrayInputStream(
MESSAGE.getBytes(CHARSET));
ByteArrayOutputStream encryptedOut = new ByteArrayOutputStream();
copy(plainIn, new CipherOutputStream(encryptedOut, cipher));
// Decrypt the message
cipher.init(Cipher.DECRYPT_MODE, key);
InputStream encryptedIn = new CipherInputStream(
new ByteArrayInputStream(encryptedOut.toByteArray()), cipher);
ByteArrayOutputStream plainOut = new ByteArrayOutputStream();
copy(encryptedIn, plainOut);
System.out.println(new String(plainOut.toByteArray(), CHARSET));
}
private static void copy(InputStream in, OutputStream out)
throws IOException {
byte[] buffer = new byte[4096];
while ( in.read(buffer) > -1) {
out.write(buffer);
}
out.flush();
}
}
The Java I/O API is inspired by the decorator pattern. Encryption/decription libraries provide a decorator CipherInputStream for reading encrypted content and a decorator CipherOutputStream to encrypt a plain source and write it to the decorated output destination.
CipherInputStream c_decf = new CipherInputStream(decf,decipher);
FileOutputStream destf = new FileOutputStream(destfile);
cout = new CipherOutputStream(destf,decipher);
while ((read = c_decf.read()) != -1) {
cout.write(read);
cout.flush();
}
It looks like you're deciphering it twice.