I am thinking about a way I can "try to protect" my application data.
Initially I was doing the following:
I have an encryption key that I use to encrypt and decrypt the data received, as well as what will be stored, I used firebase for the backend.
Basically something like this:
private String encriptar(String datos, String password) throws Exception {
SecretKeySpec secretKey = generateKey(password);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] datosEncriptadosBytes = cipher.doFinal(datos.getBytes());
String datosEncriptadosString = Base64.encodeToString(datosEncriptadosBytes, Base64.DEFAULT);
return datosEncriptadosString;
}
private String desencriptar(String datos, String password) throws Exception {
SecretKeySpec secretKey = generateKey(password);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] datosDescoficados = Base64.decode(datos, Base64.DEFAULT);
byte[] datosDesencriptadosByte = cipher.doFinal(datosDescoficados);
String datosDesencriptadosString = new String(datosDesencriptadosByte);
return datosDesencriptadosString;
}
private SecretKeySpec generateKey(String password) throws Exception {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] key = password.getBytes("UTF-8");
key = sha.digest(key);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
return secretKey;
}
Well, you can see that in all three methods the password value is required for all methods, encryption, decryption and to generate the key.
I had planned to save this password as "Remote confing" in firebase and get it when I use it, but there are many calls to the server. The other option is to store it locally, but if I do it this way, the password would be plain text in a sharedpreferences, for example.
I am somewhat confused with how to approach this.
I am a rookie on the subject (as you may have seen) and would appreciate knowing if my approach is correct in keeping the key and what you would advise me.
Thank you very much in advance.
Firebase takes care of the encryption for you. The data you send in the network does not go through in plain text. To secure your database, consider adding Security Rules.
See https://firebase.google.com/docs/rules
Related
i'm working on Android application which requires to create secure channel between the app and the server(.NET), the first step is we creating shared secret, the app generates key pair (EC) and sends the public key to the server, then the server creates its own key pair and send the public key to the app and save the shared secret created based on app's public and server private keys and the server send back to the app its own public, salt and iv, then the app perform key agreement, the result is that both the android app and the server have string created based on public-private keys.
Later when the android app want to send some encrypted message to the server, i take the generated secret, salt and iv and creates secret key to be able use Cipher to encrypt the message.
The problem is even though the app and the server have exact the same shred secret, salt and iv for some reason android generates different SecretKey than the server (we checked in debug mode to see that the shared secret salt and iv are same).
Here is the code i use to create secret key:
byte[] sharedSecretBytes = Base64.decode(sharedSecretBase64, Base64.DEFAULT);
byte[] ivBytes = Base64.decode(ivBase64, Base64.DEFAULT);
byte[] saltBytes = Base64.decode(saltBase64, Base64.DEFAULT);
String sharedSecret = new String(sharedSecretBytes, "UTF-8");
//i tried to use different encoding, no luck.
//String sharedSecretAscii = new String(sharedSecretBytes, "ASCII");
char[] charArray = sharedSecret .toCharArray();
PBEKeySpec keySpec = new PBEKeySpec(charArray, saltBytes, 1000, 256);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
SecretKey key = secretKeyFactory.generateSecret(keySpec);
//here the keyBytes are not the same as generated in the server.
byte[] keyBytes = key.getEncoded();
//i saw in some tutorial somene do this, not sure why because we can provide the key as is to the cipher,
//the problem is that both not working.
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getEncoded(), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(logNumber.getBytes("UTF-8"));
String asString = Base64.encodeToString(encrypted, Base64.NO_WRAP);
I also made try to pub hard-coded shared secret as "123abc" in both android and server side and actually it worked so my only guess is that the problem coming from converting the sharedSecrte.toCharArray(), if the sharedSecret contains weird characters can lead java generating bad secret key?
Also we working on iOS app that make exactly same process and have no issue to generate correct keys.
In the end the only thing that worked as expected was to use Bouncy Castle library
What i done is :
PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
generator.init(sharedSecretBytes, saltBytes, 1000);
byte[] dk = ((KeyParameter) generator.generateDerivedParameters(256)).getKey();
SecretKeySpec secretKeySpec = new SecretKeySpec(dk, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(logNumber.getBytes("UTF-8"));
Still don't understand why native java.security makes trouble .
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.
How do I decrypt the string which already encrypted in the below sample, when I use below code I get exception
String transforation = "AES/CBC/PKCS7Padding";
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null);
// encrypt
Cipher cipher = Cipher.getInstance(transforation);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
String encriptedPassword = cipher.doFinal("Some Password".getBytes("UTF-8"));
// decrypt
cipher = Cipher.getInstance(transforation);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
String password = new String(cipher.doFinal(encriptedPassword), "UTF-8"));
if I am providing encryptCipher.getIV() I am able to decrypt
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(encryptCipher.getIV()));
but the problem is encryptCipher.getIV() is returning random key every time when I run the app.
The Cipher's init method's java doc says
If this cipher requires any algorithm parameters and params is null, the underlying cipher implementation is supposed to generate the required parameters itself (using provider-specific default or random values) if it is being initialized for encryption or key wrapping, and raise an InvalidAlgorithmParameterException if it is being initialized for decryption or key unwrapping. The generated parameters can be retrieved using getParameters or getIV (if the parameter is an IV).
So when you don't pass in the Algorithm parameters (3rd argument in the init method), it will generate a random parameters each time. So when you do the encryptionCipher.getIV(), it will return different each time.
If you need to use IV (advisable to use):
you can use a fixed value to initialize your cipher (not highly secure)
Or you can generate random IV each time (more secure) and store it along with your encrypted value, and parse it out when decrypting.
You need to record the IV used on the encryption, and provide it along with the encrypted data, so it's availabe when decrypting.
A simple way to do this, is to preprend the encryped data with the IV, and then obtain/remove it prior to decrypting. The IV don't need to be secret, but it should be different for each encrypt.
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 have developed a main app, now I need to develop a second app which is identical to the first but only perform 70% of the functionality. I have modularized the main app's functionality but now I want to be able to turn them on/off base on a configuration file. The values in this configuration file need to be accessible within the context of Activities and Services. A lot of people suggested using SharedPrefences, but I don't need end user to modify this. It's only meant for developer to configure these settings. What's the best approach for this?
Now in android data can be stored in these ways
Now you don’t want to store configuration in shared preference, So i would suggest you to go in 2 ways
Create a SQLite database that is accessible by both apps OR
Create a file that stores the configuration data and is accessible by both apps
Whatever the method that you choose to store the data encrypt it using some encryption algorithm like this
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;
}
And invoke them like this:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos); // bm is the bitmap object
byte[] b = baos.toByteArray();
byte[] keyStart = "this is a key".getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(keyStart);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
// encrypt
byte[] encryptedData = encrypt(key,b);
// decrypt
byte[] decryptedData = decrypt(key,encryptedData);
Now you can use the key to access these values and user would not be able to understand you configuration information
or you can use a web service to save data onto the server and your both apps can use that web service to get the configuration, It wont work offline though :(