I have the following function written in Javascript for encryption using aes-256-gcm:
encrypt: function (text, masterkey){
try {
// random initialization vector
var iv = crypto.randomBytes(12);
// random salt
var salt = crypto.randomBytes(64);
// derive key: 32 byte key length - in assumption the masterkey is a cryptographic and NOT a password there is no need for
// a large number of iterations. It may can replaced by HKDF
var key = crypto.pbkdf2Sync(masterkey, salt, 2145, 32, 'sha512');
// AES 256 GCM Mode
var cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
// encrypt the given text
var encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
// extract the auth tag
var tag = cipher.getAuthTag();
// generate output
return Buffer.concat([salt, iv, tag, encrypted]).toString('base64');
}catch(e){
}
// error
return null;
}
The encrypted text by the above function is successfully decrypted back using the following function:
decrypt: function (data, masterkey){
try {
// base64 decoding
var bData = new Buffer(data, 'base64');
var salt = bData.slice(0, 64);
var iv = bData.slice(64, 76);
var tag = bData.slice(76, 92);
var text = bData.slice(92);
// derive key using; 32 byte key length
var key = crypto.pbkdf2Sync(masterkey, salt , 2145, 32, 'sha512');
// AES 256 GCM Mode
var decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
// decrypt the given text
var decrypted = decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');
return decrypted;
}catch(e){
}
// error
return null;
}
Now, I need a method in Java for decryption which is the equivalent of the above Javascript decrypt function. Following is the Java code I have written for decryption:
public void decrypt(byte[] nkb, String crKey){
//nkb is byte array formed by Base64 decoding of 'data' variable in the Javascript code
//crKey corresponds to the 'masterkey' variable
byte[] salt = Arrays.copyOfRange(nkb, 0, 64);
byte[] iv = Arrays.copyOfRange(nkb, 64, 76);
byte[] tag = Arrays.copyOfRange(nkb, 76, 92);
byte[] text = Arrays.copyOfRange(nkb, 92, nkb.length);
PBEKeySpec ks = new PBEKeySpec(crKey.toCharArray(), salt, iterations, 256);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
SecretKey pbeKey = skf.generateSecret(ks);
byte[] decrypted = decrypt(iv, pbeKey.getEncoded(), text, tag);
}
public static byte[] decrypt(byte[] ivBytes, byte[] keyBytes, byte[] textBytes, byte[] tagBytes)
throws java.io.UnsupportedEncodingException,
NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
InvalidAlgorithmParameterException,
IllegalBlockSizeException,
BadPaddingException,
NoSuchProviderException {
GCMParameterSpec ivSpec = new GCMParameterSpec(tagBytes.length*Byte.SIZE, ivBytes);
SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
return cipher.doFinal(textBytes); //getting tag mismatch error here
}
As I have commented in the above code, I get a Tag mismatch error in the last line. I would appreciate some help in finding out what I am doing wrong.
I have error in this line code:
cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec)
java.security.InvalidKeyException: Illegal key size
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)
at javax.crypto.Cipher.implInit(Cipher.java:805)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1396)
at javax.crypto.Cipher.init(Cipher.java:1327)
at com.micropro.namwebservice.utils.CryptoUtils.decrypt(CryptoUtils.java:93)
at com.micropro.namwebservice.utils.CryptoUtils.decrypt(CryptoUtils.java:82)
You need to provide the tag to the Java GCM code so that it can check that the message is authentic. The Java API expects the tag to be appended to the ciphertext. The simplest way to change your code to do that would be to replace the line
with two lines: