I am trying to encrypt and decrypt data using CBC mode.
Here, I am first reading the data from a plaintext.txt file and writing the output which is the data of the plaintext.txt to another ciphertext.txt file. While trying to decrypt, I am reading the encrypted data from the file into bytes but for some reason, it's giving me an error. Can someone help me solving this?
Here is my encryption function
// The encryption Function CBC MODE
public static byte[] encrypt(String plainText, String key) throws Exception
{
// Loading the secret key from the Key.txt file
String fileName="../data/key.txt";
try
{
BufferedReader in = new BufferedReader(new FileReader(fileName)); // Initialize a Buffer reader
key = in.readLine(); // Reading the data from txt file
}
//error message if the file is not found
catch(FileNotFoundException ex)
{
System.out.println("Unable to open file '" + fileName + "'");
}
catch(IOException ex)
{
System.out.println("Error reading file '" + fileName + "'");
}
byte[] clean = plainText.getBytes(); //Getting the data in bytes from the plain text
// Generating IV.
//From the initialization vector, we create an IvParameterSpec which is required when creating the Cipher.
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
//System.out.println("The Iv is = "+ivParameterSpec);
// Hashing key.
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(key.getBytes("UTF-8"));
byte[] keyBytes = new byte[32];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Encrypt.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(clean);
// Combine IV and encrypted part.
byte[] encryptedIVAndText = new byte[ivSize + encrypted.length];
System.arraycopy(iv, 0, encryptedIVAndText, 0, ivSize);
System.arraycopy(encrypted, 0, encryptedIVAndText, ivSize, encrypted.length);
try
{
PrintWriter writer = new PrintWriter("../data/ciphertext.txt");
writer.println(encryptedIVAndText);
writer.close();
}
catch(Exception ex)
{
System.out.println(ex);
System.out.println("File is not there");
}
return encryptedIVAndText;
}
And here is my decryption function.
// Decrypting fucntion CBC mode
public static String decrypt(byte[] encryptedIvTextBytes, String key) throws Exception
{
FileInputStream encryptedTextFis = new FileInputStream("../data/ciphertext.txt");
byte[] encText = new byte[encryptedTextFis.available()];
int lol = encryptedTextFis.available();
System.out.println("lol "+ lol);
encryptedTextFis.read(encText);
encryptedTextFis.close();
// encrypted = readFile("../data/ciphertext.txt", StandardCharsets.UTF_8).getBytes();
encryptedIvTextBytes = encText;
//System.out.println("..........??????");
String fileName="../data/key.txt";
String fileName2="../data/ciphertext.txt";
try
{
BufferedReader in = new BufferedReader(new FileReader(fileName));
BufferedReader in2 = new BufferedReader(new FileReader(fileName2));
key = in.readLine();
// byte[] array = Files.readAllBytes(new File("/path/to/file").toPath());
}
//error message if the file is not found
catch(FileNotFoundException ex)
{
System.out.println("Unable to open file '" + fileName + "'");
}
catch(IOException ex)
{
System.out.println("Error reading file '" + fileName + "'");
}
int ivSize = 16;
int keySize = 32;
//System.out.println("..........??????");
// Extract IV.
byte[] iv = new byte[ivSize];
System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.length);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
System.out.println("..........??????");
// Extract encrypted part.
int encryptedSize = encryptedIvTextBytes.length - ivSize;
byte[] encryptedBytes = new byte[encryptedSize];
System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, 0, encryptedSize);
System.out.println("..........??????");
// Hash key.
byte[] keyBytes = new byte[keySize];
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(key.getBytes());
System.arraycopy(md.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Decrypt.
Cipher cipherDecrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); //Decrypting the encrypted data to a cipherblock data
byte[] decrypted = cipherDecrypt.doFinal(encryptedBytes); // Decrypting the cipher dadat to Plaintext
//Writing the output to a txt file
try
{
PrintWriter writer = new PrintWriter("../data/result.txt");
writer.println(new String(decrypted));
writer.close();
}
catch(Exception ex)
{
System.out.println(ex);
System.out.println("File is not there");
}
return new String(decrypted); //Returning the decrypted data to main function
}
The output of the encrypted text file is of the form [B#3159c4b8.
I am getting the below error.
It seems exception is here:
System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.length);
The length of array encryptedIvTextBytes is 11 (as you say and the result is shown also). Now you want to copy 16 (as the length of iv) bytes from encryptedIvTextBytes.
But the main reason is from this note that your saving method to save encrypted data is not valid. Encrypted data may contain some bytes which are not a readable character. When you save encrypted data as a string such bytes are dropped. To solve this problem see #Maarten and #MS90 comments.
Here is a code that suits your needs:
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws Exception {
String key = "abcdefghijklmop";// you could store it somewhere ...
justToMakePlainTextFile();
byte[] encrypted = encrypt("plaintext",key);
decrypt(encrypted, key);
}
public static void justToMakePlainTextFile() {
try {
RandomAccessFile randomAccessFile = new RandomAccessFile("plaintext", "rw");
FileChannel fileChannel = randomAccessFile.getChannel();
String randomText = "Ho-ho-ho-ho! This is going to be a nice weekend and I am going to swim soon.";
byte[] bytes = randomText.getBytes();
ByteBuffer byteBuffer = ByteBuffer.allocate(randomText.length());//alocate new ByteBuffer of size randomeText.length
byteBuffer.put(bytes);
byteBuffer.flip();
fileChannel.write(byteBuffer);
randomAccessFile.close();
fileChannel.close();
} catch (IOException ioE) {
System.err.println("JVM reported an error! Take a look: " + ioE);
}
}
public static byte[] encrypt(String file, String key) throws Exception {
RandomAccessFile reader = new RandomAccessFile(file, "r");
FileChannel channel = reader.getChannel();
int bufferSize = 1024;
if (bufferSize > channel.size()) {
bufferSize = (int) channel.size();
}
ByteBuffer buff = ByteBuffer.allocate(bufferSize);
channel.read(buff);
buff.flip();
byte[] clean = buff.array();
channel.close();
reader.close();
// Generating IV.
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Hashing key.
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(key.getBytes("UTF-8"));
byte[] keyBytes = new byte[16];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Encrypt.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(clean);
// Combine IV and encrypted part.
byte[] encryptedIVAndText = new byte[ivSize + encrypted.length];
System.arraycopy(iv, 0, encryptedIVAndText, 0, ivSize);
System.arraycopy(encrypted, 0, encryptedIVAndText, ivSize, encrypted.length);
return encryptedIVAndText;
}
public static void decrypt(byte[] encryptedIvTextBytes, String key) throws Exception {
int ivSize = 16;
int keySize = 16;
// Extract IV.
byte[] iv = new byte[ivSize];
System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.length);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Extract encrypted part.
int encryptedSize = encryptedIvTextBytes.length - ivSize;
byte[] encryptedBytes = new byte[encryptedSize];
System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, 0, encryptedSize);
// Hash key.
byte[] keyBytes = new byte[keySize];
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(key.getBytes());
System.arraycopy(md.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Decrypt.
Cipher cipherDecrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decrypted = cipherDecrypt.doFinal(encryptedBytes);
try {
RandomAccessFile randomAccessFile = new RandomAccessFile("ciphertext", "rw");
FileChannel fileChannel = randomAccessFile.getChannel();
byte[] bytes = decrypted;
ByteBuffer byteBuffer = ByteBuffer.allocate(decrypted.length);//alocate new ByteBuffer of size randomeText.length
byteBuffer.put(bytes);
byteBuffer.flip();
fileChannel.write(byteBuffer);
randomAccessFile.close();
fileChannel.close();
} catch (IOException ioE) {
System.err.println("JVM reported an error! Take a look: " + ioE);
}
}
ciphertext and plaintext files are in your projects home folder created.
Related
I have found the code of AES encryption and decryption on git hub as bellow:
public static String encrypt(String plainText, String key) throws Exception {
byte[] clean = plainText.getBytes();
// Generating IV.
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Hashing key.
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(key.getBytes("UTF-8"));
byte[] keyBytes = new byte[16];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Encrypt.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(clean);
// Combine IV and encrypted part.
byte[] encryptedIVAndText = new byte[ivSize + encrypted.length];
System.arraycopy(iv, 0, encryptedIVAndText, 0, ivSize);
System.arraycopy(encrypted, 0, encryptedIVAndText, ivSize, encrypted.length);
return new String(Base64.getEncoder().encode(encryptedIVAndText));
}
// decrypt method
public static String decrypt(String encrypted, String key) throws Exception {
int ivSize = 16;
int keySize = 16;
byte[] encryptedIvTextBytes = Base64.getDecoder().decode(encrypted);
// Extract IV.
byte[] iv = new byte[ivSize];
System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.length);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Extract encrypted part.
int encryptedSize = encryptedIvTextBytes.length - ivSize;
byte[] encryptedBytes = new byte[encryptedSize];
System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, 0, encryptedSize);
// Hash key.
byte[] keyBytes = new byte[keySize];
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(key.getBytes());
System.arraycopy(md.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Decrypt.
Cipher cipherDecrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decrypted = cipherDecrypt.doFinal(encryptedBytes);
return new String(decrypted);
}
I've found the information that java determine aes algorithm 128 or 256 by key size, so I have modify the code above by changing fixed value of "keySize" to key.length() as bellow:
public static String encryptAes(String plainText, String key){
byte[] clean = plainText.getBytes();
String str = null;
int keyLen = key.length();
try{
// Generating IV.
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Hashing key.
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(key.getBytes("UTF-8"));
byte[] keyBytes = new byte[keyLen];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Encrypt.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(clean);
// Combine IV and encrypted part.
byte[] encryptedIVAndText = new byte[ivSize + encrypted.length];
System.arraycopy(iv, 0, encryptedIVAndText, 0, ivSize);
System.arraycopy(encrypted, 0, encryptedIVAndText, ivSize, encrypted.length);
str = new String(Base64.getEncoder().encode(encryptedIVAndText));
}
catch (Exception e) {
// TODO: handle exception
System.out.println("Error");
}
return str;
}
public static String decryptAes(String encrypted, String key){
int ivSize = 16;
int keySize = key.length();
String str = null;
try{
byte[] encryptedIvTextBytes = Base64.getDecoder().decode(encrypted);
// Extract IV.
byte[] iv = new byte[ivSize];
System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.length);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Extract encrypted part.
int encryptedSize = encryptedIvTextBytes.length - ivSize;
byte[] encryptedBytes = new byte[encryptedSize];
System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, 0, encryptedSize);
// Hash key.
byte[] keyBytes = new byte[keySize];
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(key.getBytes());
System.arraycopy(md.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Decrypt.
Cipher cipherDecrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decrypted = cipherDecrypt.doFinal(encryptedBytes);
str = new String(decrypted);
}
catch (Exception e) {
// TODO: handle exception
System.out.println("Error");
}
return str;
}
And here is Main with and input
public static void main (String[] args){
String txtInput = "11700000108";
String key256 = "SxKFF2Kunz7tjyUZ4sb7TzzZBR83e8FL"; //length of key will be fixed as 32
String cipher256 = encryptAes(txtInput, key256);
System.out.println("CipherAes256: " + cipher256);
String key128 = "RfZVnF76FshjN46e"; //length of key will be fixed as 16
String cipher128 = encryptAes(txtInput, key128);
System.out.println("CipherAes128: " + cipher128);
}
But the result's length of cipher256 and cipher128 are same (44 characters in this example) I'm not sure if I need to make any further changes.
Please give me advice, Thanks.
I am working on a class to encrypt/decrypt large files so I am trying to use the streams instead of byte arrays to avoid OutOfMemory exceptions.
In the encryption method I add random salt and iv to the beginning of the encrypted file and it works fine, and here is the code:
public File encryptFile(File inputFile, File outPutFile, String password, ProgressBar progressBar, Label progressPercentage){
//Create IV
byte[] ivBytes = new byte[16];
SecureRandom random1 = new SecureRandom();
random1.nextBytes(ivBytes);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
//Create the key with the salt
SecureRandom random = new SecureRandom();
byte[] salt = new byte[SALT_SIZE];
random.nextBytes(salt);
SecretKeySpec keySpec = generateAesKey(password, salt);
//Create and Init the cipher
Cipher c = Cipher.getInstance("AES/CBC/"+padding);
c.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] buf = new byte[8192];
FileInputStream in = new FileInputStream(inputFile);
FileOutputStream out = new FileOutputStream(outPutFile);
int nread;
int progress = 0;
byte[] ivAndSalt = new byte[ivBytes.length + salt.length];
System.arraycopy(ivBytes,0, ivAndSalt,0, ivBytes.length );
System.arraycopy(salt, 0, ivAndSalt, ivBytes.length, salt.length);
out.write(ivAndSalt);
while((nread = in.read(buf)) != -1) {
byte[] enc = c.update(buf, 0, nread);
out.write(enc);
progress++;
}
Then I try to get the iv and salt in decryption method and then decrypt the rest of the file into an output one with FileInputStream.getChannel.position():
public File decryptFile(File inputFile, File outPutFile, String password, ProgressBar progressBar, Label progressPercentage) {
//Create and Init Cipher
Cipher c = Cipher.getInstance("AES/CBC/" + padding);
FileInputStream in = new FileInputStream(inputFile);
FileOutputStream out = new FileOutputStream(outPutFile);
//Getting the iv and salt
byte[] ivBytes = new byte[16];
byte[] salt = new byte[SALT_SIZE];
byte[] ivAndSalt = new byte[ivBytes.length+SALT_SIZE];
in.read(ivAndSalt, 0, ivBytes.length+SALT_SIZE);
System.arraycopy(ivAndSalt, 0, ivBytes, 0, ivBytes.length);
System.arraycopy(ivAndSalt, ivBytes.length, salt, 0, SALT_SIZE);
IvParameterSpec iv =new IvParameterSpec(ivBytes);
SecretKeySpec keySpec = generateAesKey(password, salt);
c.init(Cipher.DECRYPT_MODE, keySpec, iv);
in.getChannel().position(ivAndSalt.length);
int nread;
int progress = 0;
byte[] buf = new byte[8192];
while((nread = in.read(buf)) != -1) {
byte[] enc = c.update(buf, 0, nread);
out.write(enc);
progress++;
/*if (enc.length / 8192 != 0)
System.out.println((nread*progress) + "%");*/
}
System.out.println("Size of out before doFinal(): " + out.getChannel().size());
byte[] enc = c.doFinal();
out.write(enc);
System.out.println("Size of out after doFinal(): " + out.getChannel().size());
return outPutFile;
}
I didn't get an error on calling decryptFile() but the produced file is corrupted and this means that there is an issue somewhere in the decrypting.
The problem was totally something silly, I forgot to close the input and output streams after doFinal() and writing the enc bytes array to output:
in.close();
out.close();
I have encrypted the string in php. Decrypted successfully from php and node.js. In addition, it must be decrypted by java.
Help me an example of decrypting from java?
PHP Encrypt code
/* encrypt */
$encryption_method = 'aes-256-cbc';
$secretHash = "d95acd54c6a821ff32c52825b931c194";
$iv_size = openssl_cipher_iv_length($encryption_method);
$iv = openssl_random_pseudo_bytes($iv_size);
//encrypt
$encryptedMessage = openssl_encrypt($new_token, $encryption_method, $secretHash, 0, $iv);
//Concatenate iv with data
$ciphertext = bin2hex($iv).$encryptedMessage;
/* decrypt the cipher */
$iv_size = openssl_cipher_iv_length($encryptionMethod);
$iv = hex2bin(substr($encryptedMessageWithIv, 0, $iv_size * 2));
$decryptedMessage = openssl_decrypt(substr($encryptedMessageWithIv, $iv_size * 2), $encryptionMethod, $secretHash, 0, $iv);
Below is the encryption and decryption process for a string using AES algorithm.
private static final String key = "aesEncryptionKey";
private static final String initVector = "encryptionIntVec";
public static String encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64String(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
In case init vector is not known, try using below code segment.
public byte[] decrypt(String encryptedString) throws DataLengthException, InvalidCipherTextException {
byte[] input = encryptedString.getBytes("UTF-8");
CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(new AESEngine());
SecureRandom random = new SecureRandom();;
KeyParameter key = new KeyParameter("$secretHash".getBytes());// your key string
BlockCipherPadding blockCipherPadding = new PKCS7Padding();;
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(cbcBlockCipher, blockCipherPadding);
int blockSize = cbcBlockCipher.getBlockSize(); // Make sure this block size is same as that used while encrypting the string.
int inputOffset = 0;
int inputLength = input.length;
int outputOffset = 0;
byte[] initializationVector = new byte[blockSize];
System.arraycopy(input, 0, initializationVector, 0, blockSize);
inputOffset += blockSize;
inputLength -= blockSize;
pbbc.init(encrypt, new ParametersWithIV(key, initializationVector));
byte[] output = new byte[pbbc.getOutputSize(inputLength) + outputOffset];
int outputLength = outputOffset + pbbc.processBytes(input, inputOffset, inputLength, output, outputOffset);
outputLength += pbbc.doFinal(output, outputLength);
return Arrays.copyOf(output, outputLength);
}
Just in case it helps someone in the future: encryption with AES/CBC/PKCS5PADDING along with the generation of a dynamic IV that is appended to the final ciphertext in Java can be done through the following code:
Encryption (JAVA)
public String encryptPlainText(String plainText) {
String cipherText = "";
try {
String keyString = "examplesecretkeyexamplesecretkey";
//Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy
Security.setProperty("crypto.policy", "unlimited");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec keyspec = new SecretKeySpec(keyString.getBytes(), "AES");
byte[] v = new byte[16];
new SecureRandom().nextBytes(v);
IvParameterSpec iv = new IvParameterSpec(v);
cipher.init(Cipher.ENCRYPT_MODE, keyspec, iv);
byte[] cipherTextByteArray = cipher.doFinal(plainText.getBytes());
//appending iv to ciphertext without any additional libraries to handle the concatenation of the two byte arrays
byte[] ivWithCipherTextByteArray = new byte[v.length + cipherTextByteArray.length];
System.arraycopy(v, 0, ivWithCipherTextByteArray, 0, v.length);
System.arraycopy(cipherTextByteArray, 0, ivWithCipherTextByteArray, v.length, cipherTextByteArray.length);
cipherText = new String(Base64.getEncoder().encode(ivWithCipherTextByteArray));
} catch (Exception e) {
LOG.info("Exception", e);
}
return cipherText;
}
Decryption of the cipherText obtained with the code above can be implemented in the following way:
Decryption (JAVA)
public static String decryptCipherText(String cipherText) {
String plainText="";
try {
String keyString = "examplesecretkeyexamplesecretkey";
Security.setProperty("crypto.policy", "unlimited");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec keyspec = new SecretKeySpec(keyString.getBytes(), "AES");
byte[] cipherTextByteArray = Base64.getDecoder().decode(cipherText);
//initialize the IvParameterSpec with the first 16 bytes of the cipherText
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(cipherTextByteArray, 0, 16));
//cipherText to decrypt is now the original one with the first 16 bytes removed (the IV used above)
cipherTextByteArray = Arrays.copyOfRange(cipherTextByteArray, 16, cipherTextByteArray.length);
cipher.init(Cipher.DECRYPT_MODE, keyspec, iv);
plainText = new String(cipher.doFinal(cipherTextByteArray));
} catch (Exception e) {
LOG.info("Exception", e);
}
return plainText;
}
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.
I've looked around stack to find an answer to my problem and yet nothing did work.
What im trying to achive is to encrypte XML file that is downloaded in ASYNCtask and later on decrypt it.
What I've already checked:
-Generated key is the same when encrypring and decrypting and saved in sharedpreferenced with Base64.
-IV is the same since at the moment its in static variable for testing purposes.
-Cipher is set to AES/CBC/PKCS5Padding
-Key is set to AES
The error appear in decryptXml() at line:
byte[] decrypted = cipher.doFinal(bytes);
Im all out of ideas and nothing seems to work. I hope some of u can find mistake in my code. Thanks for help!
CODE:
genetateKey()
SharedPreferences sharedPreferences = context.getSharedPreferences(GENERATED_KEY, Context.MODE_PRIVATE);
String keyStr = sharedPreferences.getString(GENERATED_KEY, null);
if (keyStr == null) {
final int outputKeyLength = 128;
SecureRandom secureRandom = new SecureRandom();
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(outputKeyLength, secureRandom);
SecretKey key = keyGenerator.generateKey();
byte[] bytes = key.getEncoded();
keyStr = Base64.encodeToString(bytes, Base64.DEFAULT);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(GENERATED_KEY, keyStr);
editor.commit();
return key.toString();
} else {
return keyStr;
}
XML encryption:
connection = (HttpURLConnection) url.openConnection();
connection.connect();
SecretKey secretKey = getSecretKey(context);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
spec = generateIv(cipher.getBlockSize());
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, spec);
input = connection.getInputStream();
cis = new CipherInputStream(input, cipher);
String FILEPATH = context.getFilesDir().getParentFile().getPath();
File file = new File(FILEPATH, "/download/" + id + "/");
if (!file.exists()) {
file.mkdirs();
}
xmlFile = new File(FILEPATH + "/download/" + id + "/", "xmldata.xml");
output = new FileOutputStream(xmlFile);
cos = new CipherOutputStream(output, cipher);
byte data[] = new byte[4096];
int count;
while ((count = cis.read(data)) != -1) {
if (isCancelled()) throw new TaskCanceledException();
cos.write(data, 0, count);
progress = -1;
publishProgress();
}
if (isCancelled()) throw new TaskCanceledException();
Decryption:
public String decryptXml() {
String data = null;
File file = new File(context.getFilesDir().getParentFile().getPath() + "/download/" + id + "/xmldata.xml");
int size = (int) file.length();
byte[] bytes = new byte[size];
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(getSecretKey(context).getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, DownloadBookAsyncTask.spec);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
bis.read(bytes, 0, bytes.length);
bis.close();
byte[] decrypted = cipher.doFinal(bytes);
}
getSecretKey():
public SecretKey getSecretKey(Context context){
SharedPreferences sharedPreferences = context.getSharedPreferences(DashboardFragment.GENERATED_KEY, Context.MODE_PRIVATE);
String stringKey = sharedPreferences.getString(DashboardFragment.GENERATED_KEY, null);
byte[] encodedKey = Base64.decode(stringKey, Base64.DEFAULT);
return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
}
EDIT
Adding IV generator method
public AlgorithmParameterSpec generateIv(int size) throws NoSuchAlgorithmException {
AlgorithmParameterSpec ivspec;
byte[] iv = new byte[size];
new SecureRandom().nextBytes(iv);
ivspec = new IvParameterSpec(iv);
return ivspec;
}
Ok I've found the problem.
The reason my code did not work was that I have used CipherInputStream in my encryption and I shouldn't have done that.
I have also redo whole decryption method which looks like this now:
byte[] wholeFileByte = null;
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, key, DownloadBookAsyncTask.ivspec);
File file = new File(context.getFilesDir().getParentFile().getPath() + "/download/" + id + "/xmldata.xml");
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
CipherInputStream cis = new CipherInputStream(fis, cipher);
byte data[] = new byte[4096];
int count;
while ((count = cis.read(data)) != -1) {
bos.write(data, 0, count);
}
if(cis != null)
cis.close();
if(bos != null)
bos.close();
if(fis != null)
fis.close();
wholeFileByte = bos.toByteArray();
String kk = new String(wholeFileByte, "UTF-8");
I think other mistake that I made was that I used doFinal in the decryption even tho the Cipher did the decryption already and that was source of some of my errors.
Thanks to #GariBN because u put me in right tracks and will upvote you when my rep allow me to :)
You create IV to encrypt the plaintext.
I'm not sure that you use the same IV in order to decrypt the ciphertext.
Usually, you want to concatenate the IV to the ciphertext, and when decrypting, read it (the first 16 bytes), and then decrypt all the other bytes (the ciphertext) with cipher that was initialized with the IV used for the encryption.
For example, if you encrypt with:
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, spec);
First, try to decrypt (right later) with:
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, spec);
cipher.doFinal(bytes)
If you succeed, then the problem is probably because inappropriate IV, and you can fix it easily.