Java large file AES Encryption is very slow - java

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;
}
}
```

Related

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 - decrypting messages using a private key [duplicate]

I'm writing a program which takes as input from the console - the name of a zip file, name of a zip file to be made containing the (de/en)crypted files generated from the first zip and a file containing the public key. I get the exception when decrypting:
exception Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.Main.decrypt(Main.java:67)
at com.Main.main(Main.java:201)
Can't figure out why I get this exception?
Public key:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCE3pA746UfpC8sFk8ZJp0yupyJqj5jy6cjdxUYoP7mCm7c0mqQDeCcDNBYW2eSozCioPrH/9L+CDQEPLYakoem+jFnUKDH5+pru/0PJTJJF8Xh/ZT9eJlvsYBr1/qSfICf6RTs7kzwq9IuSZBw7/tfNEF9i0A8FVox6HOopXod1QIDAQAB
Private key:
MIICXQIBAAKBgQCE3pA746UfpC8sFk8ZJp0yupyJqj5jy6cjdxUYoP7mCm7c0mqQDeCcDNBYW2eSozCioPrH/9L+CDQEPLYakoem+jFnUKDH5+pru/0PJTJJF8Xh/ZT9eJlvsYBr1/qSfICf6RTs7kzwq9IuSZBw7/tfNEF9i0A8FVox6HOopXod1QIDAQABAoGANOFrYBqK5lvu1koOswDWQZFZqcSSzh8IZyoGwGWa7S0r0EECXlDXmuPSq8e9IfRG8ALHrH+ZlrbnFOSgyVSWHfpj3aH+qknoSX5TW2rMQHih8865xuqheMQ+RTZ7+BRDqNsYkzxB/Z8mqzpoJQSYf+H7nWxdDCgAJVYZzxl3DmUCQQD32iEjnwiwUjii8slcmvCEZl+z84DWNdvJOg6Z38sI4AvrfpKc1WAcDg1rNZCKrRgokh54wpLt08cpFcrD04c3AkEAiTzDmc0bdgfg5wj6xHFZpYlBwiGm/bjOR2PS57P0GNU5PsDllRbFqIuzArITutO5lvZZImzuYz7Lf+cQ73pxUwJBAOdEwmdaneDo17A0m2+to3/nhqWDMVSwLMU3RyiNigZeCMFU+bkd4PBMrHi9IoJDwacZsRU9eZwxYEUV8H2Jg0ECQEEkOqRSm2pXKwX/WSjNtQPCNxhy6NUeV6vDUmTxIjh3XYjP/ynZeVEbnoj1BjB0N2/U11Jj6nPpZqb7gyppMEkCQQCoGdVYDipU+hMMnvxa0zOIyQc/a+HE0lESqn+2ZPafYi9Z1RldRMvUXhP8U7s+OuhRwprdw2ivvOFrnWyz9lL2
The code for the program is bellow . Any help is wellcomed :)
package com;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Enumeration;
import java.util.Scanner;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import javax.crypto.Cipher;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
public class Main {
public final static int BUFFER_SIZE = 117;
public static void decrypt(String originalZipFileName, String newZipFileName, String privateKeyFileName) throws Exception {
byte[] buffer = new byte[128];
ZipFile originalZipFile = new ZipFile(originalZipFileName);
ZipOutputStream newZipFile = new ZipOutputStream(new FileOutputStream(newZipFileName));
Enumeration<? extends ZipEntry> zipEntries = originalZipFile.entries();
String privateKey = getKeyString(privateKeyFileName);
PrivateKey key = makePrivateKey(privateKey);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
while(zipEntries.hasMoreElements()){
ZipEntry entry = zipEntries.nextElement();
ZipEntry copy = new ZipEntry(entry.getName());
newZipFile.putNextEntry(copy);
InputStream inputEntry = originalZipFile.getInputStream(entry);
while(inputEntry.read(buffer) != -1){
newZipFile.write(cipher.doFinal(buffer));
}
newZipFile.closeEntry();
inputEntry.close();
}
newZipFile.close();
originalZipFile.close();
}
public static void encrypt(String originalZipFileName, String newZipFileName, String publicKeyFileName) throws Exception{
byte[] buffer = new byte[BUFFER_SIZE];
ZipFile originalZipFile = new ZipFile(originalZipFileName);
ZipOutputStream newZipFile = new ZipOutputStream(new FileOutputStream(newZipFileName));
Enumeration<? extends ZipEntry> zipEntries = originalZipFile.entries();
String publicKey = getKeyString(publicKeyFileName);
PublicKey key = makePublicKey(publicKey);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
while(zipEntries.hasMoreElements()){
ZipEntry entry = zipEntries.nextElement();
ZipEntry copy = new ZipEntry(entry.getName());
newZipFile.putNextEntry(copy);
InputStream inputEntry = originalZipFile.getInputStream(entry);
while(inputEntry.read(buffer) != -1){
newZipFile.write(cipher.doFinal(buffer));
}
newZipFile.closeEntry();
inputEntry.close();
}
newZipFile.close();
originalZipFile.close();
}
public static String getKeyString(String fileName){
String key = new String();
try {
BufferedReader buf = new BufferedReader(new FileReader(fileName));
key = buf.readLine();
} catch ( IOException e) {
e.printStackTrace();
}
return key.trim();
}
public static PublicKey makePublicKey(String stored) throws GeneralSecurityException {
byte[] data = Base64.getDecoder().decode(stored);
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
return fact.generatePublic(spec);
}
public static PrivateKey makePrivateKey(String stored) throws GeneralSecurityException, Exception {
/*byte[] data = Base64.getDecoder().decode(stored);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
return fact.generatePrivate(spec);*/
byte[] data = Base64.getDecoder().decode(stored);
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(0));
ASN1EncodableVector v2 = new ASN1EncodableVector();
v2.add(new ASN1ObjectIdentifier(PKCSObjectIdentifiers.rsaEncryption.getId()));
v2.add(DERNull.INSTANCE);
v.add(new DERSequence(v2));
v.add(new DEROctetString(data));
ASN1Sequence seq = new DERSequence(v);
byte[] privKey = seq.getEncoded("DER");
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privKey);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey key = fact.generatePrivate(spec);
return key;
}
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in);
System.out.println("Enter type of operation:");
String line = scan.nextLine();
if(line.equals("encrypt")){
System.out.println("Enter name of original ZIP file:");
String originalZipFileName = scan.nextLine();
System.out.println("Enter name of new ZIP file:");
String newZipFileName = scan.nextLine();
System.out.println("Enter name of file containg public key:");
String publicKeyFileName = scan.nextLine();
encrypt(originalZipFileName, newZipFileName, publicKeyFileName);
}
if(line.equals("decrypt")){
System.out.println("Enter name of original ZIP file:");
String originalZipFileName = scan.nextLine();
System.out.println("Enter name of new ZIP file:");
String newZipFileName = scan.nextLine();
System.out.println("Enter name of file containg private key:");
String privateKeyFileName = scan.nextLine();
decrypt(originalZipFileName, newZipFileName, privateKeyFileName);
}
}
}
PS: Updated decrypt method. Still gives same error.
public static void decrypt(String originalZipFileName, String newZipFileName, String privateKeyFileName) throws Exception {
byte[] buffer = new byte[128];
ZipFile originalZipFile = new ZipFile(originalZipFileName);
ZipOutputStream newZipFile = new ZipOutputStream(new FileOutputStream(newZipFileName));
Enumeration<? extends ZipEntry> zipEntries = originalZipFile.entries();
String privateKey = getKeyString(privateKeyFileName);
PrivateKey key = makePrivateKey(privateKey);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
while(zipEntries.hasMoreElements()){
ZipEntry entry = zipEntries.nextElement();
ZipEntry copy = new ZipEntry(entry.getName());
newZipFile.putNextEntry(copy);
InputStream inputEntry = originalZipFile.getInputStream(entry);
while(inputEntry.read(buffer) != -1){
newZipFile.write(cipher.doFinal(buffer));
}
newZipFile.closeEntry();
inputEntry.close();
}
newZipFile.close();
originalZipFile.close();
}
Jozef is right.
When you create cipher with default parameters, it defaults to "RSA/ECB/PKCS1Padding". You should specify padding explicitly, if you don't like nasty surprises. Because other security providers might have different default parameters. And you never know in advance which security settings each specific JRE has.
So PKCS1 padding adds 11 bytes to your original data increasing it from 117 bytes to 128 bytes. You should take into account that these numbers are specific to 1024 bit RSA keys (which are marginally secure) and will be different for longer keys. Since you are loading the key from a file consider checking its length.
#Test
public void testPadding() throws Exception {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024, random);
KeyPair keyPair = keyGen.generateKeyPair();
/* constant 117 is a public key size - 11 */
byte[] plaintext = new byte[117];
random.nextBytes(plaintext);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] ciphertext = cipher.doFinal(plaintext);
System.out.println(plaintext.length + " becomes " + ciphertext.length);
}
This prints
117 becomes 128
And finally, consider using AES instead of RSA for file encryption.
So to fix the problem you need to use buffer of size public key length - 11 (117) for encryption and public key size (128) for decryption.
Change
outputFile.write(cipher.doFinal(buffer), 0, read);
to
outputFile.write(cipher.doFinal(buffer));
because buffer read is 117 bytes and size of doFinal result is 128 bytes.
Also you need to buffer input streams. When you are reading from file, it can be slow sometimes and then InputStream will read less data than buffer may contain. By using BufferedInputStream one ensures that there is enough data before read call returns. However, for decryption it's crucial to have the full block of data
InputStream inputEntry = new BufferedInputStream(originalZipFile.getInputStream(entry));
while((read = inputEntry.read(buffer)) != -1){
outputFile.write(cipher.doFinal(buffer), 0, read);
}
You have a problem here. read is the size of the plaintext that was read, not the ciphertext. You should remove the 2nd and 3rd parameters altogether.
It is also a waste of time and space to write the ciphertext to an intermediate file. Just write it straight to the zip stream.
The decrypt method's byte array should be 256 bytes in length as it is the default output size of the algorithm (The extra bytes result in this length). Change byte[] buffer = new byte[128]; to byte[] buffer = new byte[256];.

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());

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

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...

Categories