You already get a link from @Saptarshi Basu that shows in general how to encrypt data with AES GCM. As you see with my code there is nothing really "mystic" doing this but there are some traps to run in.
Let's start with the most important information - what is the encryption key ? From Hashicorp you received an 44 bytes long string that is the pure 32 bytes long AES GCM key but in Base64-encoding. To get the key usable with Java encryption you need to decode the key to a byte array like this:
String keyBase64 = "VxJWkOYm2F5z1nF1th9zreS6ZAZMFkCq0c/Ik460ayw=";
byte[] key = Base64.getDecoder().decode(keyBase64);
The second information we do need is the AES mode - you named it correctly as AES GCM mode and as you provide Java an 32 byte = 256 bit long key it's the requested AES GCM 256 algorithm/mode.
There is a third parameter necessary for AES GCM encryption and it's the nonce (or sometimes named as initialization vector). Hashicorp tells you to use a 96 bit = 12 byte long nonce. For safety reasons it is important that you use a different nonce each time you encrypt so it is good practice to use a (secure) randomly generated nonce:
byte[] nonceRandom = new byte[12];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(nonceRandom);
Now we are ready for encryption and putting all data together, do the ".doFinal" step and we receive a byte array with the ciphertext. But stop - we need to concatenate the used nonce and the ciphertext to a larger ciphertextWithNonce this way:
nonce | ciphertext
by simply copying the nonce and ciphertext to a new byte array. This "ciphertextWithNonce" is then Base64 encoded to the final ciphertextBase64 and for upload reasons written to a file.
If you paste your own key in the beginning of the program and run it you will receive a file named "hashicorp_test.enc" that is ready for upload to your fault.
This is a sample output (yours will differ as there is a random element):
Hashicorp Vault AES GCM encryption
ciphertext: /YB+kfVlIhMowLrsnndD737o2CcyWMfr4xnAADnCBSNCSvMG25aR8UzU2ta8wLwdnHfcago/25KFJ2ky95wpFtsCNE63xRs=
ciphertext written to file: hashicorp_test.enc
used key: VxJWkOYm2F5z1nF1th9zreS6ZAZMFkCq0c/Ik460ayw=
If you like to see this code running in an online compiler here is the link:
https://repl.it/@javacrypto/SoHashicorpVaultAesGcmEncryption
This is a "proof of concept" to show in general how to perform an encryption but it lacks some critical points that I'm to lazy to make your work :-).
- This example encrypts a string to an encrypted file - you will need to get the original data from a file
- Having a large file you may encounter an "out of memory" error as all operations with your data are done in
your heap - for a simple calculation you will need a free memory of 4.5 * original data because you take the original data
into memory, second time you have the encrypted data in memory, third time you're copying the encrypted data to
ciphertextWithNonce and in the end (number 4) you encode all data to a base64-String. For large programs you will need to
switch to a "chunk wise" encryption, done with CiphertextOutputStream
- To make the Base64-writing of the complete data a little more convenient I recommend the additional usage of Apache's Base64OutputStream (available via Maven https://mvnrepository.com/artifact/commons-codec/commons-codec).
Security warning: this code has no exception handling and is for educational purpose only.
code:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class Hashicorp_Aes_Gcm_encryption {
public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException {
System.out.println("Hashicorp Vault AES GCM encryption");
// https://stackoverflow.com/questions/64714527/how-can-i-encrypt-data-with-an-already-generated-aes-256-gcm-96-key-coming-from
// paste your key here:
String keyBase64 = "VxJWkOYm2F5z1nF1th9zreS6ZAZMFkCq0c/Ik460ayw=";
// filename with ciphertext for upload
String filename = "hashicorp_test.enc";
// my sample plaintext
String plaintext = "The quick brown fox jumps over the lazy dog";
// aes gcm encryption
// decode key
byte[] key = Base64.getDecoder().decode(keyBase64);
// generate random nonce
byte[] nonceRandom = new byte[12];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(nonceRandom);
// calculate specs
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * 8, nonceRandom);
// initialize cipher
Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");//NOPadding
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
// encrypt
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
// concentenate iv + ciphertext
int ciphertextWithNonceLength = nonceRandom.length + ciphertext.length;
byte[] ciphertextWithNonce = new byte[ciphertextWithNonceLength];
System.arraycopy(nonceRandom, 0, ciphertextWithNonce, 0, nonceRandom.length);
System.arraycopy(ciphertext, 0, ciphertextWithNonce, nonceRandom.length, ciphertext.length);
String ciphertextBase64 = Base64.getEncoder().encodeToString(ciphertextWithNonce);
System.out.println("ciphertext: " + ciphertextBase64);
// save encrypted data to a file
Files.write(Paths.get(filename), ciphertextBase64.getBytes(StandardCharsets.UTF_8));
System.out.println("ciphertext written to file: " + filename);
System.out.println("used key: " + keyBase64);
}
}