This is the tester:
public class CryptographySimpleTests extends ActivityTestCase
{
public void testsCryptographyClass_encryptAndDecrypt()
{
final String orgVal = "hi world! :D";
final String key = "key";
try
{
final byte[] encryptKey = Cryptography.deriveAES256Key(key);
final byte[] decryptKey = Cryptography.deriveAES256Key(key);
//Deviation method
Assert.assertTrue(Arrays.equals(encryptKey, decryptKey));
byte[] encrypted = Cryptography.encryptAES(encryptKey, orgVal.getBytes());
Assert.assertFalse(Arrays.equals(encrypted, orgVal.getBytes()));
byte[] decrypted = Cryptography.decryptAES(decryptKey, encrypted);
Assert.assertTrue(Arrays.equals(orgVal.getBytes(), decrypted));
}
catch (Exception e) {
Assert.fail(e.getMessage());
}
}
}
Which fails because of the last assert:
Assert.fail(e.getMessage());
When trying to execute:
byte[] decrypted = Cryptography.decryptAES(decryptKey, encrypted);
Gives this stack trace:
javax.crypto.BadPaddingException: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
at com.android.org.conscrypt.NativeCrypto.EVP_CipherFinal_ex(Native Method)
at com.android.org.conscrypt.OpenSSLCipher.doFinalInternal(OpenSSLCipher.java:430)
at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:466)
at javax.crypto.Cipher.doFinal(Cipher.java:1340)
at bdevel.encuentralo.utils.Cryptography.decryptAES(Cryptography.java:59)
at bdevel.encuentralo.CryptographySimpleTests.testsCryptographyClass_encryptAndDecrypt(CryptographySimpleTests.java:32)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
at junit.framework.TestCase.runBare(TestCase.java:134)
at junit.framework.TestResult$1.protect(TestResult.java:115)
at junit.framework.TestResult.runProtected(TestResult.java:133)
at junit.framework.TestResult.run(TestResult.java:118)
at junit.framework.TestCase.run(TestCase.java:124)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1837)
These are my functions:
public class Cryptography {
/**
* @param key AES Key
* @param inputValue Data to encrypt
* @return Can return null if something goes wrong
*/
public static byte[] encryptAES(byte[] key, byte[] inputValue)
throws NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException
{
SecretKeySpec sKeyS = new SecretKeySpec(key, "AES");
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, sKeyS);
}
catch (NoSuchAlgorithmException | InvalidKeyException i) {
cipher = null;
}
return cipher != null ? cipher.doFinal(inputValue) : null;
}
public static byte[] decryptAES(byte[] key, byte[] encryptedData)
throws NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException
{
SecretKeySpec sKeyS = new SecretKeySpec(key, "AES");
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, sKeyS);
}
catch (NoSuchAlgorithmException | InvalidKeyException i) {
cipher = null;
}
return cipher != null ? cipher.doFinal(encryptedData) : null;
}
private static byte[] deriveAES256KeySalt = null;
public static byte[] deriveAES256Key(String password)
throws InvalidKeySpecException, NoSuchAlgorithmException
{
/* Store these things on disk used to derive key later: */
int iterationCount = 1000;
int saltLength = 32; // bytes; should be the same size as the output (256 / 8 = 32)
int keyLength = 256; // 256-bits for AES-256, 128-bits for AES-128, etc
/* When first creating the key, obtain a salt with this: */
if(deriveAES256KeySalt == null) {
SecureRandom random = new SecureRandom();
deriveAES256KeySalt = new byte[saltLength];
random.nextBytes(deriveAES256KeySalt);
}
/* Use this to derive the key from the password: */
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), deriveAES256KeySalt, iterationCount, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
return keyBytes;
}
}
If the assert that checks if the keys are the same works, why do I get that exception?
You are eating an java.security.InvalidKeyException: Illegal key size or default parameters
exception in your encryptAES
and decryptAES
methods. So don't eat them, either declare as throws
or promote to RuntimeException
.
Turns out you have two problems, for this reason, you can't do 256, but 128 solves that, then you are also requesting CBC
without an IvParameterSpec
(Which is causing java.security.InvalidKeyException: Parameters missing
). So supply that or change to ECB
:
public static byte[] encryptAES(byte[] key, byte[] inputValue)
throws NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, InvalidKeyException {
SecretKeySpec sKeyS = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, sKeyS);
return cipher.doFinal(inputValue);
}
public static byte[] decryptAES(byte[] key, byte[] encryptedData)
throws NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, InvalidKeyException {
SecretKeySpec sKeyS = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, sKeyS);
return cipher.doFinal(encryptedData);
}
Key length:
public static byte[] deriveAES256Key(String password)
throws InvalidKeySpecException, NoSuchAlgorithmException {
...
int keyLength = 128; // 256-bits for AES-256, 128-bits for AES
...
So I got it working like that, but step one is to stop eating the exception and you'll get better clues and probably work out on own.