I am making an Android application and I want to encrypt a String before sending it to a DataBase, and encrytpion is correct. The problem happens when decrypting the String beca
I had the some problem. The solution:
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class StringEncrypter {
Cipher ecipher;
Cipher dcipher;
StringEncrypter(String password) {
// 8-bytes Salt
byte[] salt = {
(byte)0xA9, (byte)0x9B, (byte)0xC8, (byte)0x32,
(byte)0x56, (byte)0x34, (byte)0xE3, (byte)0x03
};
// Iteration count
int iterationCount = 19;
try {
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());
// Prepare the parameters to the cipthers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (InvalidAlgorithmParameterException e) {
System.out.println("EXCEPTION: InvalidAlgorithmParameterException");
} catch (InvalidKeySpecException e) {
System.out.println("EXCEPTION: InvalidKeySpecException");
} catch (NoSuchPaddingException e) {
System.out.println("EXCEPTION: NoSuchPaddingException");
} catch (NoSuchAlgorithmException e) {
System.out.println("EXCEPTION: NoSuchAlgorithmException");
} catch (InvalidKeyException e) {
System.out.println("EXCEPTION: InvalidKeyException");
}
}
/**
* Takes a single String as an argument and returns an Encrypted version
* of that String.
* @param str String to be encrypted
* @return <code>String</code> Encrypted version of the provided String
*/
public byte[] encrypt(String str) {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
//return new sun.misc.BASE64Encoder().encode(enc);
return enc;
} catch (BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
}
return null;
}
/**
* Takes a encrypted String as an argument, decrypts and returns the
* decrypted String.
* @param str Encrypted String to be decrypted
* @return <code>String</code> Decrypted version of the provided String
*/
public String decrypt(byte[] dec) {
try {
// Decode base64 to get bytes
//byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
//byte[] dec = Base64Coder.decode(str);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
}
return null;
}
}
I found this solution here. This works perfect. But I really wonder to know, what was the problem with AES.
From the general perspective , I think BadPaddingException can be due to the following reasons:
i> Incorrect size of the data for the encryption algorithm.
ii> Different keys are being used to encrypt & decrypt data.
getRawKey
assumes that the SecureRandom
instance is a well defined, deterministic Pseudo Random Number Generator.
First of all, it isn't well defined; the "SHA1PRNG"
method is not precisely defined. It may - and has - changed even for the SUN provider itself.
Second, the fact that seeding the SecureRandom
instance directly after construction makes it deterministic is SUN provider specific. In other words, other providers may choose to add the seed to the entropy pool. This entropy pool may have already been seeded with values obtained from the operating system. This is the case on many versions of Android. To explain it in layman terms: the Android SecureRandom
instance is fully random even if seeded directly after construction. This means that the getRawKey
method on such systems always generates a new, completely random key.
So what's the solution? The solution is either to store the key in a KeyStore
or to generate a key from a passphrase. In that case you may use the PBKDF2 (password based key derivation function #2) functionality already present in java SE and Android:
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
// note, the third argument should be set to a value as high as possible
// 10K is about the minimum nowadays
KeySpec ks = new PBEKeySpec(password, salt, 1024, 128);
SecretKey s = f.generateSecret(ks);
Key k = new SecretKeySpec(s.getEncoded(),"AES");
For those who wish to use String and Base64-encoding for usage with a database you can use these (slightly rewritten) functions from Mike Keskinov (and xalien on Anddev.org).
public String encrypt(String str)
{
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new String(Base64.encode(enc,0));
} catch (BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
}
return null;
}
public String decrypt(String str)
{
try {
// Decode base64 to get bytes
byte[] dec = Base64.decode(str, 0);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
}
return null;
}
This code also uses the built-in Android Base64 libraries, so no need for importing any additional external libraries.
Something I didn't realise at first was how to actually use this class from another class (my ignorance). Here's a simple example:
StringEncrypter se = new StringEncrypter("mypass");
String decryptedString = se.decrypt(encryptedString);
One more thing on Base64. I found out the hard way that Base64 encoding adds a '/n' linefeed character (0x0a) whenever the string gets longer than 76 characters. This severely messes up the decrypt results. To strip out these newline characters you can add something like:
b64_enc_str = se.encrypt(plaintext_str);
str = b64_enc_str.replace("/n","");
Hopefully it helps someone.