AES GCM Encryption Code (Secure Enough or not)


I am creating one library for encryption/decryption using AES-256 with GCM Mode(With Random IV/Random Salt) for every request.

Code I have written is :

    public class AESGCMChanges {

    static String plainText1 = "DEMO text to be encrypted @1234";
    static String plainText2 = "999999999999";
    
   
    public static final int AES_KEY_SIZE = 256;
    public static final int GCM_IV_LENGTH = 12;
    public static final int GCM_TAG_LENGTH = 16;
    public static final int GCM_SALT_LENGTH = 32;
    
    private static final String FACTORY_ALGORITHM = "PBKDF2WithHmacSHA256";
    private static final String KEY_ALGORITHM = "AES";
    private static final int KEYSPEC_ITERATION_COUNT = 65536;
    private static final int KEYSPEC_LENGTH = 256;
    
    private static final String dataKey = "demoKey";

    public static void main(String() args) throws Exception
    {
     
        byte() cipherText = encrypt(plainText1.getBytes());
       
        String decryptedText = decrypt(cipherText);
        System.out.println("DeCrypted Text : " + decryptedText);
        
        cipherText = encrypt(plainText2.getBytes());
        
        decryptedText = decrypt(cipherText);
        System.out.println("DeCrypted Text : " + decryptedText);
    }

    public static byte() encrypt(byte() plaintext) throws Exception
    {
        byte() IV = new byte(GCM_IV_LENGTH);
        SecureRandom random = new SecureRandom();
        random.nextBytes(IV);
        
        // Get Cipher Instance
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        
        byte() salt = generateSalt();
        
        // Generate Key
        SecretKey key = getDefaultSecretKey(dataKey, salt);
    
        
        // Create GCMParameterSpec
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
        
        // Initialize Cipher for ENCRYPT_MODE
        cipher.init(Cipher.ENCRYPT_MODE, key, gcmParameterSpec);
        
        // Perform Encryption
        byte() cipherText = cipher.doFinal(plaintext);
        byte() message = new byte(GCM_SALT_LENGTH + GCM_IV_LENGTH + plaintext.length + GCM_TAG_LENGTH);
        
        System.arraycopy(salt, 0, message, 0, GCM_SALT_LENGTH);
        System.arraycopy(IV, 0, message, GCM_SALT_LENGTH, GCM_IV_LENGTH);
        System.arraycopy(cipherText, 0, message, GCM_SALT_LENGTH+GCM_IV_LENGTH, cipherText.length);
     
        
        return message;
    }

    public static String decrypt(byte() cipherText) throws Exception
    {

        if (cipherText.length < GCM_IV_LENGTH + GCM_TAG_LENGTH + GCM_SALT_LENGTH) throw new IllegalArgumentException();
        ByteBuffer buffer = ByteBuffer.wrap(cipherText);
    
        // Get Salt from Cipher
        byte() salt = new byte(GCM_SALT_LENGTH);
        buffer.get(salt, 0, salt.length);
        
        // GET IV from cipher
        byte() ivBytes1 = new byte(GCM_IV_LENGTH);
        buffer.get(ivBytes1, 0, ivBytes1.length);
        
        byte() encryptedTextBytes = new byte(buffer.capacity() - salt.length - ivBytes1.length);
        buffer.get(encryptedTextBytes);
        
        // Get Cipher Instance
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        
        // Generate Key
        SecretKey key = getDefaultSecretKey(dataKey, salt);
    
        // Create GCMParameterSpec
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, ivBytes1);
        
        // Initialize Cipher for DECRYPT_MODE
        cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
        
        // Perform Decryption
        byte() decryptedText = cipher.doFinal(encryptedTextBytes);
        
        return new String(decryptedText);
    }
    
   

    
    private static SecretKey getDefaultSecretKey(final String password, final byte() salt) throws NoSuchAlgorithmException, InvalidKeySpecException{
        return getSecretKey(password, salt, FACTORY_ALGORITHM, KEY_ALGORITHM, KEYSPEC_ITERATION_COUNT, KEYSPEC_LENGTH);
    }

    private static SecretKey getSecretKey(final String password, 
            final byte() salt, 
            final String factoryAlgorithm, 
            final String keyAlgorithm, 
            final int iterationCount, 
            final int keyLength) throws NoSuchAlgorithmException, InvalidKeySpecException{
        SecretKeyFactory factory = SecretKeyFactory.getInstance(factoryAlgorithm);
        return new SecretKeySpec(factory.generateSecret(new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength)).getEncoded(), keyAlgorithm); //thanks alot for the bug report
    }
    
    private static byte() generateSalt()
    {
        final Random r = new SecureRandom();
        byte() salt = new byte(32);
        r.nextBytes(salt);

        return salt;
    }
    
    

}

Now my question is:

  • Does it have any security flaws?
  • Does it follow best practices?
  • Can I do something to improve it?

All the lengths that I have taken for SALT, IV, Authentication tag are they OK? or they needs to be changed.