This snippet is about cross-platform AES-GCM-256 encryption & decryption, Where payload is being encrypted using JAVA and decrypted using NODE
sample config :
{
"masterKey": "sfcpnnjFG6dULJfo1BEGqczpfN0SmwZ6bgKO5FcDRfI=",
"iterations": 2333,
"keyLength": 32,
"digest": "sha512"
}
The above mentioned values in iterations and keylength are in bytes which needs to be passed in bits inside java
Java Snippet :
import java.io.ByteArrayOutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class AES256GCMAlgo {
static String plainText = "sample text to encrypt";
public static final int GCM_IV_LENGTH = 16;
public static final int GCM_TAG_LENGTH = 16;
public static final int ITERATIONS = 2333;
public static final int KEY_LENGTH = 32;
public static final String MASTER_KEY = "sfcpnnjFG6dULJfo1BEGqczpfN0SmwZ6bgKO5FcDRfI=";
public static void main(String[] args) throws Exception
{
byte[] salt = getSalt();
byte[] encodedKey = generateRandomKeyFromMaster(salt);
byte[] IV = new byte[GCM_IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
SecretKey secretKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
byte[] cipherText = encrypt(plainText.getBytes(), secretKey, IV);
byte[] clippedCipherText = Arrays.copyOfRange(cipherText, 0, cipherText.length - (128 / Byte.SIZE) );
byte[] tagVal = Arrays.copyOfRange(cipherText, cipherText.length - (128 / Byte.SIZE), cipherText.length);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write( salt );
outputStream.write( IV );
outputStream.write( tagVal );
outputStream.write( clippedCipherText );
byte addedEncyptedVal[] = outputStream.toByteArray();
//Following us the encrypted value
System.out.println("Encrypted Value is below: " );
System.out.println(Base64.getEncoder().encodeToString(addedEncyptedVal));
//You can uncomment and call below method to check the decrypted value
//String decryptedText = decrypt(cipherText, secretKey, IV);
//System.out.println("DeCrypted Text : " + decryptedText);
}
public static byte[] encrypt(byte[] plaintext, SecretKey key, byte[] IV) throws Exception
{
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
byte[] cipherText = cipher.doFinal(plaintext);
return cipherText;
}
public static String decrypt(byte[] cipherText, SecretKey key, byte[] IV) throws Exception
{
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
public static byte[] generateRandomKeyFromMaster(byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException
{
char[] masterKey = MASTER_KEY.toCharArray();
PBEKeySpec spec = new PBEKeySpec(masterKey, salt, ITERATIONS, KEY_LENGTH * 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
byte[] randomKey = skf.generateSecret(spec).getEncoded();
return randomKey;
}
public static byte[] getSalt() throws NoSuchAlgorithmException
{
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[64];
sr.nextBytes(salt);
return salt;
}
}
On running the above snippet you will get output like this :
Encrypted Value is below: TQ1yKce0sbBLYUI2OeDzS2MOQqnev1s/K8k/Lm81asURNUCu2ykL9i44WGEvHUeQ+MpRW5xHU+iKyeE8FpfrANy1EJIsO0CaDgoAFVohl1ReTDCMlCPlZZxlx7ZNL/7d0oQoQR7bTp+0pF+/FWOiinMs0a1QdA==
(if u don't have java in your machine then u can use this website to run the above snippet https://www.jdoodle.com/online-java-compiler)
Pass the encrypted value in Node.js with the above config as shown in snippet below :
Node Snippet :
const crypto = require('crypto');
const decryptAes256Gcm = (encdata, cryptoConfigObject) => {
try {
// base64 decoding
const bData = Buffer.from(encdata, 'base64');
// convert data to buffers
const salt = bData.slice(0, 64);
const iv = bData.slice(64, 80);
const tag = bData.slice(80, 96);
const text = bData.slice(96);
// derive key using; 32 byte key length
const key = crypto.pbkdf2Sync(cryptoConfigObject.masterKey, salt, cryptoConfigObject.iterations, cryptoConfigObject.keyLength, cryptoConfigObject.digest);
// AES 256 GCM Mode
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
return decipher.update(text, 'binary', 'utf8') + decipher.final('utf8')
} catch (err) {
return err
}
}
// ======================== //////// ==========================
const cryptoConfig = {
masterKey: 'sfcpnnjFG6dULJfo1BEGqczpfN0SmwZ6bgKO5FcDRfI=',
iterations: 2333,
keyLength: 32,
digest: 'sha512'
}
const encryptedData = 'TQ1yKce0sbBLYUI2OeDzS2MOQqnev1s/K8k/Lm81asURNUCu2ykL9i44WGEvHUeQ+MpRW5xHU+iKyeE8FpfrANy1EJIsO0CaDgoAFVohl1ReTDCMlCPlZZxlx7ZNL/7d0oQoQR7bTp+0pF+/FWOiinMs0a1QdA=='
console.log('decrypted data ->', decryptAes256Gcm(encryptedData, cryptoConfig))
// ======================== //////// ==========================
On running the above snippet you will get output like this :
decrypted data -> sample text to encrypt
(if u don't have Node.js in your machine then u can use this website to run the above snippet https://www.jdoodle.com/execute-nodejs-online)