This is an automated email from the ASF dual-hosted git repository.

yasith pushed a commit to branch feat/aes-gcm-encryption
in repository https://gitbox.apache.org/repos/asf/airavata.git

commit f7a364faea2b775395ffc5f89cdc7f66a3ede4c6
Author: yasithdev <[email protected]>
AuthorDate: Mon Mar 30 16:12:41 2026 -0400

    security: upgrade credential encryption from AES/CBC to AES/GCM
    
    Replace AES/CBC/PKCS5Padding with AES/GCM/NoPadding for credential
    store encryption. The old code used a static zero IV which is insecure.
    GCM provides authenticated encryption with random IVs prepended to
    the ciphertext.
    
    Extracted from #556.
---
 .../credential/repository/db/CredentialsDAO.java   | 10 ++--
 .../airavata/security/util/SecurityUtil.java       | 59 +++++++++-------------
 .../airavata/security/util/SecurityUtilTest.java   | 13 +++--
 3 files changed, 35 insertions(+), 47 deletions(-)

diff --git 
a/airavata-api/src/main/java/org/apache/airavata/credential/repository/db/CredentialsDAO.java
 
b/airavata-api/src/main/java/org/apache/airavata/credential/repository/db/CredentialsDAO.java
index 44f050c384..1757d03cbe 100644
--- 
a/airavata-api/src/main/java/org/apache/airavata/credential/repository/db/CredentialsDAO.java
+++ 
b/airavata-api/src/main/java/org/apache/airavata/credential/repository/db/CredentialsDAO.java
@@ -414,8 +414,9 @@ public class CredentialsDAO extends ParentDAO {
             try {
                 // decrypt the data first
                 if (encrypt()) {
-                    data = SecurityUtil.decrypt(
-                            this.keyStorePath, this.secretKeyAlias, 
this.keyStorePasswordCallback, data);
+                    var key = SecurityUtil.getSymmetricKey(
+                            this.keyStorePath, this.secretKeyAlias, 
this.keyStorePasswordCallback);
+                    data = SecurityUtil.decrypt(data, key);
                 }
 
                 objectInputStream = new ObjectInputStream(new 
ByteArrayInputStream(data));
@@ -464,8 +465,9 @@ public class CredentialsDAO extends ParentDAO {
         if (encrypt()) {
             byte[] array = byteArrayOutputStream.toByteArray();
             try {
-                return SecurityUtil.encrypt(
-                        this.keyStorePath, this.secretKeyAlias, 
this.keyStorePasswordCallback, array);
+                var key = SecurityUtil.getSymmetricKey(
+                        this.keyStorePath, this.secretKeyAlias, 
this.keyStorePasswordCallback);
+                return SecurityUtil.encrypt(array, key);
             } catch (GeneralSecurityException e) {
                 throw new CredentialStoreException("Error encrypting data", e);
             } catch (IOException e) {
diff --git 
a/airavata-api/src/main/java/org/apache/airavata/security/util/SecurityUtil.java
 
b/airavata-api/src/main/java/org/apache/airavata/security/util/SecurityUtil.java
index 5aa1049d0f..e3f2088d4b 100644
--- 
a/airavata-api/src/main/java/org/apache/airavata/security/util/SecurityUtil.java
+++ 
b/airavata-api/src/main/java/org/apache/airavata/security/util/SecurityUtil.java
@@ -20,10 +20,12 @@
 package org.apache.airavata.security.util;
 
 import java.io.*;
+import java.nio.ByteBuffer;
 import java.security.*;
 import java.security.cert.CertificateException;
+import java.util.Arrays;
 import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.GCMParameterSpec;
 import org.apache.airavata.common.server.KeyStorePasswordCallback;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,52 +37,37 @@ public class SecurityUtil {
 
     public static final String PASSWORD_HASH_METHOD_PLAINTEXT = "PLAINTEXT";
     public static final String CHARSET_ENCODING = "UTF-8";
-    public static final String PADDING_MECHANISM = "AES/CBC/PKCS5Padding";
+    public static final String CIPHER_NAME = "AES/GCM/NoPadding";
+    public static final int GCM_IV_BYTES = 12; // 96 bits
+    public static final int GCM_TAG_BITS = 128;
 
     private static final Logger logger = 
LoggerFactory.getLogger(SecurityUtil.class);
 
-    public static byte[] encryptString(
-            String keyStorePath, String keyAlias, KeyStorePasswordCallback 
passwordCallback, String value)
-            throws GeneralSecurityException, IOException {
-        return encrypt(keyStorePath, keyAlias, passwordCallback, 
value.getBytes(CHARSET_ENCODING));
-    }
-
-    public static byte[] encrypt(
-            String keyStorePath, String keyAlias, KeyStorePasswordCallback 
passwordCallback, byte[] value)
-            throws GeneralSecurityException, IOException {
-
-        Key secretKey = getSymmetricKey(keyStorePath, keyAlias, 
passwordCallback);
-
-        Cipher cipher = Cipher.getInstance(PADDING_MECHANISM);
-        cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new 
byte[16]));
-        return cipher.doFinal(value);
-    }
-
-    private static Key getSymmetricKey(String keyStorePath, String keyAlias, 
KeyStorePasswordCallback passwordCallback)
+    public static Key getSymmetricKey(String keyStorePath, String keyAlias, 
KeyStorePasswordCallback passwordCallback)
             throws CertificateException, NoSuchAlgorithmException, 
KeyStoreException, IOException,
                     UnrecoverableKeyException {
         KeyStore ks = SecurityUtil.loadKeyStore(keyStorePath, 
passwordCallback);
         return ks.getKey(keyAlias, 
passwordCallback.getSecretKeyPassPhrase(keyAlias));
     }
 
-    public static byte[] decrypt(
-            String keyStorePath, String keyAlias, KeyStorePasswordCallback 
passwordCallback, byte[] encrypted)
-            throws GeneralSecurityException, IOException {
-
-        Key secretKey = getSymmetricKey(keyStorePath, keyAlias, 
passwordCallback);
-
-        Cipher cipher = Cipher.getInstance(PADDING_MECHANISM);
-        cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new 
byte[16]));
-
-        return cipher.doFinal(encrypted);
+    public static byte[] encrypt(byte[] data, Key key) throws 
GeneralSecurityException {
+        var cipher = Cipher.getInstance(CIPHER_NAME);
+        cipher.init(Cipher.ENCRYPT_MODE, key);
+        var iv = cipher.getIV();
+        var encryptedData = cipher.doFinal(data);
+        return ByteBuffer.allocate(iv.length + encryptedData.length)
+                .put(iv)
+                .put(encryptedData)
+                .array();
     }
 
-    public static String decryptString(
-            String keyStorePath, String keyAlias, KeyStorePasswordCallback 
passwordCallback, byte[] encrypted)
-            throws GeneralSecurityException, IOException {
-
-        byte[] decrypted = decrypt(keyStorePath, keyAlias, passwordCallback, 
encrypted);
-        return new String(decrypted, CHARSET_ENCODING);
+    public static byte[] decrypt(byte[] tag, Key key) throws 
GeneralSecurityException {
+        var iv = Arrays.copyOfRange(tag, 0, GCM_IV_BYTES);
+        var encryptedData = Arrays.copyOfRange(tag, GCM_IV_BYTES, tag.length);
+        var cipher = Cipher.getInstance(CIPHER_NAME);
+        var spec = new GCMParameterSpec(GCM_TAG_BITS, iv);
+        cipher.init(Cipher.DECRYPT_MODE, key, spec);
+        return cipher.doFinal(encryptedData);
     }
 
     public static KeyStore loadKeyStore(String keyStoreFilePath, 
KeyStorePasswordCallback passwordCallback)
diff --git 
a/airavata-api/src/test/java/org/apache/airavata/security/util/SecurityUtilTest.java
 
b/airavata-api/src/test/java/org/apache/airavata/security/util/SecurityUtilTest.java
index a8ae9d48ff..e066198a9b 100644
--- 
a/airavata-api/src/test/java/org/apache/airavata/security/util/SecurityUtilTest.java
+++ 
b/airavata-api/src/test/java/org/apache/airavata/security/util/SecurityUtilTest.java
@@ -39,12 +39,10 @@ public class SecurityUtilTest {
 
     @Test
     public void testEncryptString() throws Exception {
-
         String stringToEncrypt = "Test string to encrypt";
-        byte[] encrypted =
-                SecurityUtil.encryptString(keyStorePath, "mykey", new 
TestKeyStoreCallback(), stringToEncrypt);
-
-        String decrypted = SecurityUtil.decryptString(keyStorePath, "mykey", 
new TestKeyStoreCallback(), encrypted);
+        var key = SecurityUtil.getSymmetricKey(keyStorePath, "mykey", new 
TestKeyStoreCallback());
+        byte[] encrypted = 
SecurityUtil.encrypt(stringToEncrypt.getBytes(StandardCharsets.UTF_8), key);
+        String decrypted = new String(SecurityUtil.decrypt(encrypted, key), 
StandardCharsets.UTF_8);
         assertEquals(stringToEncrypt, decrypted);
     }
 
@@ -52,8 +50,9 @@ public class SecurityUtilTest {
     public void testEncryptBytes() throws Exception {
         String stringToEncrypt = "Test string to encrypt";
         byte[] plaintext = stringToEncrypt.getBytes(StandardCharsets.UTF_8);
-        byte[] encrypted = SecurityUtil.encrypt(keyStorePath, "mykey", new 
TestKeyStoreCallback(), plaintext);
-        byte[] decrypted = SecurityUtil.decrypt(keyStorePath, "mykey", new 
TestKeyStoreCallback(), encrypted);
+        var key = SecurityUtil.getSymmetricKey(keyStorePath, "mykey", new 
TestKeyStoreCallback());
+        byte[] encrypted = SecurityUtil.encrypt(plaintext, key);
+        byte[] decrypted = SecurityUtil.decrypt(encrypted, key);
         assertArrayEquals(plaintext, decrypted);
     }
 

Reply via email to