最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

AESGCMNoPadding encryption in JAVA & decryption in JavaScript - Stack Overflow

programmeradmin1浏览0评论

I am using AES/GCM/NoPadding 128 bit. I want to encrypt in Java & decrypt it in Javascript.

When I encrypt in JS & try to decrypt in Java I get the error as Tag mismatch!null

When I encrypt in Java & try to decrypt in JS I get the error as

internal/crypto/cipher.js:164
  const ret = this._handle.final();
                           ^

Error: Unsupported state or unable to authenticate data
    at Decipheriv.final (internal/crypto/cipher.js:164:28)
    at decrypt (/tmp/HoErdq6TQ2.js:51:58)

What am I missing in my JS Please suggest fixes in JS code, Java code can't be changed as it is in use on production

Java code:


import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import java.util.logging.Logger;
import java.util.Base64;

public class HelloWorld {

    private final static Logger LOGGER =  Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);

    public static void main(String []args) {
        String masterKey = "2f12cb0f1d2e3d12345f1af2b123dce4";
        String encrypted = aesEncryptStringV2("Hello, World!", masterKey);
        System.out.println(encrypted);
        

        String decrypted = aesDecryptStringV2(encrypted, masterKey);
        System.out.println(decrypted);
    }


    private static final String ALGORITHM = "AES/GCM/NoPadding";
    private static final int GCM_IV_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 16;

    private static SecretKeySpec setKeyV2(final String myKey) {
        try {
            byte[] newKey = myKey.getBytes(StandardCharsets.UTF_8);
            MessageDigest sha = MessageDigest.getInstance("SHA-512");

            newKey = sha.digest(newKey);
            newKey = Arrays.copyOf(newKey, 16);

            return new SecretKeySpec(newKey, "AES");
        } catch (Exception e) {
            System.out.println("Error in setKeyV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }

    public static synchronized String aesEncryptStringV2(
        final String strToEncrypt, final String secret) {
        try {
            SecretKeySpec newSecretKey = setKeyV2(secret);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec gcmParameterSpec = new
            GCMParameterSpec(GCM_TAG_LENGTH * 8,
                             new byte[GCM_IV_LENGTH]);
            cipher.init(Cipher.ENCRYPT_MODE, newSecretKey, gcmParameterSpec);
            return Base64.getEncoder()
                   .encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8
                                                                       )));
        } catch (Exception e) {
            System.out.println("Error in aesEncryptStringV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }

    public static synchronized String aesDecryptStringV2(
        final String strToDecrypt, final String secret) {
        try {
            SecretKeySpec newSecretKey = setKeyV2(secret);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec gcmParameterSpec = new
            GCMParameterSpec(GCM_TAG_LENGTH * 8,
                             new byte[GCM_IV_LENGTH]);
            cipher.init(Cipher.DECRYPT_MODE, newSecretKey, gcmParameterSpec);

            return new
                   String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
        } catch (Exception e) {
            System.out.println("Error in aesDecryptStringV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }
}

Javascript Code:


const crypto = require('crypto');


const cryptoConfig = {
    cipherAlgorithm: 'aes-128-gcm',
    masterKey: '2f12cb0f1d2e3d12345f1af2b123dce4',
    ivLength: 12,
    tagLength: 16,
    digest: 'sha512'
}

const generateKey = () => {
    var h = crypto.createHash(cryptoConfig.digest);
    h.update(cryptoConfig.masterKey, 'utf8');
    
    var k = h.digest().slice(0, 16);
    return k;
  };

function encrypt(content) {
    const iv = crypto.randomBytes(cryptoConfig.ivLength);
    const key = generateKey();

    const cipher = crypto.createCipheriv(cryptoConfig.cipherAlgorithm, key, iv, {authTagLength: cryptoConfig.tagLength});

    const encrypted = Buffer.concat([cipher.update(content, 'utf8'), cipher.final()]);

    const tag = cipher.getAuthTag();

    return Buffer.concat([iv, encrypted, tag]).toString('base64');
}


const decrypt = (encdata, masterkey) => {
    const bData = Buffer.from(encdata, 'base64');

    const iv = bData.slice(0, 12);
    const tag = bData.slice(bData.length - 16, bData.length);
    const text = bData.slice(12, bData.length - 16);

    const key = generateKey(masterkey);

    const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv);
    decipher.setAuthTag(tag);


    const decrypted =
      decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');


    return decrypted;
  };

const encryptedData = encrypt('hello world'); 
console.log('encrypt data -> ', encryptedData);

const decryptedData = decrypt(encryptedData); 
console.log('decryptedData -> ', decryptedData);

I am using AES/GCM/NoPadding 128 bit. I want to encrypt in Java & decrypt it in Javascript.

When I encrypt in JS & try to decrypt in Java I get the error as Tag mismatch!null

When I encrypt in Java & try to decrypt in JS I get the error as

internal/crypto/cipher.js:164
  const ret = this._handle.final();
                           ^

Error: Unsupported state or unable to authenticate data
    at Decipheriv.final (internal/crypto/cipher.js:164:28)
    at decrypt (/tmp/HoErdq6TQ2.js:51:58)

What am I missing in my JS Please suggest fixes in JS code, Java code can't be changed as it is in use on production

Java code:


import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import java.util.logging.Logger;
import java.util.Base64;

public class HelloWorld {

    private final static Logger LOGGER =  Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);

    public static void main(String []args) {
        String masterKey = "2f12cb0f1d2e3d12345f1af2b123dce4";
        String encrypted = aesEncryptStringV2("Hello, World!", masterKey);
        System.out.println(encrypted);
        

        String decrypted = aesDecryptStringV2(encrypted, masterKey);
        System.out.println(decrypted);
    }


    private static final String ALGORITHM = "AES/GCM/NoPadding";
    private static final int GCM_IV_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 16;

    private static SecretKeySpec setKeyV2(final String myKey) {
        try {
            byte[] newKey = myKey.getBytes(StandardCharsets.UTF_8);
            MessageDigest sha = MessageDigest.getInstance("SHA-512");

            newKey = sha.digest(newKey);
            newKey = Arrays.copyOf(newKey, 16);

            return new SecretKeySpec(newKey, "AES");
        } catch (Exception e) {
            System.out.println("Error in setKeyV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }

    public static synchronized String aesEncryptStringV2(
        final String strToEncrypt, final String secret) {
        try {
            SecretKeySpec newSecretKey = setKeyV2(secret);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec gcmParameterSpec = new
            GCMParameterSpec(GCM_TAG_LENGTH * 8,
                             new byte[GCM_IV_LENGTH]);
            cipher.init(Cipher.ENCRYPT_MODE, newSecretKey, gcmParameterSpec);
            return Base64.getEncoder()
                   .encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8
                                                                       )));
        } catch (Exception e) {
            System.out.println("Error in aesEncryptStringV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }

    public static synchronized String aesDecryptStringV2(
        final String strToDecrypt, final String secret) {
        try {
            SecretKeySpec newSecretKey = setKeyV2(secret);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec gcmParameterSpec = new
            GCMParameterSpec(GCM_TAG_LENGTH * 8,
                             new byte[GCM_IV_LENGTH]);
            cipher.init(Cipher.DECRYPT_MODE, newSecretKey, gcmParameterSpec);

            return new
                   String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
        } catch (Exception e) {
            System.out.println("Error in aesDecryptStringV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }
}

Javascript Code:


const crypto = require('crypto');


const cryptoConfig = {
    cipherAlgorithm: 'aes-128-gcm',
    masterKey: '2f12cb0f1d2e3d12345f1af2b123dce4',
    ivLength: 12,
    tagLength: 16,
    digest: 'sha512'
}

const generateKey = () => {
    var h = crypto.createHash(cryptoConfig.digest);
    h.update(cryptoConfig.masterKey, 'utf8');
    
    var k = h.digest().slice(0, 16);
    return k;
  };

function encrypt(content) {
    const iv = crypto.randomBytes(cryptoConfig.ivLength);
    const key = generateKey();

    const cipher = crypto.createCipheriv(cryptoConfig.cipherAlgorithm, key, iv, {authTagLength: cryptoConfig.tagLength});

    const encrypted = Buffer.concat([cipher.update(content, 'utf8'), cipher.final()]);

    const tag = cipher.getAuthTag();

    return Buffer.concat([iv, encrypted, tag]).toString('base64');
}


const decrypt = (encdata, masterkey) => {
    const bData = Buffer.from(encdata, 'base64');

    const iv = bData.slice(0, 12);
    const tag = bData.slice(bData.length - 16, bData.length);
    const text = bData.slice(12, bData.length - 16);

    const key = generateKey(masterkey);

    const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv);
    decipher.setAuthTag(tag);


    const decrypted =
      decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');


    return decrypted;
  };

const encryptedData = encrypt('hello world'); 
console.log('encrypt data -> ', encryptedData);

const decryptedData = decrypt(encryptedData); 
console.log('decryptedData -> ', decryptedData);

Share edited Dec 26, 2022 at 7:03 kashif-sk asked Dec 26, 2022 at 7:02 kashif-skkashif-sk 611 gold badge1 silver badge10 bronze badges 2
  • 1 The Java encryption code isn't returning the IV/nonce used for encryption so it is not part of the splitting in decryption code. Serious security warning: Do NOT use the above code in production as it is UNSECURE. You should consider to change your production code as the encryption in AES GCM mode gets total insecure when using a nonce more than one time with the same key (the Java code is using a static IV/nonce with 8 0x's). – Michael Fehr Commented Dec 26, 2022 at 7:46
  • @MichaelFehr I'm able to decrypt now. Thanks for the clarity & security warning, will switch to a more secure one. – kashif-sk Commented Dec 26, 2022 at 8:06
Add a ment  | 

1 Answer 1

Reset to default 2

So the Java code doesn't attach IV/nonce to the output. It uses a buffer of 0's of specified length.

Updated JS decryption code:

const decrypt = (encdata, masterkey) => {
const bData = Buffer.from(encdata, 'base64');

const iv = Buffer.alloc(12);
const tag = bData.slice(bData.length - 16, bData.length);
const text = bData.slice(0, bData.length - 16);

const key = generateKey(masterkey);

const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv);
decipher.setAuthTag(tag);


const decrypted =
  decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');


return decrypted;
};

Serious security warning: Do NOT use the above code in production as it is UNSECURE. You should consider to change your production code as the encryption in AES GCM mode gets total insecure when using a nonce more than one time with the same key (the Java code is using a static IV/nonce)

Credits - Michael Fehr's ment

发布评论

评论列表(0)

  1. 暂无评论