WHen im trying to decrypt using this class, i get "Bad pad block exception". Any idea why?
public class SimpleCrypto {
public static String encrypt(String seed, String cleartext) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result);
}
public static String decrypt(String seed, String encrypted) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = toByte(encrypted);
byte[] result = decrypt(rawKey, enc);
return new String(result);
}
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public static String toHex(String txt) {
return toHex(txt.getBytes());
}
public static String fromHex(String hex) {
return new String(toByte(hex));
}
public static byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();
return result;
}
public static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}
That piece of code does not know how to generate keys. It should certainly not use the proprietary, unspecified "SHA1PRNG" from Sun (which has different implementations under Android) to derive keys from a password.
The getRawKey function may generate different keys on different platforms. It may even generate completely random keys each time it is used, depending on the implementation of "SHA1PRNG". This means you may never be able to retrieve your plaintext ever again, especially on Android.
If you want to encrypt something with a password, please lookup articles on how to use Password Based Encryption (PBE) where the key derivation is performed using PBKDF2 - present the Oracle Java platform..
Furthermore, the seed seems to use the default character encoding, which might be UTF-16 LE, UTF-16 BE, UTF-8 or some latin based character encoding. It's also using ECB as the "AES" instance of Cipher uses ECB by default, so it is not safe for keeping your data confidential.
I think your Simple Crypto class is too simple. Any time you don't code decryption well enough to even work it's going to end up with bad pad block exception (unless you are using authenticated encryption).
And in the above code I don't even see any usage of initialization vectors, which means your code is either not going to work or be really insecure.
My suggestion don't code this yourself or even cut and paste code from the internet. There's a lot of bad cut and paste encryption code out there. Use a high level library instead.
Keyczar works on android:
https://github.com/kruton/android-keyczar-demo
Related
I have been using the following two methods to encrypt and decrypt sensitive info.
public static String encryptSensitiveInfo(String strToEncrypt,
String saltToEncrypt) throws Exception {
String encryptedString = "";
byte[] encryptedValue;
Key key = new SecretKeySpec(saltToEncrypt.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
encryptedValue = cipher.doFinal(strToEncrypt.getBytes());
encryptedString = new String(Base64.encodeBase64(encryptedValue));
encryptedValue = null;
return encryptedString;
}
public static String decryptSensitiveInfo(String strToDecrypt,
String saltToDecrypt) throws Exception {
String decryptedString = "";
byte[] decryptedValue;
Key key = new SecretKeySpec(saltToDecrypt.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
decryptedValue = cipher.doFinal(Base64.decodeBase64(strToDecrypt
.getBytes()));
decryptedString = new String(decryptedValue);
decryptedValue = null;
return decryptedString;
}
At the time of decryption I get "pad block corrupted" execption. Any help to resolve this issue would be very much appreciated. Thanks in advance.
You're correctly performing base 64 on the ciphertext because the ciphertext consists of random looking bytes. However, you forget to do the same with your key (which, inexplicably, is called saltToDecrypt in your code). If the keys do not match or if the ciphertext has become corrupted then you will almost certainly run into a BadPaddingException.
If the amount of ciphertext has changed an IllegalBlockSizeException is more likely and if the key isn't of the right size for AES, an InvalidKeyException.
I just want AES/CBC 128 bit encryption decryption in openSSl c and Android with identical result.
I have to send encrypted data using pre defined 16 bytes key from android to c via bluetooth.
So is there any common mechanism which i can use in both to produce identical result of encryption and decryption.
Any help would be appreciate.
Thank you.
I found a solution which work perfectly for Android.
I am going to post the answer if it would help anyone.
static String IV = "AAAAAAAAAAAAAAAA";
static String encryptionKey = "0123456789ABCDEF";
public static String decrypt(byte[] cipherText, String encryptionKey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/ZeroBytePadding"/*, "SunJCE"*/);
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return new String(cipher.doFinal(cipherText),"UTF-8");
}
public static byte[] encrypt(String plainText, String encryptionKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/ZeroBytePadding"/*, "SunJCE"*/);
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(plainText.getBytes("UTF-8"));
}
//To Encrypt
byte[] cipher = encrypt(plaintext, encryptionKey);
System.out.print("cipher: ");
//To Decrypt
String decrypted = decrypt(cipher, encryptionKey);
System.out.println("decrypt: " + decrypted);
I currently have an accessory that uses AES/CBC without a random number on the key. Instead, the message itself includes a random number and the key is hard-coded. I'm trying to do the same thing on my Android to exchange with the accessory through BLE. Somehow I can't figure out how generate a Key-class object without using a random number.
Here's an example of what I'd like to be able to do:
public byte[] encrypt(byte[] key, byte[] input) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding ");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(input);
}
Here's what I've tried:
public byte[] encrypt(byte[] key, byte[] input) throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom(key);
secureRandom.setSeed(key);
keyGenerator.init(128, secureRandom);
SecretKey secretkey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding ");
cipher.init(Cipher.ENCRYPT_MODE, secretkey);
return cipher.doFinal(input);
}
public byte[] encrypt(byte[] key, byte[] input) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES/CBC/NoPadding ");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding ");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
return cipher.doFinal(input);
}
Unfortunately both of those alter the key before the encryption.
How can I use my key "as is"?
If you want to encrypt with your own key without salt or using any random, you can do as following.
byte[] keyBuf= new byte[32];
byte[] b= key.getBytes("UTF-8");
int len= b.length;
if (len > keyBuf.length) len = keyBuf.length;
System.arraycopy(b, 0, keyBuf, 0, len);
SecretKey keySpec = new SecretKeySpec(keyBuf, "AES");
byte[] ivBuf= new byte[16];
IvParameterSpec ivSpec = new IvParameterSpec(ivBuf);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
Where key is my custom key as a String and b my key as a bytes[]. Initializing the cipher this way avoid the salting and allow you to always use your own key to encrypt anything.
I am encrypting images in assets folder with following code and trying to decrypt in apk. (I am doing this just to avoid easy copying of images with just unzip of apk file). I know i will have the key as part of apk.
I used and tested below code for encrypting the images with stand alone java program. (I tested them by decrpyting and it is working fine in stand alone java program.
Encryption
byte[] incrept = simpleCrypto.encrypt(KEY, simpleCrypto.getImageFile("E:/aeroplane.png"));
//Store encrypted file in SD card of your mobile with name vincent.mp3.
FileOutputStream fos = new FileOutputStream(new File("E:/out-aeroplane.png"));
fos.write(incrept);
fos.close();
Decryption
byte[] decrpt = simpleCrypto.decrypt(KEY, simpleCrypto.getImageFile("E:/out-aeroplane.png"));
//Store encrypted file in SD card of your mobile with name vincent.mp3.
FileOutputStream fosdecrypt = new FileOutputStream(new File("E:/outdecrypt-aeroplane.png"));
fosdecrypt.write(decrpt);
fosdecrypt.close();
Encrypt Decrypt logic
public byte[] getImageFile(String fileName) throws FileNotFoundException
{
byte[] Image_data = null;
byte[] inarry = null;
try {
File file = new File(fileName);
#SuppressWarnings("resource")
FileInputStream is = new FileInputStream (file); // use recorded file instead of getting file from assets folder.
int length = is.available();
Image_data = new byte[length];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
while ((bytesRead = is.read(Image_data)) != -1)
{
output.write(Image_data, 0, bytesRead);
}
inarry = output.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return inarry;
}
public byte[] encrypt(String seed, byte[] cleartext) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext);
// return toHex(result);
return result;
}
public byte[] decrypt(String seed, byte[] encrypted) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = encrypted;
byte[] result = decrypt(rawKey, enc);
return result;
}
//done
private byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
In my apk file after getting the image as InputStream, I am decrypting them after converting them as byte array. Again I am converting the decrypted byte array to input stream for BitmapFactory.decode. I tried both decodeByteArray and decodeStream. Both not working.
Image is encrypted with stand alone java program and it is decrypted in apk. (If i decrypt in stand alone java program, it is working fine.)
I am getting error saying Failed to decode Stream javax.crypto.BadPaddingException: pad block corrupted
public static Bitmap readBitmap(InputStream input) {
if (input == null)
return null;
try {
String KEY = "kumar";
byte[] inarry =IOUtils.toByteArray(input);
byte[] decrpt = SquarksCryptUtil.decrypt(KEY, inarry);
InputStream cleanStream = null;
cleanStream = new ByteArrayInputStream(decrpt);
// return BitmapFactory.decodeStream(cleanStream);
return BitmapFactory.decodeByteArray(decrpt, 0, decrpt.length);
// return BitmapFactory.decodeStream(input);
} catch (Exception e) {
Log.e(FILE_NAME, "Failed to decode Stream " + e);
return null;
} finally {
close(input);
}
}
The problem that you are facing is because you are assuming that the random number generates the same key on each platform. This issue is due to the fact that there is a very bad example on the internet that uses SecureRandom as a key derivation function, which it is not. SecureRandom is not even well defined if the seed is set directly. You can use PBKDF2 instead of the incorrect key derivation function on both sides, there should be enough pointers on stackoverflow on how to perform PBKDF2 key derivation using Java.
The best thing to do is to decrypt the images on the SE platform and then re-encrypt them correctly using PBKDF2. Currently, the way the key is derived is only specified in the source code of the Sun implementation of "SHA1PRNG". That's not a good foundation at all.
Furthermore you need to assure that there are no platform dependencies during encryption/decryption, as others already have pointed out.
I am working on SOAP parsing sending request to Dot.net server.
Response is Encrypted on the server side by AES 256 algorithm. the response from the server is
k/tMHkiyWgoof8FAsTJttWZT7sku5QcJe6iZsWjkPXS+xE7ujjPn/f0E8sqWkXLH0jiXXHYwrV1SdJjbnXsiXxitfnAB83uIOr3VunB3fMVCOxTHk+Ej4mx+hBNB9pkJlgOJfTPX666fn6mwbkmFE4uqhTVbdGjm9HiSAW0mm91UANu3EERLS8g0UUPNdHn1
Now I am using the decryption method in java files as:
**public class SimpleCrypto {
public static String decrypt(String seed, String encrypted) throws Exception {
byte[] keyb = seed.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(keyb);
SecretKeySpec skey = new SecretKeySpec(thedigest, "AES/ECB/PKCS7Padding");
Cipher dcipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
dcipher.init(Cipher.DECRYPT_MODE, skey);
byte[] clearbyte = dcipher.doFinal(toByte(encrypted));
return new String(clearbyte);
}
public static byte[] toByte(String hexString) {
int len = hexString.length()/2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
return result;
}
}**
Your toByte function doesn't make any sense. Encrypted response is not a Hex String(A hex String has only 0123456789ABCDEF characters). So you treat your response as if it is a hex String and convert it to byte[].
You need to know the encoding used on the server when it converts encrypted data to String and you should use the same encoding to get the byte[] back from the response String. So try
encrypted.getBytes(YOUR_RESPONSE_ENCODING);
EDIT
Also your cipher should use the same algorithm used on the server to encrypt, in my case I'm using:
new SecretKeySpec(thedigest, "AES/CBC/ZeroBytePadding");
Note: We also use AES 256 encryption. But there are many ways to encrypt data using AES 256. You need to know the exact algorithm and padding method used on your server.
If you get BadPaddingException that means your data length is not an exact multiple of your key length. So in your case, encrypted.getBytes(YOUR_RESPONSE_ENCODING).length % thedigest.length must be equal to 0. You should add zero bytes to the end of your data so that this constraint is satisfied.
There is a excellent guide to cryptography here:
http://www.javamex.com/tutorials/cryptography/index.shtml