Skip to content

Commit 1ce4c46

Browse files
committed
Refactor CryptoUtils
1 parent 1469a43 commit 1ce4c46

File tree

2 files changed

+87
-60
lines changed

2 files changed

+87
-60
lines changed

Diff for: org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/authentication/CryptoUtils.java

+71-39
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import java.nio.CharBuffer;
1010
import java.nio.charset.Charset;
1111
import java.security.GeneralSecurityException;
12-
import java.security.Key;
1312
import java.security.NoSuchAlgorithmException;
1413
import java.security.Provider;
1514
import java.security.SecureRandom;
@@ -129,17 +128,8 @@ public static SecretKey generateRandomSecretKey() throws NoSuchAlgorithmExceptio
129128

130129
/**
131130
* Generate a SecretKey from a password using a PBE based algorithm
132-
*
133-
* Algorithms supported:
134-
*
135-
* PBEwithHmacSHA256AndAES_256
136-
* PBEWithHmacSHA1AndAES_256
137-
* PBEWithHmacSHA512AndAES_128
138-
* PBEWithHmacSHA1AndAES_128
139-
* PBEWithHmacSHA384AndAES_256
140-
* PBEWithMD5AndDES
141-
*/
142-
public static SecretKey generateKeyFromPassword(String algorithm, char[] password) throws GeneralSecurityException {
131+
*/
132+
public static SecretKey generateKeyFromPassword(char[] password, String algorithm) throws GeneralSecurityException {
143133
// Convert the password bytes to Base64 characters because PBEKey class will not accept non-Ascii characters in a password
144134
char[] encodedPassword = encodeCharsToBase64(password);
145135

@@ -150,17 +140,9 @@ public static SecretKey generateKeyFromPassword(String algorithm, char[] passwor
150140
}
151141

152142
/**
153-
* Generate a SecretKey from a password using a PBK based algorithm with salt and iterations
154-
*
155-
* Algorithms supported:
156-
*
157-
* PBKDF2WithHmacSHA1
158-
* PBKDF2WithHmacSHA224
159-
* PBKDF2WithHmacSHA256
160-
* PBKDF2WithHmacSHA384
161-
* PBKDF2WithHmacSHA512
143+
* Generate a SecretKey from a password using a PBK based algorithm
162144
*/
163-
public static SecretKey generateKeyFromPassword(String algorithm, char[] password, byte[] salt, int iterations) throws Exception {
145+
public static SecretKey generateKeyFromPassword(char[] password, String algorithm, byte[] salt, int iterations) throws GeneralSecurityException {
164146
// Convert the password bytes to Base64 characters because PBEKey class will not accept non-Ascii characters in a password
165147
char[] encodedPassword = encodeCharsToBase64(password);
166148

@@ -169,44 +151,94 @@ public static SecretKey generateKeyFromPassword(String algorithm, char[] passwor
169151
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
170152
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
171153

154+
// Wrap the pbeKey in a SecretKeySpec
172155
return new SecretKeySpec(pbeKey.getEncoded(), "AES");
173156
}
174157

175158
/**
176-
* Get and initialise Cipher based on PBE key from generateKeyFromPassword
159+
* Encrypt/ Decrypt bytes with a password using a PBE based algorithm
177160
*
178-
* @param key The secret PBE key generated from calling generateKeyFromPassword
179-
* @param algorithm The PBE transformation algorithm to use
161+
* @param password Password
162+
* @param pbeAlgorithm The PBE algorithm to use
180163
* @param mode Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE
181-
* @param salt Salt needed for PBEParameterSpec
182-
* @param iv Optional IV - some PBE algorithms don't use one
183-
* @param iterations
164+
* @param bytes The bytes to encrypt/decypt
165+
* @param salt Salt (required)
166+
* @param iv IV (optional)
167+
* @param iterations Iterations for the PBEParameterSpec
168+
* @return The encrypted or decrypted bytes
184169
*/
185-
public static Cipher getPBECipher(Key key, String algorithm, int mode, byte[] salt, byte[] iv, int iterations) throws GeneralSecurityException {
186-
Cipher cipher = Cipher.getInstance(algorithm);
170+
public static byte[] transformWithPassword(char[] password, String pbeAlgorithm, int mode, byte[] bytes, byte[] salt, byte[] iv,
171+
int iterations) throws GeneralSecurityException {
172+
// Generate a PBE key for the Cipher
173+
SecretKey secretKey = generateKeyFromPassword(password, pbeAlgorithm);
174+
175+
// Encrypt the input with the Cipher
176+
Cipher keyCipher = Cipher.getInstance(pbeAlgorithm);
187177

188-
// PBEParameterSpec with salt and (optional) iv spec
178+
// IvParameterSpec is optional depending on the algorithm
189179
IvParameterSpec paramSpec = iv != null ? new IvParameterSpec(iv) : null;
190180

191-
// Create parameters from the salt and an arbitrary number of iterations (paramSpec will be null if iv is null)
181+
// Create parameters from the salt and the iterations (paramSpec will be null if iv is null)
192182
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, iterations, paramSpec);
193-
183+
194184
// Set the cipher mode to decryption or encryption
195-
cipher.init(mode, key, pbeParamSpec);
185+
keyCipher.init(mode, secretKey, pbeParamSpec);
186+
187+
// Encypt/Decrypt the bytes
188+
return keyCipher.doFinal(bytes);
189+
}
196190

197-
return cipher;
191+
/**
192+
* Encrypt/ Decrypt bytes with a password using a PBK based algorithm
193+
*
194+
* @param password Password
195+
* @param pbkAlgorithm The PBK algorithm to use
196+
* @param cipherAlgorithm The Cipher algorithm to use - AES or AES/CBC/PKCS5Padding or AES/GCM/NoPadding
197+
* @param mode Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE
198+
* @param bytes The bytes to encrypt/decypt
199+
* @param salt Salt (required)
200+
* @param iv IV (required)
201+
* @param iterations Iterations for the PBEKeySpec
202+
* @return The encrypted or decrypted bytes
203+
*/
204+
public static byte[] transformWithPassword2(char[] password, String pbkAlgorithm, String cipherAlgorithm, int mode, byte[] bytes, byte[] salt, byte[] iv,
205+
int iterations) throws GeneralSecurityException {
206+
// Generate a key with the PBK Algorithm
207+
SecretKey secretKey = generateKeyFromPassword(password, pbkAlgorithm, salt, iterations);
208+
209+
// Get the cipher from the key and cipher algorithm
210+
Cipher keyCipher = getCipher(secretKey, cipherAlgorithm, mode, iv);
211+
212+
// Encypt/Decrypt the bytes
213+
return keyCipher.doFinal(bytes);
214+
}
215+
216+
/**
217+
* Encrypt/ Decrypt bytes with a SecretKey
218+
*
219+
* @param key The secret key to use for the Cipher
220+
* @param cipherAlgorithm The Cipher algorithm to use - AES or AES/CBC/PKCS5Padding or AES/GCM/NoPadding
221+
* @param mode Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE
222+
* @param bytes The bytes to encrypt/decypt
223+
* @param iv IV (optional)
224+
* @return The encrypted or decrypted bytes
225+
* @throws GeneralSecurityException
226+
*/
227+
public static byte[] transformWithKey(SecretKey key, String cipherAlgorithm, int mode, byte[] bytes, byte[] iv) throws GeneralSecurityException {
228+
Cipher cipher = getCipher(key, cipherAlgorithm, mode, iv);
229+
return cipher.doFinal(bytes);
198230
}
199231

200232
/**
201233
* Get and initialise Cipher with a secret key and optional iv
202-
* param @iv is optional and not used for AES
234+
* param @iv is optional and not used for AES, AES/CBC/PKCS5Padding or AES/GCM/NoPadding
203235
*
204-
* @param key The scret key
205-
* @param algorithm The transformation algorithm to use
236+
* @param key The secret key
237+
* @param algorithm The transformation algorithm to use - AES,
206238
* @param mode Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE
207239
* @param iv Optional IV
208240
*/
209-
public static Cipher getCipher(Key key, String algorithm, int mode, byte... iv) throws GeneralSecurityException {
241+
public static Cipher getCipher(SecretKey key, String algorithm, int mode, byte... iv) throws GeneralSecurityException {
210242
Cipher cipher = Cipher.getInstance(algorithm);
211243

212244
switch(algorithm) {

Diff for: org.archicontribs.modelrepository/src/org/archicontribs/modelrepository/authentication/internal/EncryptedCredentialsStorage.java

+16-21
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,7 @@ public boolean storePassword(char[] password) throws GeneralSecurityException, I
129129
byte[] passwordBytes = CryptoUtils.convertCharsToBytes(password);
130130

131131
// Encrypt the password
132-
Cipher cipher = CryptoUtils.getCipher(key, CIPHER_ALGORITHM, Cipher.ENCRYPT_MODE);
133-
byte[] encrypted = cipher.doFinal(passwordBytes);
132+
byte[] encrypted = CryptoUtils.transformWithKey(key, CIPHER_ALGORITHM, Cipher.ENCRYPT_MODE, passwordBytes, null);
134133

135134
// Store in properties file as a Base64 encoded string
136135
getProperties().setProperty(PASSWORD, Base64.getEncoder().encodeToString(encrypted)); // Use Base64 because this is a string
@@ -167,8 +166,7 @@ public char[] getPassword() throws IOException, GeneralSecurityException {
167166
}
168167

169168
// Decrypt the password
170-
Cipher cipher = CryptoUtils.getCipher(key, CIPHER_ALGORITHM, Cipher.DECRYPT_MODE);
171-
passwordBytes = cipher.doFinal(passwordBytes);
169+
passwordBytes = CryptoUtils.transformWithKey(key, CIPHER_ALGORITHM, Cipher.DECRYPT_MODE, passwordBytes, null);
172170

173171
// Use UTF-8 because we used that to encrypt it
174172
return CryptoUtils.convertBytesToChars(passwordBytes);
@@ -340,8 +338,13 @@ private static SecretKey loadPrimaryKey(char[] password) throws GeneralSecurityE
340338
byte[] keybytes = Arrays.copyOfRange(bytes, PBE_SALT_LENGTH, bytes.length);
341339

342340
// Decrypt the key bytes with the password and salt
343-
Cipher cipher = getCipherWithPassword(password, Cipher.DECRYPT_MODE, salt);
344-
keybytes = cipher.doFinal(keybytes);
341+
keybytes = CryptoUtils.transformWithPassword(password,
342+
PBE_ALGORITHM,
343+
Cipher.DECRYPT_MODE,
344+
keybytes,
345+
salt,
346+
null, // no iv
347+
PBE_ITERATIONS);
345348

346349
// Return the key
347350
return new SecretKeySpec(keybytes, "AES");
@@ -359,8 +362,13 @@ private static void savePrimaryKey(SecretKey key, char[] password) throws Genera
359362
byte[] salt = CryptoUtils.generateRandomBytes(PBE_SALT_LENGTH);
360363

361364
// Encrypt the key
362-
Cipher cipher = getCipherWithPassword(password, Cipher.ENCRYPT_MODE, salt);
363-
byte[] keybytes = cipher.doFinal(key.getEncoded());
365+
byte[] keybytes = CryptoUtils.transformWithPassword(password,
366+
PBE_ALGORITHM,
367+
Cipher.ENCRYPT_MODE,
368+
key.getEncoded(),
369+
salt,
370+
null, // no iv
371+
PBE_ITERATIONS);
364372

365373
File primaryKeyFile = getPrimaryKeyFile();
366374

@@ -380,19 +388,6 @@ private static void savePrimaryKey(SecretKey key, char[] password) throws Genera
380388
}
381389
}
382390

383-
/**
384-
* Create a Cipher using a password rather than a key
385-
* The key is generated from the the password
386-
* See https://stackoverflow.com/questions/13673556/using-password-based-encryption-on-a-file-in-java
387-
*/
388-
private static Cipher getCipherWithPassword(char[] password, int mode, byte[] salt) throws GeneralSecurityException {
389-
// Generate the SecretKey from the password
390-
SecretKey key = CryptoUtils.generateKeyFromPassword(PBE_ALGORITHM, password);
391-
392-
// Return the Cipher using the key, salt, no iv, and iteration count
393-
return CryptoUtils.getPBECipher(key, PBE_ALGORITHM, mode, salt, null, PBE_ITERATIONS);
394-
}
395-
396391
private static char[] askUserForPrimaryPassword() {
397392
// Check that current thread is the UI thread in case this is called from a non-UI thread
398393
if(Display.getCurrent() != null) {

0 commit comments

Comments
 (0)