Skip to content

Commit b05bbeb

Browse files
committed
restructure key encryption/decryption functions
1 parent aea1d5f commit b05bbeb

File tree

5 files changed

+52
-139
lines changed

5 files changed

+52
-139
lines changed

airavata-api/src/main/java/org/apache/airavata/common/utils/KeyStorePasswordCallback.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public interface KeyStorePasswordCallback {
5555
* Instead of the actual file.
5656
* @return The password to open the keystore.
5757
*/
58-
char[] getStorePassword() throws RuntimeException;
58+
char[] getStorePassword();
5959

6060
/**
6161
* Caller should implement the interface. Should return the pass phrase for

airavata-api/src/main/java/org/apache/airavata/common/utils/SecurityUtil.java

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
package org.apache.airavata.common.utils;
2121

2222
import java.io.*;
23+
import java.nio.ByteBuffer;
2324
import java.security.*;
2425
import java.security.cert.CertificateException;
26+
import java.util.Arrays;
2527
import javax.crypto.Cipher;
26-
import javax.crypto.spec.IvParameterSpec;
28+
import javax.crypto.spec.GCMParameterSpec;
2729
import org.slf4j.Logger;
2830
import org.slf4j.LoggerFactory;
2931

@@ -34,54 +36,19 @@ public class SecurityUtil {
3436

3537
public static final String PASSWORD_HASH_METHOD_PLAINTEXT = "PLAINTEXT";
3638
public static final String CHARSET_ENCODING = "UTF-8";
37-
public static final String PADDING_MECHANISM = "AES/CBC/PKCS5Padding";
39+
public static final String CIPHER_NAME = "AES/GCM/NoPadding";
40+
public static final int GCM_IV_BYTES = 12; // 96 bits
41+
public static final int GCM_TAG_BITS = 128;
3842

3943
private static final Logger logger = LoggerFactory.getLogger(SecurityUtil.class);
4044

41-
public static byte[] encryptString(
42-
String keyStorePath, String keyAlias, KeyStorePasswordCallback passwordCallback, String value)
43-
throws GeneralSecurityException, IOException {
44-
return encrypt(keyStorePath, keyAlias, passwordCallback, value.getBytes(CHARSET_ENCODING));
45-
}
46-
47-
public static byte[] encrypt(
48-
String keyStorePath, String keyAlias, KeyStorePasswordCallback passwordCallback, byte[] value)
49-
throws GeneralSecurityException, IOException {
50-
51-
Key secretKey = getSymmetricKey(keyStorePath, keyAlias, passwordCallback);
52-
53-
Cipher cipher = Cipher.getInstance(PADDING_MECHANISM);
54-
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
55-
return cipher.doFinal(value);
56-
}
57-
58-
private static Key getSymmetricKey(String keyStorePath, String keyAlias, KeyStorePasswordCallback passwordCallback)
45+
public static Key getSymmetricKey(String keyStorePath, String keyAlias, KeyStorePasswordCallback passwordCallback)
5946
throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException,
6047
UnrecoverableKeyException {
6148
KeyStore ks = SecurityUtil.loadKeyStore(keyStorePath, passwordCallback);
6249
return ks.getKey(keyAlias, passwordCallback.getSecretKeyPassPhrase(keyAlias));
6350
}
6451

65-
public static byte[] decrypt(
66-
String keyStorePath, String keyAlias, KeyStorePasswordCallback passwordCallback, byte[] encrypted)
67-
throws GeneralSecurityException, IOException {
68-
69-
Key secretKey = getSymmetricKey(keyStorePath, keyAlias, passwordCallback);
70-
71-
Cipher cipher = Cipher.getInstance(PADDING_MECHANISM);
72-
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
73-
74-
return cipher.doFinal(encrypted);
75-
}
76-
77-
public static String decryptString(
78-
String keyStorePath, String keyAlias, KeyStorePasswordCallback passwordCallback, byte[] encrypted)
79-
throws GeneralSecurityException, IOException {
80-
81-
byte[] decrypted = decrypt(keyStorePath, keyAlias, passwordCallback, encrypted);
82-
return new String(decrypted, CHARSET_ENCODING);
83-
}
84-
8552
public static KeyStore loadKeyStore(String keyStoreFilePath, KeyStorePasswordCallback passwordCallback)
8653
throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
8754

@@ -93,4 +60,32 @@ public static KeyStore loadKeyStore(String keyStoreFilePath, KeyStorePasswordCal
9360
}
9461
return KeyStore.getInstance(keystoreFile, passwordCallback.getStorePassword());
9562
}
63+
64+
public static byte[] encrypt(byte[] data, Key key) throws GeneralSecurityException {
65+
// Initialize the cipher
66+
var cipher = Cipher.getInstance(SecurityUtil.CIPHER_NAME);
67+
cipher.init(Cipher.ENCRYPT_MODE, key);
68+
var iv = cipher.getIV();
69+
// Encrypt the data
70+
var encryptedData = cipher.doFinal(data);
71+
// Construct tag [iv,encryptedData]
72+
var tag = ByteBuffer.allocate(iv.length + encryptedData.length)
73+
.put(iv)
74+
.put(encryptedData)
75+
.array();
76+
return tag;
77+
}
78+
79+
public static byte[] decrypt(byte[] tag, Key key) throws GeneralSecurityException {
80+
// Deconstruct tag [iv,encryptedData]
81+
var iv = Arrays.copyOfRange(tag, 0, GCM_IV_BYTES);
82+
var encryptedData = Arrays.copyOfRange(tag, GCM_IV_BYTES, tag.length);
83+
// Initialize the cipher
84+
var cipher = Cipher.getInstance(SecurityUtil.CIPHER_NAME);
85+
var spec = new GCMParameterSpec(GCM_TAG_BITS, iv);
86+
cipher.init(Cipher.DECRYPT_MODE, key, spec);
87+
// Decrypt the data
88+
var data = cipher.doFinal(encryptedData);
89+
return data;
90+
}
9691
}

airavata-api/src/main/java/org/apache/airavata/credential/store/utils/CredentialSerializationUtils.java

Lines changed: 8 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@
2020
package org.apache.airavata.credential.store.utils;
2121

2222
import java.io.*;
23-
import java.security.KeyStore;
2423
import java.security.SecureRandom;
2524
import java.util.Base64;
26-
import javax.crypto.Cipher;
2725
import javax.crypto.KeyGenerator;
2826
import javax.crypto.SecretKey;
2927
import javax.crypto.spec.SecretKeySpec;
3028
import org.apache.airavata.common.exception.ApplicationSettingsException;
3129
import org.apache.airavata.common.utils.ApplicationSettings;
3230
import org.apache.airavata.common.utils.DefaultKeyStorePasswordCallback;
31+
import org.apache.airavata.common.utils.KeyStorePasswordCallback;
32+
import org.apache.airavata.common.utils.SecurityUtil;
3333
import org.apache.airavata.credential.store.credential.Credential;
3434
import org.apache.airavata.credential.store.store.CredentialStoreException;
3535
import org.slf4j.Logger;
@@ -102,30 +102,9 @@ public static Credential deserializeCredential(byte[] serializedCredential) thro
102102
*/
103103
public static byte[] serializeCredentialWithEncryption(Credential credential) throws CredentialStoreException {
104104
try {
105-
// First serialize the credential
106105
byte[] serializedCredential = serializeCredential(credential);
107-
108-
// Get the secret key from keystore
109106
SecretKey secretKey = getSecretKeyFromKeyStore();
110-
111-
// Use AES/CBC/PKCS5Padding to ensure IV is used and generated
112-
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
113-
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
114-
byte[] encryptedData = cipher.doFinal(serializedCredential);
115-
116-
// Get the IV (should not be null)
117-
byte[] iv = cipher.getIV();
118-
if (iv == null) {
119-
throw new CredentialStoreException("IV is null after cipher initialization");
120-
}
121-
122-
// Combine IV and encrypted data
123-
byte[] combined = new byte[iv.length + encryptedData.length];
124-
System.arraycopy(iv, 0, combined, 0, iv.length);
125-
System.arraycopy(encryptedData, 0, combined, iv.length, encryptedData.length);
126-
127-
return combined;
128-
107+
return SecurityUtil.encrypt(serializedCredential, secretKey);
129108
} catch (Exception e) {
130109
logger.error("Error encrypting credential", e);
131110
throw new CredentialStoreException("Error encrypting credential", e);
@@ -141,21 +120,8 @@ public static byte[] serializeCredentialWithEncryption(Credential credential) th
141120
public static Credential deserializeCredentialWithDecryption(byte[] encryptedCredential)
142121
throws CredentialStoreException {
143122
try {
144-
// Get the secret key from keystore
145123
SecretKey secretKey = getSecretKeyFromKeyStore();
146-
147-
// Extract IV and encrypted data
148-
byte[] iv = new byte[16]; // AES IV size
149-
byte[] encryptedData = new byte[encryptedCredential.length - 16];
150-
System.arraycopy(encryptedCredential, 0, iv, 0, 16);
151-
System.arraycopy(encryptedCredential, 16, encryptedData, 0, encryptedData.length);
152-
153-
// Decrypt the data
154-
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
155-
cipher.init(Cipher.DECRYPT_MODE, secretKey, new javax.crypto.spec.IvParameterSpec(iv));
156-
byte[] decryptedData = cipher.doFinal(encryptedData);
157-
158-
// Deserialize the decrypted data
124+
byte[] decryptedData = SecurityUtil.decrypt(encryptedCredential, secretKey);
159125
return deserializeCredential(decryptedData);
160126

161127
} catch (Exception e) {
@@ -177,25 +143,9 @@ public static byte[] serializeCredentialWithEncryption(
177143
Credential credential, String keystorePath, String keyAlias, KeyStorePasswordCallback passwordCallback)
178144
throws CredentialStoreException {
179145
try {
180-
// First serialize the credential
181146
byte[] serializedCredential = serializeCredential(credential);
182-
183-
// Get the secret key from custom keystore
184147
SecretKey secretKey = getSecretKeyFromCustomKeyStore(keystorePath, keyAlias, passwordCallback);
185-
186-
// Encrypt the serialized data
187-
Cipher cipher = Cipher.getInstance("AES");
188-
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
189-
byte[] encryptedData = cipher.doFinal(serializedCredential);
190-
191-
// Combine IV and encrypted data
192-
byte[] iv = cipher.getIV();
193-
byte[] combined = new byte[iv.length + encryptedData.length];
194-
System.arraycopy(iv, 0, combined, 0, iv.length);
195-
System.arraycopy(encryptedData, 0, combined, iv.length, encryptedData.length);
196-
197-
return combined;
198-
148+
return SecurityUtil.encrypt(serializedCredential, secretKey);
199149
} catch (Exception e) {
200150
logger.error("Error encrypting credential with custom keystore", e);
201151
throw new CredentialStoreException("Error encrypting credential with custom keystore", e);
@@ -215,23 +165,9 @@ public static Credential deserializeCredentialWithDecryption(
215165
byte[] encryptedCredential, String keystorePath, String keyAlias, KeyStorePasswordCallback passwordCallback)
216166
throws CredentialStoreException {
217167
try {
218-
// Get the secret key from custom keystore
219168
SecretKey secretKey = getSecretKeyFromCustomKeyStore(keystorePath, keyAlias, passwordCallback);
220-
221-
// Extract IV and encrypted data
222-
byte[] iv = new byte[16]; // AES IV size
223-
byte[] encryptedData = new byte[encryptedCredential.length - 16];
224-
System.arraycopy(encryptedCredential, 0, iv, 0, 16);
225-
System.arraycopy(encryptedCredential, 16, encryptedData, 0, encryptedData.length);
226-
227-
// Decrypt the data
228-
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
229-
cipher.init(Cipher.DECRYPT_MODE, secretKey, new javax.crypto.spec.IvParameterSpec(iv));
230-
byte[] decryptedData = cipher.doFinal(encryptedData);
231-
232-
// Deserialize the decrypted data
169+
byte[] decryptedData = SecurityUtil.decrypt(encryptedCredential, secretKey);
233170
return deserializeCredential(decryptedData);
234-
235171
} catch (Exception e) {
236172
logger.error("Error decrypting credential with custom keystore", e);
237173
throw new CredentialStoreException("Error decrypting credential with custom keystore", e);
@@ -244,12 +180,7 @@ public static Credential deserializeCredentialWithDecryption(
244180
* @throws Exception if key retrieval fails
245181
*/
246182
private static SecretKey getSecretKeyFromKeyStore() throws Exception {
247-
KeyStore keyStore = KeyStore.getInstance("JKS");
248-
try (FileInputStream fis = new FileInputStream(KEYSTORE_PATH)) {
249-
keyStore.load(fis, KEYSTORE_PASSWORD_CALLBACK.getStorePassword());
250-
}
251-
252-
return (SecretKey) keyStore.getKey(KEY_ALIAS, KEYSTORE_PASSWORD_CALLBACK.getSecretKeyPassPhrase(KEY_ALIAS));
183+
return (SecretKey) SecurityUtil.getSymmetricKey(KEYSTORE_PATH, KEY_ALIAS, KEYSTORE_PASSWORD_CALLBACK);
253184
}
254185

255186
/**
@@ -262,12 +193,7 @@ private static SecretKey getSecretKeyFromKeyStore() throws Exception {
262193
*/
263194
private static SecretKey getSecretKeyFromCustomKeyStore(
264195
String keystorePath, String keyAlias, KeyStorePasswordCallback passwordCallback) throws Exception {
265-
KeyStore keyStore = KeyStore.getInstance("JKS");
266-
try (FileInputStream fis = new FileInputStream(keystorePath)) {
267-
keyStore.load(fis, passwordCallback.getStorePassword());
268-
}
269-
270-
return (SecretKey) keyStore.getKey(keyAlias, passwordCallback.getSecretKeyPassPhrase(keyAlias));
196+
return (SecretKey) SecurityUtil.getSymmetricKey(keystorePath, keyAlias, passwordCallback);
271197
}
272198

273199
/**
@@ -299,13 +225,4 @@ public static SecretKey stringToSecretKey(String keyString) {
299225
byte[] keyBytes = Base64.getDecoder().decode(keyString);
300226
return new SecretKeySpec(keyBytes, "AES");
301227
}
302-
303-
/**
304-
* Interface for keystore password callbacks.
305-
*/
306-
public interface KeyStorePasswordCallback {
307-
char[] getStorePassword();
308-
309-
char[] getSecretKeyPassPhrase(String keyAlias);
310-
}
311228
}

airavata-api/src/test/java/org/apache/airavata/common/utils/SecurityUtilTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,19 @@ public class SecurityUtilTest {
3838
public void testEncryptString() throws Exception {
3939

4040
String stringToEncrypt = "Test string to encrypt";
41-
byte[] encrypted =
42-
SecurityUtil.encryptString(keyStorePath, "mykey", new TestKeyStoreCallback(), stringToEncrypt);
43-
44-
String decrypted = SecurityUtil.decryptString(keyStorePath, "mykey", new TestKeyStoreCallback(), encrypted);
41+
var key = SecurityUtil.getSymmetricKey(keyStorePath, "mykey", new TestKeyStoreCallback());
42+
byte[] encrypted = SecurityUtil.encrypt(stringToEncrypt.getBytes(StandardCharsets.UTF_8), key);
43+
String decrypted = new String(SecurityUtil.decrypt(encrypted, key));
4544
assertEquals(stringToEncrypt, decrypted);
4645
}
4746

4847
@Test
4948
public void testEncryptBytes() throws Exception {
5049
String stringToEncrypt = "Test string to encrypt";
5150
byte[] plaintext = stringToEncrypt.getBytes(StandardCharsets.UTF_8);
52-
byte[] encrypted = SecurityUtil.encrypt(keyStorePath, "mykey", new TestKeyStoreCallback(), plaintext);
53-
byte[] decrypted = SecurityUtil.decrypt(keyStorePath, "mykey", new TestKeyStoreCallback(), encrypted);
51+
var key = SecurityUtil.getSymmetricKey(keyStorePath, "mykey", new TestKeyStoreCallback());
52+
byte[] encrypted = SecurityUtil.encrypt(plaintext, key);
53+
byte[] decrypted = SecurityUtil.decrypt(encrypted, key);
5454
assertEquals(plaintext, decrypted);
5555
}
5656

airavata-api/src/test/java/org/apache/airavata/credential/store/store/impl/db/CredentialsDAOTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.apache.airavata.common.utils.DBUtil;
3636
import org.apache.airavata.common.utils.DatabaseTestCases;
3737
import org.apache.airavata.common.utils.DerbyUtil;
38+
import org.apache.airavata.common.utils.KeyStorePasswordCallback;
3839
import org.apache.airavata.credential.store.credential.CommunityUser;
3940
import org.apache.airavata.credential.store.credential.CredentialOwnerType;
4041
import org.apache.airavata.credential.store.credential.impl.certificate.CertificateCredential;
@@ -320,7 +321,7 @@ public void testSerializationWithEncryption() throws CredentialStoreException, U
320321
assertTrue(Arrays.equals(privateKey.getEncoded(), newKey.getEncoded()));
321322
}
322323

323-
private class TestACSKeyStoreCallback implements CredentialSerializationUtils.KeyStorePasswordCallback {
324+
private class TestACSKeyStoreCallback implements KeyStorePasswordCallback {
324325

325326
@Override
326327
public char[] getStorePassword() {

0 commit comments

Comments
 (0)