Encryption using AES 256 in Android and IPhone (Different result) - android

I am trying to implement client side encryption/decryption on Android platform by referencing IOS implementations. I am wrestling with the question that encryption and decryption on Android and IOS platform are different even they have used the same algorithm. Let`s say, when Android device encrypt and upload a file to server, the IOS device couldn`t download and decrypt it correctly.
The algorithm I am using
Encrypt the file key with the user provided password. We first use PBKDF2 algorithm (1000 iteratioins of SHA256) to derive a key/iv pair from the password, then use AES 256/CBC to encrypt the file key. The result is called the "encrypted file key". This encrypted file key will be sent to and stored on the server. When you need to access the data, you can decrypt the file key from the encrypted file key.
All file data is encrypted by the file key with AES 256/CBC. We use PBKDF2 algorithm (1000 iterations of SHA256) to derive key/iv pair from the file key.
store the derived key/iv pair locally and use them to encrypt files. After encryption, the data is uploaded to the server. The same with decrypt files when downloading files.
Android code
private static final String TAG = Crypto.class.getSimpleName();
private static final String CIPHER_ALGORITHM = "AES/CBC/NoPadding";
private static int KEY_LENGTH = 32;
private static int KEY_LENGTH_SHORT = 16;
// minimum values recommended by PKCS#5, increase as necessary
private static int ITERATION_COUNT = 1000;
// Should generate random salt for each repo
private static byte[] salt = {(byte) 0xda, (byte) 0x90, (byte) 0x45, (byte) 0xc3, (byte) 0x06, (byte) 0xc7, (byte) 0xcc, (byte) 0x26};
private Crypto() {
}
/**
* decrypt repo encKey
*
* #param password
* #param randomKey
* #param version
* #return
* #throws UnsupportedEncodingException
* #throws NoSuchAlgorithmException
*/
public static String deriveKeyPbkdf2(String password, String randomKey, int version) throws UnsupportedEncodingException, NoSuchAlgorithmException {
if (TextUtils.isEmpty(password) || TextUtils.isEmpty(randomKey)) {
return null;
}
PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
gen.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password.toCharArray()), salt, ITERATION_COUNT);
byte[] keyBytes;
if (version == 2) {
keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH * 8)).getKey();
} else
keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH_SHORT * 8)).getKey();
SecretKey realKey = new SecretKeySpec(keyBytes, "AES");
final byte[] iv = deriveIVPbkdf2(realKey.getEncoded());
return seafileDecrypt(fromHex(randomKey), realKey, iv);
}
public static byte[] deriveIVPbkdf2(byte[] key) throws UnsupportedEncodingException {
PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
gen.init(key, salt, 10);
return ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH_SHORT * 8)).getKey();
}
/**
* All file data is encrypted by the file key with AES 256/CBC.
*
* We use PBKDF2 algorithm (1000 iterations of SHA256) to derive key/iv pair from the file key.
* After encryption, the data is uploaded to the server.
*
* #param plaintext
* #param key
* #return
*/
private static byte[] seafileEncrypt(byte[] plaintext, SecretKey key, byte[] iv) {
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
return cipher.doFinal(plaintext);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
Log.e(TAG, "NoSuchAlgorithmException " + e.getMessage());
return null;
} catch (InvalidKeyException e) {
e.printStackTrace();
Log.e(TAG, "InvalidKeyException " + e.getMessage());
return null;
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
Log.e(TAG, "InvalidAlgorithmParameterException " + e.getMessage());
return null;
} catch (NoSuchPaddingException e) {
e.printStackTrace();
Log.e(TAG, "NoSuchPaddingException " + e.getMessage());
return null;
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
Log.e(TAG, "IllegalBlockSizeException " + e.getMessage());
return null;
} catch (BadPaddingException e) {
e.printStackTrace();
Log.e(TAG, "BadPaddingException " + e.getMessage());
return null;
}
}
/**
* All file data is encrypted by the file key with AES 256/CBC.
*
* We use PBKDF2 algorithm (1000 iterations of SHA256) to derive key/iv pair from the file key.
* After encryption, the data is uploaded to the server.
*
* #param plaintext
* #param key
* #return
*/
public static byte[] encrypt(byte[] plaintext, String key, byte[] iv, int version) throws NoSuchAlgorithmException {
PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
gen.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(key.toCharArray()), salt, ITERATION_COUNT);
byte[] keyBytes;
if (version == 2) {
keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH * 8)).getKey();
} else
keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH_SHORT * 8)).getKey();
SecretKey realKey = new SecretKeySpec(keyBytes, "AES");
return seafileEncrypt(plaintext, realKey , iv);
}
IOS code
+ (int)deriveKey:(const char *)data_in inlen:(int)in_len version:(int)version key:(unsigned char *)key iv:(unsigned char *)iv
{
unsigned char salt[8] = { 0xda, 0x90, 0x45, 0xc3, 0x06, 0xc7, 0xcc, 0x26 };
if (version == 2) {
PKCS5_PBKDF2_HMAC (data_in, in_len,
salt, sizeof(salt),
1000,
EVP_sha256(),
32, key);
PKCS5_PBKDF2_HMAC ((char *)key, 32,
salt, sizeof(salt),
10,
EVP_sha256(),
16, iv);
return 0;
} else if (version == 1)
return EVP_BytesToKey (EVP_aes_128_cbc(), /* cipher mode */
EVP_sha1(), /* message digest */
salt, /* salt */
(unsigned char*)data_in,
in_len,
1 << 19, /* iteration times */
key, /* the derived key */
iv); /* IV, initial vector */
else
return EVP_BytesToKey (EVP_aes_128_ecb(), /* cipher mode */
EVP_sha1(), /* message digest */
NULL, /* salt */
(unsigned char*)data_in,
in_len,
3, /* iteration times */
key, /* the derived key */
iv); /* IV, initial vector */
}
+(int)seafileEncrypt:(char **)data_out outlen:(int *)out_len datain:(const char *)data_in inlen:(const int)in_len version:(int)version key:(uint8_t *)key iv:(uint8_t *)iv
{
int ret, blks;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (&ctx);
if (version == 2)
ret = EVP_EncryptInit_ex (&ctx,
EVP_aes_256_cbc(), /* cipher mode */
NULL, /* engine, NULL for default */
key, /* derived key */
iv); /* initial vector */
else if (version == 1)
ret = EVP_EncryptInit_ex (&ctx,
EVP_aes_128_cbc(), /* cipher mode */
NULL, /* engine, NULL for default */
key, /* derived key */
iv); /* initial vector */
else
ret = EVP_EncryptInit_ex (&ctx,
EVP_aes_128_ecb(), /* cipher mode */
NULL, /* engine, NULL for default */
key, /* derived key */
iv); /* initial vector */
if (ret == DEC_FAILURE)
return -1;
blks = (in_len / BLK_SIZE) + 1;
*data_out = (char *)malloc (blks * BLK_SIZE);
if (*data_out == NULL) {
Debug ("failed to allocate the output buffer.\n");
goto enc_error;
}
int update_len, final_len;
/* Do the encryption. */
ret = EVP_EncryptUpdate (&ctx,
(unsigned char*)*data_out,
&update_len,
(unsigned char*)data_in,
in_len);
if (ret == ENC_FAILURE)
goto enc_error;
/* Finish the possible partial block. */
ret = EVP_EncryptFinal_ex (&ctx,
(unsigned char*)*data_out + update_len,
&final_len);
*out_len = update_len + final_len;
/* out_len should be equal to the allocated buffer size. */
if (ret == ENC_FAILURE || *out_len != (blks * BLK_SIZE))
goto enc_error;
EVP_CIPHER_CTX_cleanup (&ctx);
return 0;
enc_error:
EVP_CIPHER_CTX_cleanup (&ctx);
*out_len = -1;
if (*data_out != NULL)
free (*data_out);
*data_out = NULL;
return -1;
}
+ (void)generateKey:(NSString *)password version:(int)version encKey:(NSString *)encKey key:(uint8_t *)key iv:(uint8_t *)iv
{
unsigned char key0[32], iv0[16];
char passwordPtr[256] = {0}; // room for terminator (unused)
[password getCString:passwordPtr maxLength:sizeof(passwordPtr) encoding:NSUTF8StringEncoding];
if (version < 2) {
[NSData deriveKey:passwordPtr inlen:(int)password.length version:version key:key iv:iv];
return;
}
[NSData deriveKey:passwordPtr inlen:(int)password.length version:version key:key0 iv:iv0];
char enc_random_key[48], dec_random_key[48];
int outlen;
hex_to_rawdata(encKey.UTF8String, enc_random_key, 48);
[NSData seafileDecrypt:dec_random_key outlen:&outlen datain:(char *)enc_random_key inlen:48 version:2 key:key0 iv:iv0];
[NSData deriveKey:dec_random_key inlen:32 version:2 key:key iv:iv];
}
- (NSData *)encrypt:(NSString *)password encKey:(NSString *)encKey version:(int)version
{
uint8_t key[kCCKeySizeAES256+1] = {0}, iv[kCCKeySizeAES128+1];
[NSData generateKey:password version:version encKey:encKey key:key iv:iv];
char *data_out;
int outlen;
int ret = [NSData seafileEncrypt:&data_out outlen:&outlen datain:self.bytes inlen:(int)self.length version:version key:key iv:iv];
if (ret < 0) return nil;
return [NSData dataWithBytesNoCopy:data_out length:outlen];
}
Here is the complete project for anyone who found this useful.
Support client side encryption #487
How does an encrypted library work?

Hi me too had the same problem.
I found 2 things causing mismatch for my code:
1. ios and android encryption algorithms was not same(i have requested PKCS7Padding algorithm in ios and i tried NoPadding and AES/CBC/PKCS5Padding algorithms in android.
2. The key generated in ios is different than in Android.
Please see my worked code both gives same output in ios and android:
IOS:
- (NSString *) encryptString:(NSString*)plaintext withKey:(NSString*)key {
NSData *data = [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
return [data base64EncodedStringWithOptions:kNilOptions];
}
- (NSString *) decryptString:(NSString *)ciphertext withKey:(NSString*)key {
if ([ciphertext isKindOfClass:[NSString class]]) {
NSData *data = [[NSData alloc] initWithBase64EncodedString:ciphertext options:kNilOptions];
return [[NSString alloc] initWithData:[data AES256DecryptWithKey:key] encoding:NSUTF8StringEncoding];
}
return nil;
}
Android:(We modified aes w.r.t iOS default method)
private static String Key = "your key";
public static String encryptString(String stringToEncode) throws NullPointerException {
try {
SecretKeySpec skeySpec = getKey(Key);
byte[] clearText = stringToEncode.getBytes("UTF8");
final byte[] iv = new byte[16];
Arrays.fill(iv, (byte) 0x00);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);
String encrypedValue = Base64.encodeToString(cipher.doFinal(clearText), Base64.DEFAULT);
return encrypedValue;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
public static String encryptString(String stringToEncode) throws NullPointerException {
try {
SecretKeySpec skeySpec = getKey(Key);
byte[] clearText = stringToEncode.getBytes("UTF8");
final byte[] iv = new byte[16];
Arrays.fill(iv, (byte) 0x00);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
byte[] cipherData = cipher.doFinal(Base64.decode(stringToEncode.getBytes("UTF-8"), Base64.DEFAULT));
String decoded = new String(cipherData, "UTF-8");
return decoded;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
private static SecretKeySpec getKey(String password) throws UnsupportedEncodingException {
int keyLength = 256;
byte[] keyBytes = new byte[keyLength / 8];
Arrays.fill(keyBytes, (byte) 0x0);
byte[] passwordBytes = password.getBytes("UTF-8");
int length = passwordBytes.length < keyBytes.length ? passwordBytes.length : keyBytes.length;
System.arraycopy(passwordBytes, 0, keyBytes, 0, length);
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
return key;
}
Hope this help u guys
Thanks

Related

How to store multiple variables in AndroidKeystore?

I'm trying to use Biometric and EncryptedSharedPreferences to store multi passwords of a user in the AndroidKeystore (and in the StrongBox).
I'm trying it, but I can't get to store more than one. I explain it below:
We store a string value by passing a keyalias for the keystore and the key-value we want to save. Example:
KEYALIAS: FIRSTKS
KEY: password1
VALUE: 123abc
If we try to read the value, we get it without problem.
If we try to save another value in FIRSTKS, the previously stored value is lost and only the new saved value is found.
KEYALIAS: FIRSTKS
KEY: password2
VALUE: 456def
If I try to read password1, the value is not found.
I can only store new values if I create a different keyalias. Example:
KEYALIAS: FIRSTKS
KEY: password1
VALUE: 123abc
KEYALIAS: SECONDKS
KEY: password2
VALUE: 456def
I don't know if this is a normal behaviour or not.
I guess my biometrics methods to access encrypt and decrypt are OK because if not, I wouldn't be able to read the values. But I leave my cryptographic class to see if you see any errors in the logic.
public class CryptographicUtils {
private static final String TAG = "CrytographicUtils";
/**
* Transforms Byte array into Hexadecimal String.
*
* #param buf Byte array to be transformed.
* #return String.
*/
public static String asHex(byte[] buf) {
StringBuilder strum = new StringBuilder(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10) {
strum.append("0");
}
strum.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strum.toString();
}
/**
* Transforms Hexadecimal String into Byte array.
*
* #param string String to be transformed.
* #return Byte array,
*/
public static byte[] fromHexString(String string) {
int len = string.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(string.charAt(i), 16) << 4)
+ Character.digit(string.charAt(i + 1), 16));
}
return data;
}
/**
* Generates the Biometric Key.
*
* #param context Context.
*/
public static void generateBiometricKey(Context context, String keyAlias, boolean userAuthenticationRequired, int validationTimeout) {
try {
KeyGenParameterSpec.Builder spec = new KeyGenParameterSpec.Builder(
keyAlias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);
spec.setBlockModes(KeyProperties.BLOCK_MODE_CBC);
spec.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
spec.setKeySize(256);
spec.setUserAuthenticationRequired(userAuthenticationRequired);
spec.setUserAuthenticationValidityDurationSeconds(validationTimeout);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M ) {
spec.setInvalidatedByBiometricEnrollment(userAuthenticationRequired);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
boolean hasStrongBox = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE);
spec.setDigests(KeyProperties.DIGEST_SHA256);
spec.setIsStrongBoxBacked(hasStrongBox);
}
generateSecretKey(spec.build());
} catch (NoSuchProviderException | NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
Toast.makeText(context,
"Authentication error: " + e.getMessage(), Toast.LENGTH_SHORT)
.show();
}
}
/**
* Generates the Biometric Secret Key.
*
* #param keyGenParameterSpec Key Generator.
* #throws NoSuchProviderException Exception with Security Provider.
* #throws NoSuchAlgorithmException Exception with cryptographic algorithm.
* #throws InvalidAlgorithmParameterException Exception for invalid or inappropriate algorithm parameters.
*/
private static void generateSecretKey(KeyGenParameterSpec keyGenParameterSpec) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(keyGenParameterSpec);
keyGenerator.generateKey();
}
/**
* Obtains a Secret Key.
*
* #return Secret Key.
* #throws KeyStoreException KeyStore exception.
* #throws CertificateException Exception of certificate problems.
* #throws NoSuchAlgorithmException Exception with cryptographic algorithm.
* #throws IOException I/O exception.
* #throws UnrecoverableKeyException Exception key in the keystore cannot be recovered
*/
public static SecretKey getSecretKey(String keyAlias) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
// Before the keystore can be accessed, it must be loaded.
keyStore.load(null);
return ((SecretKey) keyStore.getKey(keyAlias, null));
}
/**
* Obtains a Cipher.
*
* #return Cipher.
* #throws NoSuchPaddingException Exception padding mechanism is not available in the environment.
* #throws NoSuchAlgorithmException Exception with cryptographic algorithm.
*/
public static Cipher getEncryptCipher(Context context, String keyAlias, boolean userAuthenticationRequired, int validationTimeout) throws NoSuchPaddingException, NoSuchAlgorithmException, KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException, InvalidKeyException {
CryptographicUtils.generateBiometricKey(context, keyAlias, userAuthenticationRequired, validationTimeout);
Cipher res = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
SecretKey secretKey = getSecretKey(keyAlias);
res.init(Cipher.ENCRYPT_MODE, secretKey);
return res;
}
/**
* Obtains a Decrypt Cipher.
*
* #throws NoSuchPaddingException Exception padding mechanism is not available in the environment.
* #throws NoSuchAlgorithmException Exception with cryptographic algorithm.
* #throws InvalidAlgorithmParameterException Exception for invalid or inappropriate algorithm parameters.
* #throws InvalidKeyException Exception for invalid Keys.
*/
public static Cipher getDecryptCipher(String keyAlias, String ivString) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, UnrecoverableKeyException, CertificateException, KeyStoreException, IOException {
SecretKey secretKey = CryptographicUtils.getSecretKey(keyAlias);
String algorithm = KeyProperties.KEY_ALGORITHM_AES;
String blockMode = KeyProperties.BLOCK_MODE_CBC;
String padding = KeyProperties.ENCRYPTION_PADDING_PKCS7;
String alg = algorithm + "/" + blockMode + "/" + padding;
Cipher res = Cipher.getInstance(alg);
byte[] iv = fromHexString(ivString);
res.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
return res;
}
/**
* The Cipher created with [getInitializedCipherForEncryption] is used here
*/
public static String encryptData(String plainText, Cipher cipher) throws BadPaddingException, IllegalBlockSizeException {
return asHex(Objects.requireNonNull(Objects.requireNonNull(cipher.doFinal(plainText.getBytes(Charset.defaultCharset())))));
}
public static String getIV(Cipher cipher) {
return asHex(cipher.getIV());
}
/**
* The Cipher created with [getInitializedCipherForDecryption] is used here
*/
public static String decryptData(String ciphertext, Cipher cipher) throws BadPaddingException, IllegalBlockSizeException {
byte [] cipherData = fromHexString(ciphertext);
byte[] passBioByteArray = cipher.doFinal(cipherData);
return new String(passBioByteArray, Charset.defaultCharset());
}
}
I understand that I can store more than one value in the keystore with a particular keyalias, or not?
Thanks for all

Problem with decrypt String which is Encrypted in iOS AES/CBC/PKCS7Padding 128 bit Algorithm in Android

Guide me on this. I am facing problem with decryption of string which is encrypted by iOS "AES/CBC/PKCS7Padding". It throws exception. I want to decrypt string in android, which is encrypted in ios.
In Android i am following this structure:
For Example:
String text = "rzp_test_DezQO1BVMXhkZY";
String key = "5b0904cfada01b8182bcc029b928244d"; // secret key - 128 bit key
String iv_key ="c999cbd1f130db1d";
I want to encrypt and decrypt 'text' string mentioned above. If i do encryption and decryption from Android only. then its working fine. but if i tried to decrypt key which is encrypted in ios then it throws errors.
// Create key and cipher
Cipher dcipher, d1cipher;
IvParameterSpec ivSpec = new IvParameterSpec(iv_key.getBytes());
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
ecipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
dcipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
ecipher.init(ENCRYPT_MODE, aesKey, ivSpec);
dcipher.init(DECRYPT_MODE, aesKey, ivSpec);
Encryption Algo:
public String encrypt(String str) throws Exception {
// 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 Base64.encodeToString(enc, Base64.DEFAULT);
}
Decryption Algo:
public String decrypt(String str) throws Exception {
// Decode base64 to get bytes
String decrypted = "";
try {
byte[] dec = Base64.decode(str, Base64.DEFAULT);
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
decrypted = new String(utf8, "UTF8").trim();
} catch (Exception e) {
e.printStackTrace();
}
return decrypted;
}
Its working fine in Android for both Encryption and Decryption. Check with this:
String encrypted = encrypt(text);
System.out.println("Encrypted String: " + encrypted);
String decrypted = decrypt(encrypted);
System.out.println("Decrypted String: " + decrypted);
Problem is with decrypting below encrypted string from ios, This Throws error:
String decrypted1 = decrypt("c7076c78fc5d9d92c1d86c1500dcc0366ddf1b6e32df00ceadc911239935460d");
System.out.println("Decrypted String1: " + decrypted1);
Error is:
W/System.err: javax.crypto.BadPaddingException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT
W/System.err: at com.android.org.conscrypt.NativeCrypto.EVP_CipherFinal_ex(Native Method)
W/System.err: at com.android.org.conscrypt.OpenSSLCipher$EVP_CIPHER.doFinalInternal(OpenSSLCipher.java:570)
W/System.err: at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:351)
W/System.err: at javax.crypto.Cipher.doFinal(Cipher.java:1741)
W/System.err: at com.example.aesencryption.Activities.MainActivity1.decrypt(MainActivity1.java:178)
Anyone can please guide me on this??
In IOS:
Following functions are used.
func aesEncrypt() -> String {
let iv: [UInt8] = Array(AES_IV.utf8)
let key: [UInt8] = Array(AES_SECRET.utf8)
do{
let encrypted = try AES (key: key, blockMode: CBC(iv: iv)).encrypt([UInt8](self.data(using: .utf8)!))
return Data(encrypted).base64EncodedString()
}
catch{
print("error on encrypting data ")
return ""
}
}
func aesDecrypt() -> String {
let iv: [UInt8] = Array(AES_IV.utf8)
let key: [UInt8] = Array(AES_SECRET.utf8)
do{
guard let data = Data(base64Encoded: self) else { return "" }
let decrypted = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7).decrypt([UInt8](data))
return String(bytes: decrypted, encoding: .utf8) ?? self
}
catch{
print("error on encrypting data ")
return ""
}
}
I got real string by using following method.
public static String decryptKey(String key_id) {
// Decode base64 to get bytes
String decrypted = "";
try {
Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
dcipher.init(DECRYPT_MODE, aesKey, ivSpec);
byte[] utf8 = dcipher.doFinal(hexToBytes(key_id));
// Decode using utf-8
decrypted = new String(utf8, "UTF8").trim();
} catch (Exception e) {
e.printStackTrace();
}
return decrypted;
}
public static byte[] hexToBytes(String str) {
if (str == null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i = 0; i < len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i * 2, i * 2 + 2), 16);
}
return buffer;
}
}

Google play store in-app billing

I've recently developed an app that I am soon to put on the Google play store. In this app I plan on adding one in-app purchase that once purchased allows the user to change colour settings(background & text).
I realised that checking the Google play store is unnecessary every time the application runs, especially if the purchase has been made already.It may also cause a problem if the user doesn't have GPRS/Wifi when using the app!
I was therefore thinking about creating a shared preference which would act as the condition to check whether the user has purchased the in-app purchase.
Is there any other way that is maybe more secure? as I've read shared preferences can be altered quite easily.
Any advice or suggestions would be appreciated.
You may use ProGuard.
Or you can save purchased status in secure way using some Encrypted text either in shared preference or in database. So it gets harder to manipulate it.
Encryption example:
Encrypt:
MCrypt mcrypt = new MCrypt();
String encrypted = MCrypt.bytesToHex(mcrypt.encrypt("Text to Encrypt"));
Decrypt:
MCrypt mcrypt = new MCrypt();
String decrypted = new String(mcrypt.decrypt(encrypted));
Mcrypt.java
public class MCrypt {
private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!)
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)
public MCrypt() {
ivspec = new IvParameterSpec(iv.getBytes());
keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
}
public byte[] encrypt(String text) throws Exception {
if (text == null || text.length() == 0) throw new Exception("Empty string");
byte[] encrypted = null;
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
try {
encrypted = android.util.Base64.encode(encrypted, android.util.Base64.NO_PADDING);
} catch (NoClassDefFoundError e) {
}
return encrypted;
}
public byte[] decrypt(String code) throws Exception {
if (code == null || code.length() == 0) throw new Exception("Empty string");
byte[] decrypted = null;
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
try {
decrypted = cipher.doFinal(android.util.Base64.decode(code, android.util.Base64.NO_PADDING));
} catch (NoClassDefFoundError e) {
}
return decrypted;
}
private static String padString(String source) {
char paddingChar = ' ';
int size = 16;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++) {
source += paddingChar;
}
return source;
}
}
Reference:
128 bit encryption

Generating ECPublicKey from byte array

I get a 64 uncompressed public key and need to run ECDH to generate a shared secret.
In order to call ECDH I need to convert the byte array to PublicKey and I am using the following code I have found in this forum:
public static void setOtherPublicKey(byte[] publicKeyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException
{
try {
//EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
KeyFactory generator = KeyFactory.getInstance("EC");
//PrivateKey privateKey = generator.generatePrivate(privateKeySpec);
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
blePubKey = generator.generatePublic(publicKeySpec);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to create KeyPair from provided encoded keys", e);
}
}
This code throws an InvalidKeySpecException.
As example, the public key of the other party is:
9b5e9a5a971877530c9cadbbea93c2ee2483d65052678f745bad79f110173520
54019832e11376537a76c4defd0b3dfdc667a974239147f323cdcfd2baa39892
Adding the code after getting the answers below:
public static void setOtherPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException
{
// first generate key pair of your own
ECPublicKey pubKey = (ECPublicKey) SecPage.g_kpA.getPublic();
ECParameterSpec params = pubKey.getParams();
int keySizeBytes = params.getOrder().bitLength() / Byte.SIZE;
// get the other party 64 bytes
//byte [] otherPub = crypto.getBlePubKeyBytes();
byte[] otherPub = hexStringToByteArray("ac2bdd28fce5c7b181b34f098b0934742281246ed907a5f646940c1edcb724e7c7358356aebea810322a8e324cc77f376df4cabd754110ad41ec178c0a6b8e5f");
ByteArrayBuffer xBytes = new ByteArrayBuffer(33);
ByteArrayBuffer yBytes = new ByteArrayBuffer(33);
byte[] zero = {(byte)0x00};
xBytes.append(zero, 0, 1);
xBytes.append(otherPub, 0, 32);
yBytes.append(zero, 0, 1);
yBytes.append(otherPub, 32, 32);
// generate the public key point
BigInteger x = new BigInteger(xBytes.buffer());
BigInteger y = new BigInteger(yBytes.buffer());
ECPoint w = new ECPoint(x, y);
// generate the key of the other side
ECPublicKeySpec otherKeySpec = new ECPublicKeySpec(w , params);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
blePubKey = (ECPublicKey) keyFactory.generatePublic(otherKeySpec);
}
Well, whaty'know, you can actually do this... explanation in the comments.
public class ECDHPub {
private static ECPublicKey decodeECPublicKey(ECParameterSpec params,
final byte[] pubkey) throws NoSuchAlgorithmException,
InvalidKeySpecException {
int keySizeBytes = params.getOrder().bitLength() / Byte.SIZE;
int offset = 0;
BigInteger x = new BigInteger(1, Arrays.copyOfRange(pubkey, offset,
offset + keySizeBytes));
offset += keySizeBytes;
BigInteger y = new BigInteger(1, Arrays.copyOfRange(pubkey, offset,
offset + keySizeBytes));
ECPoint w = new ECPoint(x, y);
ECPublicKeySpec otherKeySpec = new ECPublicKeySpec(w, params);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
ECPublicKey otherKey = (ECPublicKey) keyFactory
.generatePublic(otherKeySpec);
return otherKey;
}
private static byte[] encodeECPublicKey(ECPublicKey pubKey) {
int keyLengthBytes = pubKey.getParams().getOrder().bitLength()
/ Byte.SIZE;
byte[] publicKeyEncoded = new byte[2 * keyLengthBytes];
int offset = 0;
BigInteger x = pubKey.getW().getAffineX();
byte[] xba = x.toByteArray();
if (xba.length > keyLengthBytes + 1 || xba.length == keyLengthBytes + 1
&& xba[0] != 0) {
throw new IllegalStateException(
"X coordinate of EC public key has wrong size");
}
if (xba.length == keyLengthBytes + 1) {
System.arraycopy(xba, 1, publicKeyEncoded, offset, keyLengthBytes);
} else {
System.arraycopy(xba, 0, publicKeyEncoded, offset + keyLengthBytes
- xba.length, xba.length);
}
offset += keyLengthBytes;
BigInteger y = pubKey.getW().getAffineY();
byte[] yba = y.toByteArray();
if (yba.length > keyLengthBytes + 1 || yba.length == keyLengthBytes + 1
&& yba[0] != 0) {
throw new IllegalStateException(
"Y coordinate of EC public key has wrong size");
}
if (yba.length == keyLengthBytes + 1) {
System.arraycopy(yba, 1, publicKeyEncoded, offset, keyLengthBytes);
} else {
System.arraycopy(yba, 0, publicKeyEncoded, offset + keyLengthBytes
- yba.length, yba.length);
}
return publicKeyEncoded;
}
public static void main(String[] args) throws Exception {
// (only) required for named curves other than those used in JCE
Security.addProvider(new BouncyCastleProvider());
// create local and remote key
KeyPairGenerator kpgen = KeyPairGenerator.getInstance("ECDH", "BC");
ECGenParameterSpec genspec = new ECGenParameterSpec("brainpoolp256r1");
kpgen.initialize(genspec);
KeyPair localKeyPair = kpgen.generateKeyPair();
KeyPair remoteKeyPair = kpgen.generateKeyPair();
// test generation
byte[] encodedRemotePublicKey = encodeECPublicKey((ECPublicKey) remoteKeyPair
.getPublic());
// test creation
ECPublicKey remoteKey = decodeECPublicKey(
((ECPublicKey) localKeyPair.getPublic()).getParams(),
encodedRemotePublicKey);
// local key agreement
KeyAgreement localKA = KeyAgreement.getInstance("ECDH");
localKA.init(localKeyPair.getPrivate());
localKA.doPhase(remoteKey, true);
byte[] localSecret = localKA.generateSecret();
// remote key agreement
KeyAgreement remoteKA = KeyAgreement.getInstance("ECDH");
remoteKA.init(remoteKeyPair.getPrivate());
remoteKA.doPhase((ECPublicKey) localKeyPair.getPublic(), true);
byte[] remoteSecret = localKA.generateSecret();
// validation
System.out.println(Arrays.equals(localSecret, remoteSecret));
}
}

Android 4.2 broke my encrypt/decrypt code and the provided solutions don't work

First of all, I've already seen
Android 4.2 broke my AES encrypt/decrypt code
and
Encryption error on Android 4.2
and the provided solution:
SecureRandom sr = null;
if (android.os.Build.VERSION.SDK_INT >= JELLY_BEAN_4_2) {
sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
} else {
sr = SecureRandom.getInstance("SHA1PRNG");
}
doesn't work for me, because, when decoding data encrypted in Android<4.2 in Android 4.2, I get:
javax.crypto.BadPaddingException: pad block corrupted
at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:709)
My code is quite simple, and was working until Android 4.2:
public static byte[] encrypt(byte[] data, String seed) throws Exception {
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
secrand.setSeed(seed.getBytes());
keygen.init(128, secrand);
SecretKey seckey = keygen.generateKey();
byte[] rawKey = seckey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return cipher.doFinal(data);
}
public static byte[] decrypt(byte[] data, String seed) throws Exception {
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
secrand.setSeed(seed.getBytes());
keygen.init(128, secrand);
SecretKey seckey = keygen.generateKey();
byte[] rawKey = seckey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
return cipher.doFinal(data);
}
My guess is that the default provider wasn't the only thing that changed in Android 4.2, otherwise my code would work with the proposed solution.
My code was based on some post I found here at StackOverflow a long time ago; I see that it differs from the mentioned posts as it just crypts and decrypts byte arrays, whereas the others solutions crypt and decrypt Strings (HEX Strings, I think).
Does it have to do with the seed? Does it have a min/max length, restriction of chars, etc?
Any idea / solution?
EDIT:
After a lot of tests, I see that there are 2 problems:
The provider changed in Android 4.2 (API 17) -> This one is easy to fix, just apply the solution I mentioned at top of the post
BouncyCastle changed from 1.34 to 1.45 in Android 2.2 (API 8)->Android2.3 (API 9), so the decryption problem I previously told is the same as described here: BouncyCastle AES error when upgrading to 1.45
So now the question is: is there any way to recover data crypted in BouncyCastle 1.34 in BouncyCastle 1.45+?
First a disclaimer:
DO NOT ever use SecureRandom to derive a key! This is broken and doesn't make sense!
The following block of code from the question tries to deterministically derive a key from a password, called the "seed" as the password is used to "seed" the random number generator.
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
secrand.setSeed(seed.getBytes());
keygen.init(128, secrand);
SecretKey seckey = keygen.generateKey();
However, the "SHA1PRNG" algorithm is not well defined and implementations of "SHA1PRNG" may return different or even fully random keys as a result.
If you're reading an AES key from disk, just store the actual key and don't go through this weird dance. You can get a SecretKey for AES usage from the bytes by doing:
SecretKey key = new SecretKeySpec(keyBytes, "AES");
If you're using a password to derive a key, follow Nelenkov's excellent tutorial with the caveat that a good rule of thumb is the salt size should be the same size as the key output.
The iterationCount (work factor) is of course subject to change and should be changed as CPU power progresses - generally it is recommended not to go lower than 40 to 100K as of 2018. Beware that PBKDF2 only adds a constant time delay to guessing passwords; it is not a replacement for really weak passwords.
It looks like this:
/* User types in their password: */
String password = "password";
/* 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
byte[] salt; // Should be of saltLength
/* When first creating the key, obtain a salt with this: */
SecureRandom random = new SecureRandom();
byte[] salt = new byte[saltLength];
random.nextBytes(salt);
/* Use this to derive the key from the password: */
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt,
iterationCount, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
That's it. Anything else you should not use.
private static final int ITERATION_COUNT = 1000;
private static final int KEY_LENGTH = 256;
private static final String PBKDF2_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA1";
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final int PKCS5_SALT_LENGTH = 32;
private static final String DELIMITER = "]";
private static final SecureRandom random = new SecureRandom();
public static String encrypt(String plaintext, String password) {
byte[] salt = generateSalt();
SecretKey key = deriveKey(password, salt);
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
byte[] iv = generateIv(cipher.getBlockSize());
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
byte[] cipherText = cipher.doFinal(plaintext.getBytes("UTF-8"));
if(salt != null) {
return String.format("%s%s%s%s%s",
toBase64(salt),
DELIMITER,
toBase64(iv),
DELIMITER,
toBase64(cipherText));
}
return String.format("%s%s%s",
toBase64(iv),
DELIMITER,
toBase64(cipherText));
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static String decrypt(String ciphertext, String password) {
String[] fields = ciphertext.split(DELIMITER);
if(fields.length != 3) {
throw new IllegalArgumentException("Invalid encypted text format");
}
byte[] salt = fromBase64(fields[0]);
byte[] iv = fromBase64(fields[1]);
byte[] cipherBytes = fromBase64(fields[2]);
SecretKey key = deriveKey(password, salt);
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
byte[] plaintext = cipher.doFinal(cipherBytes);
return new String(plaintext, "UTF-8");
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
private static byte[] generateSalt() {
byte[] b = new byte[PKCS5_SALT_LENGTH];
random.nextBytes(b);
return b;
}
private static byte[] generateIv(int length) {
byte[] b = new byte[length];
random.nextBytes(b);
return b;
}
private static SecretKey deriveKey(String password, byte[] salt) {
try {
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBKDF2_DERIVATION_ALGORITHM);
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
return new SecretKeySpec(keyBytes, "AES");
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
private static String toBase64(byte[] bytes) {
return Base64.encodeToString(bytes, Base64.NO_WRAP);
}
private static byte[] fromBase64(String base64) {
return Base64.decode(base64, Base64.NO_WRAP);
}
Source
The problem is that with the new provider, the following snippet of code
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
secrand.setSeed(seed.getBytes());
keygen.init(128, secrand);
SecretKey seckey = keygen.generateKey();
byte[] rawKey = seckey.getEncoded();
generates a different, genuinely random rawKey every time it's executed. So, you're trying to decrypt with a key different from the one used to encrypt data and you get the exception. You won't be able to recover your key or data when it has been generated this way, and only the seed has been saved.
What fixed it for me (as #Giorgio suggested) was just replacing this:
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
with this:
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG", "Crypto");
I am unable to give you answer to your asked question but I'd simply try to work this around >- if you face some problems with bouncycastle across devices/OS version, you should ditch built-in versions completely and instead add bouncycastle as jar to your project, change your import to point to that jar, rebuild and assuming it all works you'd be immune to android built-in version changes from now on.
Because all of this didn't help me to generate an encrypted password which was deterministic on all android devices (>=2.1), I searched for another AES implementation. I found one which works for me on all devices. I'm not a security specialist, I'm not sure if the technique isn't as secure as it could be. I'm only posting the code for people who have run in the same problem that I had face before.
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import android.util.Log;
public class EncodeDecodeAES {
private static final String TAG_DEBUG = "TAG";
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!)
private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)
public EncodeDecodeAES() {
ivspec = new IvParameterSpec(iv.getBytes());
keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (GeneralSecurityException e) {
Log.d(TAG_DEBUG, e.getMessage());
}
}
public byte[] encrypt(String text) throws Exception {
if (text == null || text.length() == 0)
throw new Exception("Empty string");
byte[] encrypted = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e) {
Log.d(TAG_DEBUG, e.getMessage());
throw new Exception("[encrypt] " + e.getMessage());
}
return encrypted;
}
public byte[] decrypt(String code) throws Exception {
if (code == null || code.length() == 0)
throw new Exception("Empty string");
byte[] decrypted = null;
try {
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
} catch (Exception e) {
Log.d(TAG_DEBUG, e.getMessage());
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
public static String bytesToHex(byte[] data) {
if (data == null) {
return null;
}
int len = data.length;
String str = "";
for (int i = 0; i < len; i++) {
if ((data[i] & 0xFF) < 16)
str = str + "0" + java.lang.Integer.toHexString(data[i] & 0xFF);
else
str = str + java.lang.Integer.toHexString(data[i] & 0xFF);
}
return str;
}
public static byte[] hexToBytes(String str) {
if (str == null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i = 0; i < len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i * 2, i * 2 + 2), 16);
}
return buffer;
}
}
private static String padString(String source) {
char paddingChar = ' ';
int size = 16;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++) {
source += paddingChar;
}
return source;
}
}
You can use it like:
EncodeDecodeAES aes = new EncodeDecodeAES ();
/* Encrypt */
String encrypted = EncodeDecodeAES.bytesToHex(aes.encrypt("Text to Encrypt"));
/* Decrypt */
String decrypted = new String(aes.decrypt(encrypted));
Source: HERE
It's does have to do with the seed indeed and it's also should use multiple of 8 (like 8, 16, 24 or 32), try complete the seed with A's and B's or 1's and 0s (has to be something like this ABAB..., because AAA.. or BBB.. will not work also.) up to reach a multiple of 8 number. There is an other thing if you are reading and encrypting only bytes, (not converting it to Char64 as I did), then you need an appropriate PKCS5 or PKCS7 Padding, however in your case (due only 128bits and it's has been created with older versions of Android) PKCS5 would be enough, though you also should put it in your SecreteKeySpec something like "AES/CBC/PKCS5Padding" or "AES/ECB/PKCS5Padding" rather than just "AES", because Android 4.2 it's using PKCS7Padding as default and if it's only bytes you really need the same algorithm that was the default before. Try get a device with an Android earlier than 4.2 check the Object tree on your "keygen.init(128, secrand);" if I'm not mistaken it's has the label cipher, than use it.
Give it a try.

Categories

Resources