I am currently making an Android app that includes encrypting a String with AES. But for some reason my app does not decrypt properly. I tried to change the Base64 format but it does not fix it. The code is similar to the example on Android Encryption with the Android Cryptography API
Does anyone know where did I go wrong with my functions? Since it does not decode to the same string as my encoded string ("pls").
Your help is much appreciated.
byte[] a = encryptFIN128AES("pls");
String b = decryptFIN128AES(a);
Log.e("AES_Test", "b = " + b);
/**
* Encrypts a string with AES (128 bit key)
* #param fin
* #return the AES encrypted byte[]
*/
private byte[] encryptFIN128AES(String fin) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey("Test1".toCharArray(), "Test2".getBytes()).getEncoded(),"AES");
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES key generation error");
}
// Encode the original data with AES
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, sks);
encodedBytes = c.doFinal(fin.getBytes());
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES encryption error");
}
return encodedBytes;
}
/**
* Decrypts a string with AES (128 bit key)
* #param encodedBytes
* #return the decrypted String
*/
private String decryptFIN128AES(byte[] encodedBytes) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey("Test1".toCharArray(), "Test2".getBytes()).getEncoded(),"AES");
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES key generation error");
}
// Decode the encoded data with AES
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, sks);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES decryption error");
}
return Base64.encodeToString(decodedBytes, Base64.DEFAULT);
}
public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
final int iterations = 1000;
// Generate a 256-bit key
final int outputKeyLength = 128;
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
return secretKey;
}
Output:
E/AES_Test: b = cGxz
**
[EDIT] Modified my code but now there is a NullPointerException
**
/**
* Encrypts a string with AES (128 bit key)
* #param fin
* #return the AES encrypted string
*/
private byte[] encryptFIN128AES(String fin) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey(PASSPHRASE, SALT.getBytes(StandardCharsets.UTF_8)).getEncoded(), "AES");
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES key generation error");
}
// Encode the original data with AES
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, sks);
encodedBytes = c.doFinal(fin.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES encryption error");
}
return encodedBytes;
}
/**
* Decrypts a string with AES (128 bit key)
* #param encodedBytes
* #return the decrypted String
*/
private String decryptFIN128AES(byte[] encodedBytes) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey(PASSPHRASE, SALT.getBytes(StandardCharsets.UTF_8)).getEncoded(), "AES");
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES key generation error");
}
// Decode the encoded data with AES
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, sks);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES decryption error");
}
//return Base64.encodeToString(decodedBytes, Base64.DEFAULT);
return new String(decodedBytes, StandardCharsets.UTF_8);
}
// generateKey(char[] passphraseOrPin, byte[] salt) remains the same
Error:
E/decryptFIN128AES: AES decryption error
E/AndroidRuntime: FATAL EXCEPTION: Thread-176
Process: testapp.ttyi.nfcapp, PID: 2920
java.lang.NullPointerException: Attempt to get length of null array
at java.lang.String.<init>(String.java:371)
at testapp.ttyi.nfcapp.DisplayQRActivity.decryptFIN128AES(DisplayQRActivity.java:254)
at testapp.ttyi.nfcapp.DisplayQRActivity.access$100(DisplayQRActivity.java:29)
at testapp.ttyi.nfcapp.DisplayQRActivity$1.run(DisplayQRActivity.java:77)
at java.lang.Thread.run(Thread.java:818)
**
[EDIT2] Resolved (But no Padding/Encryption Mode allowed)
**
I managed to resolve the issue. (Decodes to "pls") using Codo's solution ofreturn new String(decodedBytes, StandardCharsets.UTF_8);
Though it only works when the algorithm used is:
Cipher c = Cipher.getInstance("AES");
When I put Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
The "NullPointerException" as seen above will happen. My observation shows that during decryption:
try {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, sks);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES decryption error");
}
something will fail and it will always print out:
E/decryptFIN128AES: AES decryption error
And thus the NullPointerException will occur as decodedBytes is always initiated to NULL.
Your process is not balanced. For encryption you do:
Encode string using default charset (fin.getBytes()) to get binary data
Encrypt binary data to get encrypted data (doFinal)
For the decryption, you do:
Decrypt encrypted data to get unencrypted binary data (doFinal)
Encode binary data as a Base64 string
Instead of Base64 encoding, the last step should be the reverse of step 1 in the encryption, i.e. you should decode the binary data into a string:
return String(decodedBytes);
It strongly recommend, you do not use the default charset for encoding and decoding as it depends on the system's setting. So it could be different between the system where you encrypt and decyrpt.
So use:
fin.getBytes(StandardCharsets.UTF_8);
and:
return String(decodedBytes, StandardCharsets.UTF_8);
The same applies for the salt.
Also note that you should specify the padding and chaining mode. If you don't, provider-specific default values apply. See #Ryan's answer for more details.
You should research more on how to use AES correctly as you are missing some basic fundamentals of AES security: no IV (assuming using CBC), no mode specified (such as CBC), and no padding specified (such as PKCS5).
Looks like char encoding issue. With minor modifications it works.
in encryptFIN128AES:
encodedBytes = c.doFinal(Base64.getEncoder().encode(fin.getBytes()));
in decryptFIN128AES:
return new String(Base64.getDecoder().decode(decodedBytes));
Related
I have the following code for encrypt
public static String encrypt(String value, char[] secret) {
try {
final byte[] bytes = value != null ? value.getBytes(StandardCharsets.UTF_8) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8), 20));
return new String(Base64.encodeBase64(pbeCipher.doFinal(bytes)), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
and the following code for decrypt.
public static String decrypt(String value, char[] secret) {
try {
final byte[] bytes = value != null ? Base64.decodeBase64(value.getBytes(StandardCharsets.UTF_8)) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8), 20));
return new String(pbeCipher.doFinal(bytes), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
However, sometimes the exception is being thrown at
pbeCipher.doFinal(bytes)
in decrypt method.
The exception is javax.crypto.BadPaddingException: pad block corrupted
It's strange, as I'm getting this exception sometimes with the same values .
Any ideas?
Thanks.
The most likely reason would simply be the wrong password to be supplied. If the wrong password is supplied then the wrong key is derived. Then the ciphertext will be decrypted to garbage plaintext. This will only be noticed if the padding exception gets thrown: unpadding random bytes is likely to fail.
You could e.g. first validate that the derived key is correct by performing a HMAC over known data using the derived key. In addition, it would be a good idea to use some kind of authenticated encryption, so that if the key or data is wrong or corrupted that decryption does indeed fail. If you're unlucky then - at this time - the data will decrypt, unpadding will succeed and you end up with garbage plaintext.
Of course, you'd better upgrade to PBKDF2 for key derivation, and upgrade AES to e.g. AES-GCM instead of DES. Currently your encryption is entirely insecure, even if you use a strong password.
Your problem is
IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8)
I have ran the following code multiple times and no exception occurred and the decrypted data was equal to "Hello there!":
public static void main(String[] args)
{
new CryptographyError();
}
private CryptographyError()
{
char[] secret = "MySecret".toCharArray();
String mesasge = "Hello there!";
EncryptedData encryptedData = encrypt(mesasge, secret);
System.out.println("ENCRYPTED " + encryptedData.encryptedString);
String decrypted = decrypt(encryptedData, secret);
System.out.println("DECRYPTED " + decrypted);
}
private static final SecureRandom RANDOM = new SecureRandom();
public static EncryptedData encrypt(String value, char[] secret) {
try {
final byte[] bytes = value != null ? value.getBytes(StandardCharsets.UTF_8) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
byte[] salt = new byte[8];
RANDOM.nextBytes(salt);
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(salt, 20));
return new EncryptedData(salt, new String(Base64.getEncoder().encode(pbeCipher.doFinal(bytes)), StandardCharsets.UTF_8));
} catch (Exception e) {
e.printStackTrace();
System.out.println(value);
}
return null;
}
public static String decrypt(EncryptedData encryptedData, char[] secret) {
try {
String value = encryptedData.encryptedString;
byte[] salt = encryptedData.salt;
final byte[] bytes = value != null ? Base64.getDecoder().decode(value.getBytes(StandardCharsets.UTF_8)) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(salt, 20));
return new String(pbeCipher.doFinal(bytes), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static class EncryptedData
{
private final byte[] salt;
private final String encryptedString;
private EncryptedData(byte[] salt, String encryptedString)
{
this.salt = salt;
this.encryptedString = encryptedString;
}
}
The only main difference between my code and your code is
IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8)
which means that must not return the same value on encryption and decryption.
Also if you want to test this you can just change the salt between them and notice the exception is thrown again.
Also Maarten Bodewes gave you some good notes about how to improve your code.
I would like to encrypt 3 strings using AES 128 in Java / Grails, and using the code below, but i get the error "An error occurred when encrypting", can someone tell me what is wrong with my code, how to fix it. thanks in advance and to Stackoverflow.
String url = "https://someurl.com"
String token = createToken(bookNumber, invNumber, cusNumber)
url += '?ref=' + token
class AesEncryptor {
static byte[] encrypt(String clearText) {
byte[] encrypted = null
try {
byte[] iv = new byte[16]
Arrays.fill(iv, (byte) 0)
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
encrypted = cipher.doFinal(clearText.getBytes("UTF-8"))
}
catch (Exception e) {
log.error "An error occurred when encrypting", e
}
encrypted
}
/**
* Creates a token.
* #return
*/
static String createToken(final String bookNumber, final String invNumber, final String cusNumber) {
String data = bookNumber + invNumber + cusNumber
String token = URLEncoder.encode(Base64.encodeBase64String(encrypt(data)), "UTF-8")
token
}
}
the error i get:
java.lang.IllegalStateException: Cipher not initialized
at javax.crypto.Cipher.checkCipherState(Cipher.java:1672)
at javax.crypto.Cipher.doFinal(Cipher.java:2079)
at javax.crypto.Cipher$doFinal$1.call(Unknown Source)
cipher.init method call is missed in your code. Check the below code.
public byte[] encrypt(byte[] data, byte[] key) {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
return cipher.doFinal(data);
}
For decrypt have to change mode to Cipher.DECRYPT_MODE
In this code, this line is causing an exception:
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
javax.crypto.BadPaddingException: pad block corrupted
I got the code from:
http://www.techrepublic.com/blog/software-engineer/attention-android-developers-keep-user-data-safe/
Any ideas?
private String decrypt (String encryptedText) {
byte[] clearText = null;
try {
SecretKeySpec ks = new SecretKeySpec(getKey(), "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, ks);
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
return new String(clearText, "UTF-8");
} catch (Exception e) {
return null;
}
}
Details: I am encrypting it on the android as well
owlstead's advice was helpful, but for this case when using the code in
Attention Android developers: Keep user data safe
http://www.techrepublic.com/blog/software-engineer/attention-android-developers-keep-user-data-safe/
I made some changes to the code that might be helpful for other people in the future. I completely deleted the getkey method.
private static String seed;
/**
* Encrypts the text.
* #param clearText The text you want to encrypt
* #return Encrypted data if successful, or null if unsucessful
*/
protected String encrypt(String clearText) {
byte[] encryptedText = null;
try {
byte[] keyData = seed.getBytes();
SecretKey ks = new SecretKeySpec(keyData, "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, ks);
encryptedText = c.doFinal(clearText.getBytes("UTF-8"));
return Base64.encodeToString(encryptedText, Base64.DEFAULT);
} catch (Exception e) {
return null;
}
}
/**
* Decrypts the text
* #param encryptedText The text you want to encrypt
* #return Decrypted data if successful, or null if unsucessful
*/
protected String decrypt (String encryptedText) {
byte[] clearText = null;
try {
byte[] keyData = seed.getBytes();
SecretKey ks = new SecretKeySpec(keyData, "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, ks);
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
return new String(clearText, "UTF-8");
} catch (Exception e) {
return null;
}
}
Java + Android + Encryption + Exception means just one thing normally, somebody is using the SecureRandom class again as a key derivation function. This fails when the SecureRandom implementation of "SHA1PRNG" does not behave as the one in Sun's implementation in Java SE. Especially if the seed is added to the state of the random number generator instead of the seed being used as a starting point of the PRNG.
Basically, simply use SecretKey aesKey = new SecretKeySpec(byte[] keyData, "AES") instead, or - if you start off with a password - try and generate the key using PBKDF2.
For me, the problem is in getKey()
Make sure that two invocation of getKey() return the same value.
I used new SecureRandom(password.getBytes()) to generate key. It worked on Windows, but on Android, it returned different value for different call.
I Reffred From this : https://androidfreetutorial.wordpress.com/2017/03/14/android-encryptiondecryption-with-aes-algorithm/
Change to "AES" From "AES/ECB/PKCS7Padding";
I have an Python application that creates a few files encrypted with AES/PKCS7. I have to read those files with a Java service. But my code is throwing an exception:
"javax.crypto.IllegalBlockSizeException: last block incomplete in decryption"
Here's my decryption code:
public String Decrypt(String strText)
{
try
{
// Text to decrypt
byte[] test = strText.getBytes();
//bytKey is the same key as Python app
SecretKeySpec objKey = new SecretKeySpec(bytKey, "AES");
Cipher objCipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
objCipher.init(Cipher.DECRYPT_MODE, objKey);
// Here I got an exception >>
byte[] bytValue = objCipher.doFinal(test);
return new String(bytValue);
}
catch (Exception exc)
{
exc.printStackTrace();
}
return "";
}
If I decode the encrypted text before step into doFinal I get another exception:
"javax.crypto.BadPaddingException: pad block corrupted"
public String Decrypt(String strText)
{
try
{
BASE64Decoder decoder = new BASE64Decoder();
byte[] test = decoder.decodeBuffer(strText);
SecretKeySpec objKey = new SecretKeySpec(bytKey, "AES");
Cipher objCipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
objCipher.init(Cipher.DECRYPT_MODE, objKey);
byte[] bytValue = objCipher.doFinal(test);
return new String(bytValue);
}
catch (Exception exc)
{
exc.printStackTrace();
}
return "";
}
I'm not an expert on encrypt/decrypt and I guess it's quite simple to solve.
Any ideas to solve this issue? Thanks in advance!
I am first going to describe the problem which I have, and then give some background to what I am trying to do. Finally I shall paste some relevant code snippets.
I am trying to implement secret key encryption/decryption using the method specified in https://stackoverflow.com/a/992413/171993. If I use that example as-is, it works (although I did notice that I need to re-instantiate the Cipher class, otherwise the decryption produces garbage). However, in my implementation I get the following exception:
java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:77)
at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:91)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:469)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:217)
at javax.crypto.Cipher.implInit(Cipher.java:790)
at javax.crypto.Cipher.chooseProvider(Cipher.java:848)
at javax.crypto.Cipher.init(Cipher.java:1347)
at javax.crypto.Cipher.init(Cipher.java:1281)
at securitytest.SecurityManager.getCipher(SecurityManager.java:175)
at securitytest.SecurityManager.decryptSecretKey(SecurityManager.java:379)
at securitytest.SecurityManager.<init>(SecurityManager.java:82)
at securitytest.Test.main(Test.java:44)
To beat off the obvious question, yes, I do use the same algorithm: in fact, I assigned AES/CBC/PKCS5Padding to a constant and use that for instantiating both the Cipher class for encryption and decryption. I have also tried using only AES instantiate Cipher for the decryption, but that did not work either.
What I am trying to do is to password-protect a secret key by using AES/CBC/PKCS5Padding. I generate a random salt and initialisation vector. After encrypting the secret key, I append the initialisation vector (an array of bytes) to the encrypted value (also an array of bytes, creating a new array). I then encode this value in Base64 and store it in a Sqlite database, along with the salt (which, for the sake of simplicity, I store as a comma-separated string of values). However when I try to decrypt, I get the above exception. I can verify that directly after my call to the encryption method and directly before the decryption method, the following values are exactly the same (when converted to Base64 so that I can print it out):
The salt
The initialisation vector
The encrypted secret key (i.e. the cipher text)
I have tried both Java 6 and 7: both give the same results. I have also ruled out the unlimited strength jurisdiction policy files as an issue. In fact, I get a similar error if I substitute "AES" with another algorithm and adjust the length of the salt accordingly (for example "Blowfish" with IV length 8, which produces java.security.InvalidKeyException: Wrong algorithm: Blowfish required).
Google has not been able to help me with this problem. If anyone can shed some light on this, I would be very appreciative.
Here are some code snippets (my apologies, it is a little rough):
private static final int INIT_VECTOR_LENGTH = 16;
private static final int PRIVATE_KEY_LENGTH = 128;
private static final int SALT_LENGTH = 16;
private static final int PBE_KEYSPEC_ITERATIONS = 65536;
private static final String CIPHER_ALGORITHM = "AES";
private static final String CIPHER_ALGORITHM_MODE = "CBC";
private static final String CIPHER_ALGORITHM_PADDING = "PKCS5Padding";
private static final String DIGEST = "SHA1";
private static final String PLAINTEXT_ENCODING = "UTF8";
private static final String PRNG = DIGEST + "PRNG";
private static final String SECRET_KEY_FACTORY = "PBKDF2WithHmac" + DIGEST;
private static final String CIPHER = CIPHER_ALGORITHM + "/" + CIPHER_ALGORITHM_MODE + "/" + CIPHER_ALGORITHM_PADDING;
private IvParameterSpec ivSpec;
private final BASE64Encoder encoder = new BASE64Encoder();
private final BASE64Decoder decoder = new BASE64Decoder();
private Cipher getCipher(SecretKey key, int mode) {
Cipher cipher = null;
try {
cipher = Cipher.getInstance(CIPHER);
}
catch (NoSuchAlgorithmException e) {System.err.println(System.err.println(e.getMessage());}
catch (NoSuchPaddingException e) {System.err.println(e.getMessage());}
try {
if (mode == Cipher.ENCRYPT_MODE) {
cipher.init(mode, key);
AlgorithmParameters params = cipher.getParameters();
ivSpec = params.getParameterSpec(IvParameterSpec.class);
}
else {
/* This is my point-of-failure. */
cipher.init(mode, key, ivSpec);
}
}
catch (InvalidKeyException e) {System.err.println(e.getMessage());}
catch (InvalidAlgorithmParameterException e) {System.err.println(e.getMessage());}
catch (InvalidParameterSpecException e) {System.err.println(e.getMessage());}
return cipher;
}
private SecurityData.Secrets generateSecrets(SecretKey decryptedKey, byte[] salt, String passphrase) {
/* Generate a new key for encrypting the secret key. */
byte[] raw = null;
PBEKey key = null;
PBEKeySpec password = new PBEKeySpec(passphrase.toCharArray(), salt, PBE_KEYSPEC_ITERATIONS, PRIVATE_KEY_LENGTH);
SecretKeyFactory factory = null;
byte[] initVector = null;
byte[] secretKeyBytes = decryptedKey.getEncoded();
try {
factory = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY);
key = (PBEKey) factory.generateSecret(password);
}
catch (NoSuchAlgorithmException e) {System.err.println(e.getMessage());}
catch (InvalidKeySpecException e) {System.err.println(e.getMessage());}
SecretKeySpec newKey = new SecretKeySpec(key.getEncoded(), CIPHER_ALGORITHM);
/* Encrypt the secret key. */
IvParameterSpec ivSpec = new IvParameterSpec(initVector);
Cipher cipher = getCipher(newKey, ivSpec, Cipher.ENCRYPT_MODE);
try {
raw = cipher.doFinal(secretKeyBytes);
}
catch (IllegalBlockSizeException e) {System.err.println(e.getMessage());}
catch (BadPaddingException e) {System.err.println(e.getMessage());}
return new SecurityData.Secrets(encoder.encode(concatByteArrays(initVector, raw)), joinByteArray(salt));
}
private SecretKey decryptSecretKey(String encryptedKey, String salt, String passphrase) {
/* Get initialisation vector. */
byte[] raw = null, decoded = null, initVector = new byte[INIT_VECTOR_LENGTH];
try {
decoded = decoder.decodeBuffer(encryptedKey);
} catch (IOException e) {System.err.println(e.getMessage());}
System.arraycopy(decoded, 0, initVector, 0, INIT_VECTOR_LENGTH);
raw = new byte[decoded.length-INIT_VECTOR_LENGTH];
System.arraycopy(decoded, INIT_VECTOR_LENGTH, raw, 0, decoded.length-INIT_VECTOR_LENGTH);
IvParameterSpec ivSpec = new IvParameterSpec(initVector);
/* Generate the key. */
byte[] rawSalt = splitByteArrayString(salt);
PBEKeySpec password = new PBEKeySpec(passphrase.toCharArray(), rawSalt, PBE_KEYSPEC_ITERATIONS, PRIVATE_KEY_LENGTH);
SecretKeyFactory factory = null;
PBEKey key = null;
try {
factory = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY);
key = (PBEKey) factory.generateSecret(password);
}
catch (NoSuchAlgorithmException e) {System.err.println(e.getMessage());}
catch (InvalidKeySpecException e) {System.err.println(e.getMessage());}
Cipher cipher = getCipher(key, Cipher.DECRYPT_MODE);
/* Decrypt the message. */
byte[] stringBytes = null;
try {
stringBytes = cipher.doFinal(raw);
}
catch (IllegalBlockSizeException e) {System.err.println(e.getMessage());}
catch (BadPaddingException e) {System.err.println(e.getMessage());}
/* Converts the decoded message to a String. */
String clear = null;
try {
clear = new String(stringBytes, PLAINTEXT_ENCODING);
}
catch (UnsupportedEncodingException e) {System.err.println(e.getMessage());}
return new SecretKeySpec(clear.getBytes(), CIPHER_ALGORITHM);
}
The SecretKey object needs to return "AES" from its getAlgorithm() method. That's why the example has these steps:
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");