3

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)

1 Answer 1

8

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

return cipher.doFinal(textBytes);

with two lines:

cipher.update(textBytes);
return cipher.doFinal(tagBytes);
Sign up to request clarification or add additional context in comments.

3 Comments

It works perfectly thanks. Does this mean that the tag bytes are appended at the end of the text bytes?
@AnishHirlekar yes, Java expects the bytes of the ciphertext immediately followed by the tag bytes, and will check that the tag matches, throwing an exception if it doesn’t.
Works perfectly, but I'm wondering why cipher.updateAAD(tagBytes) and then cipher.doFinal(textBytes) doesn't work Nodejs-to-java (produces "tag mismatch"), but works Java-to-Java.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.