Send file encrypted (Server) and receive file decrypted (Client) with AES 256 - java

Im sending a file from Server to a Client but i need to send a encrypted file with AES 256 and receive a original for client use in diferente machine.
I want use 2 string to generate a SHA256 for example: "fruit" and "car29".
After generate this key I want to use this key as secretkey to encrypt with AES256.The client and server know the two strings.
My server code:
public final static int SOCKET_PORT = 4444;
public final static String FILE_TO_SEND = "C:\\file.txt";
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
FileInputStream fis = null;
BufferedInputStream bis = null;
OutputStream os = null;
ServerSocket servsock = null;
Socket sock = null;
try {
servsock = new ServerSocket(SOCKET_PORT);
while (true) {
System.out.println("Waiting...");
try {
sock = servsock.accept();
System.out.println("Accepted connection : " + sock);
// send file
File myFile = new File(FILE_TO_SEND);
byte[] mybytearray = new byte[(int) myFile.length()];
fis = new FileInputStream(myFile);
bis = new BufferedInputStream(fis);
bis.read(mybytearray, 0, mybytearray.length);
os = sock.getOutputStream();
System.out.println("Sending " + FILE_TO_SEND + "(" + mybytearray.length + " bytes)");
os.write(mybytearray, 0, mybytearray.length);
os.flush();
System.out.println("Done.");
} finally {
if (bis != null) {
bis.close();
}
if (os != null) {
os.close();
}
if (sock != null) {
sock.close();
}
}
}
} finally {
if (servsock != null) {
servsock.close();
}
}
}
My client code:
public final static int SOCKET_PORT = 4444;
public final static String SERVER = "127.0.0.1";
public final static String FILE_TO_RECEIVED = "C:\\file.txt";
public final static int FILE_SIZE = 6022386;
public static void main (String [] args ) throws IOException {
int bytesRead;
int current = 0;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
Socket sock = null;
try {
sock = new Socket(SERVER, SOCKET_PORT);
System.out.println("Connecting...");
// receive file
byte [] mybytearray = new byte [FILE_SIZE];
InputStream is = sock.getInputStream();
fos = new FileOutputStream(FILE_TO_RECEIVED);
bos = new BufferedOutputStream(fos);
bytesRead = is.read(mybytearray,0,mybytearray.length);
current = bytesRead;
do {
bytesRead =
is.read(mybytearray, current, (mybytearray.length-current));
if(bytesRead >= 0) current += bytesRead;
} while(bytesRead > -1);
bos.write(mybytearray, 0 , current);
bos.flush();
System.out.println("File " + FILE_TO_RECEIVED
+ " downloaded (" + current + " bytes read)");
}
finally {
if (fos != null) fos.close();
if (bos != null) bos.close();
if (sock != null) sock.close();
}
}
Thanks in advance!

There's some function I write for you, check it out.
For generating HASH/Digest:
public byte[] generateHASH(byte[] message) throws Exception {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] hash = messageDigest.digest(message);
return hash;
}
For Encryption:
public byte[] encrypt(byte[] msg, byte[] key, byte[] iv) 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);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
} else {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
}
byte[] encMessage = cipher.doFinal(msg);
return encMessage;
}
For Decryption:
public byte[] decrypt(byte[] encMsgtoDec, byte[] key, byte[] iv) 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);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
} else {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
}
byte[] decMsg = cipher.doFinal(encMsgtoDec);
return decMsg;
}
Note:
If you use CBC mode, then both encrypt() and decrypt() use same iv, otherwise you don't need iv, and of course key is same for both.
Your key generation process is naive. You better use RSA public key encryption for key exchange or Diffie–Hellman key exchange method for secret key transfer.

I would suggest you to use SSL. The data encryption comes by default. The SSL handshake takes care of generating and exchanging the encryption keys and subsequently encrypting the data from the source and decrypting it at the receiving end. All this happens at the transport layer and the application does not have to bother or do anything explicit except for configuring it to use SSL.

Related

Error trying to use AES encryption in java

I am trying to build a Java program that compresses, then encrypts files using AES encryption. However, I am getting the following error during the encryption process:
Exception in thread "main" java.security.NoSuchAlgorithmException: Cannot find any provider supporting PBKDF2WithHmacSHA256
at java.base/javax.crypto.Cipher.getInstance(Cipher.java:574)
at MyJavaProject.zip.encryptFile(zip.java:75)
at MyJavaProject.zip.main(zip.java:58)
I've included my code below. It's supposed to compress the file I point it toward, then encrypt it, and save the bytes for the salt and IV so I can decrypt later. I'm using Java 16 and my JRE is JavaSE-12.
class zip {
public static void main(String[] args) throws IOException, InvalidKeyException, NoSuchPaddingException,
NoSuchAlgorithmException, InvalidAlgorithmParameterException, BadPaddingException,
IllegalBlockSizeException, InvalidKeySpecException {
FileInputStream fis = new FileInputStream("texty.txt");
FileOutputStream fos = new FileOutputStream("texty(comp)");
DeflaterOutputStream dos = new DeflaterOutputStream(fos);
int data;
while ((data = fis.read()) != -1) {
dos.write(data);
}
fis.close();
dos.close();
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
SecretKey key = getKeyFromPassword("Password", salt.toString());
String algorithm = "AES/CBC/PKCS5Padding";
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
IvParameterSpec Iv = new IvParameterSpec(iv);
encryptFile(algorithm, key, Iv, new File("texty(comp)"), new File("texty(compNenc)"));
FileOutputStream fs = new FileOutputStream(new File("intravenus"));
BufferedOutputStream bos = new BufferedOutputStream(fs);
bos.write(iv);
bos.close();
FileOutputStream fs2 = new FileOutputStream(new File("pepper"));
BufferedOutputStream bos2 = new BufferedOutputStream(fs2);
bos2.write(salt);
bos2.close();
}
public static void encryptFile(String algorithm, SecretKey key, IvParameterSpec iv, File inputFile, File outputFile)
throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
FileInputStream inputStream = new FileInputStream(inputFile);
FileOutputStream outputStream = new FileOutputStream(outputFile);
byte[] buffer = new byte[64];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
byte[] output = cipher.update(buffer, 0, bytesRead);
if (output != null) {
outputStream.write(output);
}
}
byte[] outputBytes = cipher.doFinal();
if (outputBytes != null) {
outputStream.write(outputBytes);
}
inputStream.close();
outputStream.close();
}
public static SecretKey getKeyFromPassword(String password, String salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 65536, 256);
SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return secret;
}
}
UPDATE:
Thanks you to #that other guy for helping me out with the initial error, but now I have a new one that I am not quite advanced enough to understand. When I try and download the bytes I store for the salt and IV, and attempt to decrypt the file, I get the following error message:
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:975)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1056)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2091)
at MyJavaProject.unzip.decryptFile(unzip.java:99)
at MyJavaProject.unzip.main(unzip.java:52)
My code for the decryption file is listed below. If anyone could help I'd greatly appreciate it.
class unzip {
public static void main(String[] args)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException,
NoSuchPaddingException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
byte[] fileData = new byte[16];
DataInputStream dis = null;
dis = new DataInputStream(new FileInputStream(new File("intravenus")));
dis.readFully(fileData);
if (dis != null) {
dis.close();
}
byte[] iv = fileData;
IvParameterSpec Iv = new IvParameterSpec(iv);
byte[] fileData2 = new byte[16];
DataInputStream dis2 = null;
dis2 = new DataInputStream(new FileInputStream(new File("pepper")));
dis2.readFully(fileData2);
if (dis2 != null) {
dis2.close();
}
byte[] salt = fileData2;
SecretKey key = getKeyFromPassword("Password", salt.toString());
String algorithm = "AES/CBC/PKCS5Padding";
decryptFile(algorithm, key, Iv, new File("texty(compNenc)"), new File("texty(compNdec)"));
// assign Input File : file2 to FileInputStream for reading data
FileInputStream fis = new FileInputStream("texty(compNdec)");
// assign output file: file3 to FileOutputStream for reading the data
FileOutputStream fos = new FileOutputStream("texty(decompNdec)");
// assign inflaterInputStream to FileInputStream for uncompressing the data
InflaterInputStream iis = new InflaterInputStream(fis);
// read data from inflaterInputStream and write it into FileOutputStream
int data;
while ((data = iis.read()) != -1) {
fos.write(data);
}
// close the files
fos.close();
iis.close();
}
public static SecretKey getKeyFromPassword(String password, String salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 65536, 256);
SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return secret;
}
public static void decryptFile(String algorithm, SecretKey key, IvParameterSpec iv, File encryptedFile,
File decryptedFile) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
FileInputStream inputStream = new FileInputStream(encryptedFile);
FileOutputStream outputStream = new FileOutputStream(decryptedFile);
byte[] buffer = new byte[64];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
byte[] output = cipher.update(buffer, 0, bytesRead);
if (output != null) {
outputStream.write(output);
}
}
byte[] output = cipher.doFinal();
if (output != null) {
outputStream.write(output);
}
inputStream.close();
outputStream.close();
}
}

Why am I getting an illegalBlockSizeException when trying to decrypt this message?

I am creating a server (Responder B) and client (Initiator A) which will exchange encrypted messages that will be decrypted on the receiving end. However, when I send an encrypted message from responder B to initiator a, I am getting the "Input length must be multiple of 8 when decrypting with the padded cipher" error.
Here is the server/Responder B code:
public class ResponderB {
protected static String masterKey = "NETWORK SECURITY";
protected String identityA;
protected String ciphertext;
private DataOutputStream out;
private Socket socket;
private ServerSocket server;
private DataInputStream in;
public ResponderB(int port, String ciphertext) {
this.ciphertext = ciphertext;
try {
server = new ServerSocket(port);
System.out.println("Responder started");
System.out.println("Waiting for initiator ...");
socket = server.accept();
System.out.println("Responder accepted");
out = new DataOutputStream(socket.getOutputStream());
in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
String line = "";
//System.out.println("ResponderB ciphertext: "+this.ciphertext);
try {
line = in.readUTF();
this.identityA = line;
System.out.println("Identity Recieved");
System.out.println("Initiator Identity: "+this.identityA);
this.identityA = encrypt(this.identityA, masterKey);
this.ciphertext = this.identityA + " " + this.ciphertext;
System.out.println("Message Two Ciphertext in bytes: "+this.ciphertext.getBytes());
System.out.println("Message Two Ciphertext: "+this.ciphertext);
out.writeUTF(this.ciphertext);
}catch(IOException i){
System.out.println(i);
}
System.out.println("Closing connection");
//close connection
socket.close();
in.close();
}catch(IOException i){
System.out.println(i);
}
}
public static void main(String args[]){
String sessionKey = "SESSION";
String ciphertext1 = ""; String ciphertext2 = "";
String identityB = "RESPONDER B";
ciphertext1 = encrypt(identityB, masterKey);
ciphertext2 = encrypt(sessionKey, masterKey);
String cleartext = ciphertext1 + " " + ciphertext2;
ResponderB initiator = new ResponderB(6666, cleartext);
}}
This is the client/initiator a code:
public class InitiatorA {
private String identity = "INITIATOR A";
private String masterKey = "NETWORK SECURITY";
private String ciphertext, plaintext, temptext, sessionKey;
private Socket socket;
private BufferedReader input;
private DataOutputStream out;
private DataInputStream in;
public InitiatorA(String address, int port) {
// try to establish a connection
try {
socket = new Socket(address, port);
System.out.println("Connected");
//ready the input reader
input = new BufferedReader(new InputStreamReader(System.in));
in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
out = new DataOutputStream(socket.getOutputStream());
// String to read a message from the input
String line = "";
String buffer = "";
//line = input.readLine(); // Reads line from keyboard
System.out.println("Cleartext of message 1: " + this.identity);
out.writeUTF(this.identity); // writes it to the output stream
buffer = in.readUTF();
this.ciphertext = buffer;
System.out.println("The received ciphertext of message 2: " + this.ciphertext);
String[] tokens = this.ciphertext.split("\\s+");
for(int i = 0; i < tokens.length; i++){
this.plaintext = this.plaintext + decrypt(tokens[i], this.masterKey);
}
System.out.println("The decrypted message 2: " + this.plaintext);
} catch (IOException i) {
System.out.println(i);
}
}
public static void main(String args[]) {
InitiatorA responder = new InitiatorA("127.0.0.1", 6666);
}}
Finally, these are the encrypt and decrypt methods I am using, the algorithm to encrypt and decrypt the messages is DES, and I am using a custom key (masterKey) that is being converted to a secret Key from a string, One thing I noticed is that the byte [] of the ciphertext being transmitted is changing from the server to the client-side, but the actual text in the cipher text remains the same.
Another exception I am getting is the: "
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption." But I don't see how a bad key would be in place here since the same string is being used on the server and client-side.
public static String encrypt(String plain, String key) {
String plaintext, ciphertext = "", plaintext1;
byte[] plaintextByte, plaintext1Byte, ciphertextByte, keyByte;
plaintext = plain;
try {
keyByte = key.getBytes();
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey desKey = keyFactory.generateSecret(new DESKeySpec(keyByte));
plaintextByte = plaintext.getBytes();
Cipher desCipher;
desCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
desCipher.init(Cipher.ENCRYPT_MODE, desKey);
ciphertextByte = desCipher.doFinal(plaintextByte);
ciphertext = new String(ciphertextByte, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException i) {
System.out.println(i);
}
return ciphertext;
}
public static String decrypt(String cipher, String key) {
String plaintext = "", ciphertext, plaintext1="";
byte[] plaintextByte, plaintext1Byte, ciphertextByte, keyByte;
try {
keyByte = key.getBytes();
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey desKey = keyFactory.generateSecret(new DESKeySpec(keyByte));
Cipher desCipher;
desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
desCipher.init(Cipher.DECRYPT_MODE, desKey);
plaintext1Byte = desCipher.doFinal(cipher.getBytes());
plaintext1 = new String(plaintext1Byte, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException i) {
System.out.println(i);
}
return plaintext1;
}
Thank you in advance for the help.

AES+ Base64 Decrypt file appeared garbled

I am using socket to transfer file and AES 256 to encrypt/decrypt, I use the byte array to accept file's content and encrypt it, but only the first byte size array success to decrypt and other appeared garbled(the encrypted byte size extend to 1368 and AES block size is 128bits not sure is it effect),below is my code:
public void sendfile() {
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
byte[] byteArray = new byte[1024];
int bytesCount = 0;
while ((bytesCount = fis.read(byteArray)) >= 0) {
String encryptString = new String(byteArray, "UTF-8");
bos.write(EncryptAES.encrypt(encryptString, "12345").getBytes("UTF-8"));
}
}
public void receiveFile(File file, BufferedInputStream bis) {
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
byte[] byteArray = new byte[1024];
int bytesCount = 0;
while ((bytesCount = bis.read(byteArray)) >= 0) {
String encryptString = new String(byteArray, "UTF-8");
bos.write(EncryptAES.decrypt(encryptString, "12345").getBytes("UTF-8"));
}
}
public static String encrypt(String content,SecretKey secretKey) {
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] byteContent = cipher.doFinal(content.getBytes("UTF-8"));
return Base64.getEncoder().withoutPadding().encodeToString(byteContent);
}
public static String decrypt(String content,SecretKey secretKey) {
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding", "BC");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] byteCipherText = Base64.getDecoder().decode(content);
String encryptString = new String(byteCipherText, "UTF-8");
byte[] decryptedText = cipher.doFinal(byteCipherText);
encryptString = new String(decryptedText, "UTF-8");
return new String(decryptedText, "UTF-8");
}

Problem with decryption , cipher in android AES/CTR/NoPadding

When i use this code on android Marshmallow(Android 6.0.1) ,the decryption is Ok but when i run on device with android Oreo(Android 8) the decryption value is not the same and data is not Correct.
private void decrypt(Cipher cipher, Uri uri) throws Exception {
long a = 113845229;
InputStream inputStream = getContentResolver().openInputStream(uri);
CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
cipherInputStream.skip(a);
byte[] buffer = new byte[8];
cipherInputStream.read(buffer);
}
// create cipher
private Cipher createCipher(byte[] iv, byte[] salt, String password) throws Exception {
IvParameterSpec mIvParameterSpec = new IvParameterSpec(iv);
SecretKeySpec mSecretKeySpec = generate(password, salt);
Cipher mCipher = Cipher.getInstance("AES/CTR/NoPadding");
mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, mIvParameterSpec);
return mCipher;
}
// generate key
private SecretKeySpec generate(String password, byte[] salt) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
byte[] key = md.digest(password.getBytes(StandardCharsets.UTF_8));
return new SecretKeySpec(key, "AES");
}
buffer data is ok in android 6 but in android 8 the data is not correct.
I believe you're looking for random access to ctr encrypted data;
Skip method in CipherInputStream just doesn't do that and is 'Android version independent'(still in use; not deprecated or replaced since api level 1!);
Take a look at CipherInputStream class file; It has few internal properties:
private Cipher cipher;//the cipher you pass to constructor;
// the underlying input stream
private InputStream input;
/* the buffer holding data that have been read in from the
underlying stream, but have not been processed by the cipher
engine. the size 512 bytes is somewhat randomly chosen */
private byte[] ibuffer = new byte[512];//holds encrypted data
// having reached the end of the underlying input stream
private boolean done = false;
/* the buffer holding data that have been processed by the cipher
engine, but have not been read out */
private byte[] obuffer;//a portion of data that's decrypted but not yet read;
// the offset pointing to the next "new" byte
private int ostart = 0;
// the offset pointing to the last "new" byte
private int ofinish = 0;
and this is what skip does in CipherInputStream;
public long skip(long n) throws IOException {
int available = ofinish - ostart;
if (n > available) {
n = available;
}
if (n < 0) {
return 0;
}
ostart += n;
return n;
}
It doesn't load new data to obuffer or ibuffer; it only skips on what is available in the obuffer( just increases ostart);
This should do it(has room for improvement):
private static IvParameterSpec calculateIVForOffset(final IvParameterSpec iv,
final long blockOffset) {
final BigInteger ivBI = new BigInteger(1, iv.getIV());
final BigInteger ivForOffsetBI = ivBI.add(BigInteger.valueOf(blockOffset
/ AES_BLOCK_SIZE));
final byte[] ivForOffsetBA = ivForOffsetBI.toByteArray();
final IvParameterSpec ivForOffset;
if (ivForOffsetBA.length >= AES_BLOCK_SIZE) {
ivForOffset = new IvParameterSpec(ivForOffsetBA, ivForOffsetBA.length - AES_BLOCK_SIZE,
AES_BLOCK_SIZE);
} else {
final byte[] ivForOffsetBASized = new byte[AES_BLOCK_SIZE];
System.arraycopy(ivForOffsetBA, 0, ivForOffsetBASized, AES_BLOCK_SIZE
- ivForOffsetBA.length, ivForOffsetBA.length);
ivForOffset = new IvParameterSpec(ivForOffsetBASized);
}
return ivForOffset;
}
long offset = 113845229;// aka a
private void decrypt(Cipher cipher, Uri uri) throws Exception {
long skip_this_much=offset-(offset%16);
InputStream inputStream = getContentResolver().openInputStream(uri);
do{
skip_this_much=skip_this_much-inputStream.skip(skip_this_much);//InputStream.skip does not necessarily skip as much as specified in parameter and returns the actually skipped value;
}while(skip_this_much!=0);//not there yet; keep skipping;
CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
int read_this_much=8;
byte[] buffer=new byte[read_this_much+(offset%16)];
cipherInputStream.read(buffer,0,read_this_much+(offset%16));//improve this yourself
buffer= Arrays.copyOfRange(buffer,offset%16,read_this_much+(offset%16));
}
// create cipher for offset
private Cipher createCipher(byte[] iv, byte[] salt, String password) throws Exception {
IvParameterSpec mIvParameterSpec = new IvParameterSpec(iv);
SecretKeySpec mSecretKeySpec = generate(password, salt);
Cipher mCipher = Cipher.getInstance("AES/CTR/NoPadding");
mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, calculateIVForOffset(mIvParameterSpec,offset));
return mCipher;
}
// generate key
private SecretKeySpec generate(String password, byte[] salt) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
byte[] key = md.digest(password.getBytes(StandardCharsets.UTF_8));
return new SecretKeySpec(key, "AES");
}
I came to the conclusion after research .you should implement InputStream with specific Cipher.
private static final int AES_BLOCK_SIZE = 16;
private InputStream mUpstream;
private Cipher mCipher;
private SecretKeySpec mSecretKeySpec;
private IvParameterSpec mIvParameterSpec;
public StreamingCipherInputStream(InputStream inputStream, Cipher cipher,
SecretKeySpec secretKeySpec, IvParameterSpec ivParameterSpec) {
super(inputStream, cipher);
mUpstream = inputStream;
mCipher = cipher;
mSecretKeySpec = secretKeySpec;
mIvParameterSpec = ivParameterSpec; }
#Override
public int read(byte[] b, int off, int len) throws IOException {
return super.read(b, off, len); }
public long forceSkip(long bytesToSkip) throws IOException {
long skipped = mUpstream.skip(bytesToSkip);
try {
int skip = (int) (bytesToSkip % AES_BLOCK_SIZE);
long blockOffset = bytesToSkip - skip;
long numberOfBlocks = blockOffset / AES_BLOCK_SIZE;
BigInteger ivForOffsetAsBigInteger = new BigInteger(1,
mIvParameterSpec.getIV()).add(BigInteger.valueOf(numberOfBlocks));
byte[] ivForOffsetByteArray = ivForOffsetAsBigInteger.toByteArray();
IvParameterSpec computedIvParameterSpecForOffset;
if (ivForOffsetByteArray.length < AES_BLOCK_SIZE) {
byte[] resizedIvForOffsetByteArray = new byte[AES_BLOCK_SIZE];
System.arraycopy(ivForOffsetByteArray, 0, resizedIvForOffsetByteArray,
AES_BLOCK_SIZE - ivForOffsetByteArray.length, ivForOffsetByteArray.length);
computedIvParameterSpecForOffset = new IvParameterSpec(resizedIvForOffsetByteArray);
} else {
computedIvParameterSpecForOffset = new IvParameterSpec(ivForOffsetByteArray, ivForOffsetByteArray.length - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
}
mCipher.init(Cipher.ENCRYPT_MODE, mSecretKeySpec, computedIvParameterSpecForOffset);
byte[] skipBuffer = new byte[skip];
mCipher.update(skipBuffer, 0, skip, skipBuffer);
Arrays.fill(skipBuffer, (byte) 0);
} catch (Exception e) {
return 0;
}
return skipped;
}
#Override
public int available() throws IOException {
return mUpstream.available();}

javax.crypto.BadPaddingException: Given final block not properly padded while decryption

I am trying to decrypt a file in java. The first 16 bytes of decrypted file are IV (initialization vector). Please help in resolving the above exception.
I am trying to prepend the IV in the output file in AESFileEncryption() and then reading it while decryption.
Thank You.
public class AESFileEncryption {
public static void encrypt(String path,String pwd) throws Exception {
FileOutputStream outFile;
try (
FileInputStream inFile = new FileInputStream(path)) {
String fileName=path;
System.out.println(path);
outFile = new FileOutputStream(fileName+".aes");
// password to encrypt the file
String password = pwd;
byte[] salt = {
(byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
(byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
};
SecretKeyFactory factory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(password.toCharArray(),salt,65536,128);// user-chosen password that can be used with password-based encryption (PBE).
SecretKey secretKey = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(secretKey.getEncoded(), "AES");//Secret KeySpec is a class and implements inteface SecretKey
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[16];
random.nextBytes(bytes);
IvParameterSpec ivspec = new IvParameterSpec(bytes);
cipher.init(Cipher.ENCRYPT_MODE, secret,ivspec);//opmode,key
outFile.write(bytes);
byte[] input = new byte[64];
int bytesRead;
while ((bytesRead = inFile.read(input)) != -1) {
byte[] output = cipher.update(input, 0, bytesRead);
if (output != null)
Files.write(Paths.get(fileName+".aes"), output, StandardOpenOption.APPEND);
} byte[] output = cipher.doFinal();
if (output != null)
Files.write(Paths.get(fileName+".aes"), output, StandardOpenOption.APPEND);
}
outFile.flush();
outFile.close();
File f=new File(path);
boolean x=f.delete();
if(x){
System.out.println("File deleted");
}
JOptionPane.showMessageDialog(null,"File Encrypted.");
}
}
Decryption code
public class AESFileDecryption {
public static void decrypt(String path,String pwd) throws Exception {
String password = pwd;
String fileName=path;
File file=new File(path);
//System.out.println(inFile.toString());
String fileNameWithOutExt = path.replaceFirst("[.][^.]+$", "");
System.out.println(fileName);
System.out.println(fileNameWithOutExt);
byte[] salt = {
(byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
(byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
};
System.out.println("1");
FileInputStream fis = new FileInputStream(path);
SecretKeyFactory factory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(password.toCharArray(),salt,65536,128);
SecretKey tmp = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
System.out.println("2");
// file decryption
Cipher cipher=null;
byte bytes[]=new byte[16];
fis.read(bytes, 0, 16);
IvParameterSpec ivspec = new IvParameterSpec(bytes);
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, ivspec);
System.out.println("3");
FileOutputStream fos = new FileOutputStream(fileNameWithOutExt);
System.out.println("4");
byte[] in = new byte[64];
int read;
while ((read = fis.read(in,16,(int)file.length()-16)) != -1) {
byte[] output = cipher.update(in, 0, read);
if (output != null)
fos.write(output);
}
try{
byte[] output = cipher.doFinal();
if (output != null)
fos.write(output);
fis.close();
fos.flush();
fos.close();
System.out.println("File Decrypted.");
}
catch(IOException | BadPaddingException | IllegalBlockSizeException e)
{
System.out.println(e+"");
}
}
}
There are a few problems with the small example, but the one that is most immediately your problem is the line
while ((read = fis.read(in,16,(int)file.length()-16)) != -1) {
You seem to be confused about the meaning of the offset parameter to the read(). It is not the offset into the file, but rather the offset into the array (in) that is specified in the first parameter.
A non-exhaustive list of other problems that I see include:
writing to the file using the two independent mechanisms (FileOutputStream.write() and Files.write()). This actually worked ok when I ran your program but it seems like it's asking for trouble. There's no reason to use Files.write() here.
fis.read(bytes, 0, 16); does not check the return value.
It seems you are struggling with finding some IO idioms that you're comfortable with. Or perhaps just experimenting. At the risk of giving you even more options to juggle, you might consider investigating google's open source Guava library. Many people find it has just what they needed.

Categories