How to Read and Encrypt File Java
In this postal service, we will discuss how to encrypt and decrypt a file using the AES encryption algorithm in GCM manner. We will start by writing a file reader / author to read and write files into byte arrays. Then we will attempt to encrypt and decrypt these byte arrays. This example has been written in Java 11. However, with little modifications, information technology can be back-ported to older JDK versions.
Reading / Writing files from and to byte arrays
The first step in our tutorial is to build the power to read and write files. We will be converting files to and from byte arrays. Nosotros demand the data to be in byte array format for encryption and decryption purposes.
Reading files in Java is quite straightforward. All that is needed is to initialize a new File object and read the file data into a byte array using a file input stream.
File file = new File(path); byte [] fileData = new byte[(int) file.length()]; try(FileInputStream fileInputStream = new FileInputStream(file)) { fileInputStream.read(fileData); }
Once we read the file using the fileInputStream, the data is copied into our byte array. Notice that we are using the effort-with-resources manner here to initialize our FileInputStream. If yous are using a Coffee version older than viii, then y'all volition need to explicitly phone call fileInputStream.close() in order to avoid any retentivity leaks.
Writing a byte array into a file is even simpler than readying from a file. All that is needed is to write the byte assortment using a FileInputStream. Here, we do not need to initialize a file. We merely need to indicate to the FileOutputStream where our output file is.
public static void writeFile(String path, byte [] information) throws IOException { try(FileOutputStream fileOutputStream = new FileOutputStream(path)) { fileOutputStream.write(data); } }
Once more, delight brand sure to close the stream if you are using a JDK version which is older than 8. To put it all together, we take put the 2 functions into a form called the NullbeansFileManager.
package com.nullbeans; import java.io.*; /** * case filereader/writer for nullbeans.com */ public class NullbeansFileManager { /** * Reads the file in the given path into a byte assortment * @param path: Path to the file, including the file name. For example: "C:/myfolder/myfile.txt" * @return byte array of the file data * @throws IOException */ public static byte[] readFile(String path) throws IOException { File file = new File(path); byte [] fileData = new byte[(int) file.length()]; try(FileInputStream fileInputStream = new FileInputStream(file)) { fileInputStream.read(fileData); } return fileData; } /** * Writes a file with the given data into a file with the given path * @param path: Path to the file to be created, including the file name. For example: "C:/myfolder/myfile.txt" * @param data: byte array of the data to be written * @throws IOException */ public static void writeFile(String path, byte [] information) throws IOException { try(FileOutputStream fileOutputStream = new FileOutputStream(path)) { fileOutputStream.write(data); } } }
Encrypting and decrypting byte arrays using AES + GCM mode
Beginning of all, if you lot are non familiar with GCM, then I would recommend that you take a few minutes to read well-nigh it hither: https://en.wikipedia.org/wiki/Galois/Counter_Mode
If y'all are not interested in the theoretical function, then y'all tin skip ahead to the implementation. But if not, then let us discuss our choice of algorithm for this tutorial 🙂
The reward of using GCM way over cake cipher modes or block concatenation ciphers is because data blocks can be encrypted in parallel, while too maintaining confidentiality by using a different counter value for encrypting each block. Other cake cipher modes such as a blockchain cipher crave that each block to exist encrypted depend on a hash value generated from encrypting the previous block. While this my be useful for integrity, it is also slow performing.
When encrypting information using AES, we need three main components:
- Key: An AES key can exist a 128 bit, 192-bit or a 256 flake. While a larger key theoretically provides more protection as it would be less vulnerable for a brute strength assail, an 128-bit cardinal is usually plenty for everyday usage. This is considering it would take an astronomical corporeality of computing power to animate being force a 128-fleck central (unless a quantum figurer is used…). In our instance, we will apply a 128-fleck key.
- Nonce:A nonce, also called aninitialization vector is a random value chosen at encryption time and is meant to be used only once. The nonce is normally sent in plain format during an encrypted transmission as decryption is nearly impossible without it. Unremarkably, the nonce is combined with a user generated password to provide an encryption key and to perform the encryption itself. If a constant encryption key is used everytime to encrypt a manual, an assailant might eventually deduce the key if given plenty encrypted information. However, since a different central is created everytime an encryption occurs past combining the user password and a nonce, it is much harder for the attacker to decrypt a transmission or to deduce the user password.
- Information: The third component in this formula is the actual data.
One can visualize the procedure in Coffee via the following diagram:
Implementation
Generating an AES central
In club to perform whatever encryption / decryption using AES, nosotros will first need to generate the key. Since our example will use a user called countersign and a nonce/initialization vector (iv), let us start by creating our key generation method:
public static SecretKey generateSecretKey(Cord password, byte [] iv) throws NoSuchAlgorithmException, InvalidKeySpecException { KeySpec spec = new PBEKeySpec(password.toCharArray(), iv, 65536, 128); // AES-128 SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); byte[] key = secretKeyFactory.generateSecret(spec).getEncoded(); return new SecretKeySpec(cardinal, "AES"); }
Here we employ a PBE key spec to generate a 128-chip key. This is required in order to generate a fixed length key. Since the password provided past the user can vary in length, nosotros will need to make sure that we always have a 128-bit primal. Otherwise, the key will exist rejected past the encryption algorithm.
Encrypting a byte array
Now, allow us start with the process of encrypting the byte array. To get-go the encryption, nosotros demand the 3 components mentioned previously. Permit us get-go by generating the nonce.
//Prepare the nonce SecureRandom secureRandom = new SecureRandom(); //Noonce should exist 12 bytes byte[] iv = new byte[12]; secureRandom.nextBytes(iv);
The SecureRandom class provides us with random values to be used for generating the initialization vector.The GCM specification recommends a 12 byte nonce. Therefore, we cull to create a 12 byte assortment for the job. Our next step is to generate our encryption primal and perform the encryption.
//Set your central/password SecretKey secretKey = generateSecretKey(cardinal, 4); Cipher cipher = Goose egg.getInstance("AES/GCM/NoPadding"); GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //Encryption mode on! nil.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec); //Encrypt the data byte [] encryptedData = cypher.doFinal(information);
The Cipher class can perform different types of encryption/decryption procedures. Here, we configured our instance for AES + GCM encryption. The nil.doFinal(data) call takes-in the plain text information byte assortment and returns the encrypted assortment.
Note that the encrypted array does not include the nonce or the nonce size. Therefore we will need to generate another byte array with the nonce and the nonce size prepended to information technology. This tin can be achieved using a ByteBuffer. Too observe the Aught.ENCRYPT_MODE which we selected during initialization. Yous can notice the complete encryption office in the side by side subsection.
Decrypting a byte array
To decrypt a byte array, we basically need to perform the same steps as in the encrypt function, just this time, nosotros chose the Zippo.DECRYPT_MODE. The merely extra steps that we need to perform is to excerpt the nonce and the nonce length from the beginning of the byte assortment.
//Wrap the data into a byte buffer to ease the reading process ByteBuffer byteBuffer = ByteBuffer.wrap(encryptedData); int noonceSize = byteBuffer.getInt(); //Make certain that the file was encrypted properly if(noonceSize < 12 || noonceSize >= 16) { throw new IllegalArgumentException("Nonce size is incorrect. Make certain that the incoming information is an AES encrypted file."); } byte[] iv = new byte[noonceSize]; byteBuffer.get(iv);
Once nosotros have the initialization vector, we can simply combine it with the user's password to generate the secret key and begin our decryption process. Below is our consummate AESEncryptionManager example class:
package com.nullbeans; import javax.crypto.*; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import java.nio.ByteBuffer; import java.security.InvalidAlgorithmParameterException; import coffee.security.InvalidKeyException; import coffee.security.NoSuchAlgorithmException; import coffee.security.SecureRandom; import coffee.security.spec.InvalidKeySpecException; import coffee.security.spec.KeySpec; /** * Encryption / Decryption service using the AES algorithm * example for nullbeans.com */ public class AESEncryptionManager { /** * This method volition encrypt the given data * @param key : the countersign that will be used to encrypt the data * @param information : the data that volition exist encrypted * @return Encrypted data in a byte array */ public static byte [] encryptData(String key, byte [] data) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException { //Prepare the nonce SecureRandom secureRandom = new SecureRandom(); //Noonce should exist 12 bytes byte[] iv = new byte[12]; secureRandom.nextBytes(iv); //Prepare your key/password SecretKey secretKey = generateSecretKey(cardinal, iv); Naught cipher = Aught.getInstance("AES/GCM/NoPadding"); GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //Encryption mode on! cipher.init(Aught.ENCRYPT_MODE, secretKey, parameterSpec); //Encrypt the data byte [] encryptedData = cipher.doFinal(data); //Concatenate everything and return the concluding data ByteBuffer byteBuffer = ByteBuffer.classify(4 + iv.length + encryptedData.length); byteBuffer.putInt(iv.length); byteBuffer.put(4); byteBuffer.put(encryptedData); return byteBuffer.array(); } public static byte [] decryptData(String cardinal, byte [] encryptedData) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException { //Wrap the data into a byte buffer to ease the reading procedure ByteBuffer byteBuffer = ByteBuffer.wrap(encryptedData); int noonceSize = byteBuffer.getInt(); //Brand certain that the file was encrypted properly if(noonceSize < 12 || noonceSize >= 16) { throw new IllegalArgumentException("Nonce size is incorrect. Make sure that the incoming data is an AES encrypted file."); } byte[] iv = new byte[noonceSize]; byteBuffer.go(iv); //Prepare your primal/countersign SecretKey secretKey = generateSecretKey(primal, iv); //get the rest of encrypted information byte[] cipherBytes = new byte[byteBuffer.remaining()]; byteBuffer.get(cipherBytes); Naught naught = Cipher.getInstance("AES/GCM/NoPadding"); GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //Encryption mode on! cipher.init(Nix.DECRYPT_MODE, secretKey, parameterSpec); //Encrypt the information render naught.doFinal(cipherBytes); } /** * Function to generate a 128 bit key from the given countersign and iv * @param password * @param iv * @render Secret cardinal * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException */ public static SecretKey generateSecretKey(String countersign, byte [] iv) throws NoSuchAlgorithmException, InvalidKeySpecException { KeySpec spec = new PBEKeySpec(password.toCharArray(), four, 65536, 128); // AES-128 SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); byte[] primal = secretKeyFactory.generateSecret(spec).getEncoded(); return new SecretKeySpec(key, "AES"); } }
Encrypting and decrypting files
At the beginning of our example, nosotros mentioned that our goal is to be able to encrypt and decrypt files using our own written Java program. And then let u.s. start past defining our chief class and run an example:
public class Primary { public static void chief(String[] args) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeySpecException, IOException { // write your lawmaking here Cord file = args[0]; String destFile = args[1]; String fashion = args[two]; String password = args[three]; byte[] fileBytes = NullbeansFileManager.readFile(file); byte[] resultBytes = null; if(mode.equalsIgnoreCase("encrypt")){ resultBytes = AESEncryptionManager.encryptData(password, fileBytes); }else { resultBytes = AESEncryptionManager.decryptData(countersign, fileBytes); } NullbeansFileManager.writeFile(destFile, resultBytes); } }
Let us run our example with the post-obit plain text file every bit our plain text data.
Our arguments will include the file "PlainText.txt" equally the input file, "EncryptedText.txt" as the output file, the "encrypt" argument and the countersign "GreatePass". If we open the encrypted file it will look similar this:
As you tin can encounter, the data has been encrypted successfully. Now let us try to decrypt the file. As AES is a symmetric encryption algorithm, nosotros volition need to provide the same password in order to decrypt the file. The output file looks as follows:
Equally our decryption upshot is the aforementioned as the plainText file, we can verify that our encryption/decryption procedure has been performed successfully. Delight feel free to effort our instance with different key lengths and different passwords.
Decrypting using the wrong password
And then what if nosotros tried to insert the incorrect password. What would happen? Well, nosotros tried it out, and this was the output:
Exception in thread "chief" javax.crypto.AEADBadTagException: Tag mismatch! at java.base/com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:580) at java.base/com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1116) at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1053) at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.coffee:853) at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446) at java.base/javax.crypto.Aught.doFinal(Cipher.java:2202) at com.nullbeans.AESEncryptionManager.decryptData(AESEncryptionManager.java:92) at com.nullbeans.Main.principal(Main.java:28)
This exception indicates that the Cipher could not verify the given Authentication tag. In other words, the combination of countersign and nonce did not match the 1 used to encrypt the file, and therefore the decryption process fails. Then if your user tried to insert the wrong passwords, y'all will know 😉
Summary
In this tutorial, we discussed how to read and write a file from the filesystem into a byte assortment. We then discussed how to encrypt and decrypt data using AES in GCM mode. Give thanks you for reading 🙂
Source: https://nullbeans.com/how-to-encrypt-decrypt-files-byte-arrays-in-java-using-aes-gcm/
0 Response to "How to Read and Encrypt File Java"
Post a Comment