I am using the following code to encrypt a file in sd card.
void encrypt() throws IOException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException {
String myString = getOutputFile();
File myFile = new File(myString);
FileInputStream inputStream = new FileInputStream(myFile);
File encodedfile = new File(path,"filename" + ".mp4");
FileOutputStream outputStream = new FileOutputStream(encodedfile);
SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, sks);
CipherOutputStream cos = new CipherOutputStream(outputStream, cipher);
int b;
byte[] d = new byte[8];
while((b = inputStream.read(d)) != -1){
cos.write(d, 0, b);
}
cos.flush();
cos.close();
inputStream.close();
As i am new to cryptography, i don't know whether am using 256 bit encryption. Am i using a 256 bit encryption. If not what code should i add to make it a 256 bit encryption
No, you are using 128 bit encryption, as your password is 16 ASCII characters. Combine that with the default character set of Android (UTF-8) and the result of getBytes() will be key data of 16 bytes, or 128 bits.
Not that it matters if it is 128 bit or 256 bit. If you directly store the password or key in your code, use a password as key or if you rely on the default ECB mode of encryption, then your code is not secure.
Learn about key management, use at least CBC mode encryption and create a fully random AES key of 256 bits (32 bytes). The AES keysize (as used within Cipher) fully depends on the key in Java / Android.
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 am googling and testing solution for a while and so far no success. There is always some problem with it. Following code is "working" (meaning do not show any error while it is running) on Android Kitkat and higher, but decrypted files are not readable. Why?
final static byte[] iv = new byte[16];//ADDED
final static int buffer = 102400;
final static String encryptionType = "AES/CFB8/NoPadding";//CHANGED TO DIFFERENT TYPE
static void encrypt(String password, File fileInput, File fileOutput) throws Exception {
IvParameterSpec ivParams = new IvParameterSpec(iv);//ADDED
FileInputStream fis = new FileInputStream(fileInput);
FileOutputStream fos = new FileOutputStream(fileOutput);
SecretKeySpec sks = new SecretKeySpec(password.getBytes("UTF-8"), encryptionType);
Cipher cipher = Cipher.getInstance(encryptionType);
//cipher.init(Cipher.ENCRYPT_MODE, sks);REPLACED
cipher.init(Cipher.ENCRYPT_MODE, sks, ivParams);
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
int b;
byte[] d = new byte[buffer];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
cos.flush();
cos.close();
fis.close();
}
static void decrypt(String password, File fileInput, File fileOutput) throws Exception {
IvParameterSpec ivParams = new IvParameterSpec(iv);//ADDED
FileInputStream fis = new FileInputStream(fileInput);
FileOutputStream fos = new FileOutputStream(fileOutput);
SecretKeySpec sks = new SecretKeySpec(password.getBytes("UTF-8"), encryptionType);
Cipher cipher = Cipher.getInstance(encryptionType);
//cipher.init(Cipher.ENCRYPT_MODE, sks);REPLACED
cipher.init(Cipher.DECRYPT_MODE, sks, ivParams);
CipherInputStream cis = new CipherInputStream(fis, cipher);
int b;
byte[] d = new byte[buffer];
while ((b = cis.read(d)) != -1) {
fos.write(d, 0, b);
}
fos.flush();
fos.close();
cis.close();
}
EDIT: After I changed type to "AES/CFB8/NoPadding", it seems to be ok, there is no error in process, but decrypted file is not readable.
The problem in the decrypt method is caused by this line:
cipher.init(Cipher.ENCRYPT_MODE, sks);
the mode needs to Cipher.DECRYPT_MODE, so the line should be
cipher.init(Cipher.DECRYPT_MODE, sks);
Other issues are the use of the long obsolete DESede algorithm, the lack of any IV generation and handling, the absence of a good password-based key derivation algorithm, and the lack of any MAC on the ciphertext. Correctly using AES GCM mode with proper nonce generation and handling, and use of PBKDF2 (which is available on Android and Oracle Java) would represent significant improvements.
You don't supply an IV, so one is generated for you automatically. You must find a way to transmit this IV to the recipient. Typically the IV/Nonce is prepending to the ciphertext and stripped off by the recipient in order to decrypt the data. CipherInputStream/CipherOutputStream does not do this for you, so you must do it on your own.
I finally solve this problem by using shorter password. I am not sure why, but on Android 7 and 8, there is no problem with long password, but same password on Android 4.4 leads to crazy errors and brake encryption.
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 :(
hi guys is there any simple way to encrypt and decrypt the images across platforms like decrypting the image in android encrypted in the iPhone and vise-versa.
Thanks in Advance..
You can use 56 bit DES encryption. It is supported both in iphone and android. You cannot use RSA because image may be larger than 127 byte. Two years before when I was trying with AES 128 bit encryption. I found there was limitation of using AES 128 bit encryption and put it in market place. So avoid AES also. java supprots AES. Hence nadorid also supports DES
AES encryption is the best way encrypt a file in android or in IOS.In android I have tried encryption.This link will help you to do in android .The below code will help tou to encrypt a byte array with the key in android.
encryptionKey will be your password
public static byte[] encrypt(byte[] key, byte[] data) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(data);
return encrypted;
}
/**
* DEcrypt byte array with given Key using AES Algorithm
* Key can be generated using <Code>getKey()</Code>
* #param key Key that Is used for decrypting data
* #param data Data passed to decrypt
* #return decrypted data
* */
public static byte[] decrypt1(byte[] key, byte[] encrypted) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
/**
* get the Key for encryption this can be used for while decrypting and encrypting too.
* */
public static byte[] getKey() throws Exception
{
byte[] keyStart = EncrypteDecrypte.encryptionKey.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();
return key;
}
I want to encrypt/decrypt some passwords in the SQLite database of my application. To do that I have searched on the internet and I have found the AES algorithm.
I have this code:
public String encript(String dataToEncrypt)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
// I'm using AES encription
if(!dataToEncrypt.equals("")){
String key = "FMVWf8d_sm#fz";
Cipher c = Cipher.getInstance("AES");
SecretKeySpec k;
try {
k = new SecretKeySpec(key.getBytes(), "AES");
c.init(Cipher.ENCRYPT_MODE, k);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return new String(c.doFinal(Base64.decode(dataToEncrypt)));
}
return "";
}
public String decript(String encryptedData)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
if(!encryptedData.equals("")){
String key = "FMVWf8d_sm#fz";
Cipher c = Cipher.getInstance("AES");
SecretKeySpec k = new SecretKeySpec(Base64.decode(key), "AES");
c.init(Cipher.DECRYPT_MODE, k);
return new String(c.doFinal(Base64.decode(encryptedData)));
}
return "";
}
After running this I get this error on encrypt method:
01-27 14:50:51.698: ERROR/ACTIVITY(782):
java.security.InvalidKeyException: Key length not 128/192/256 bits.
I have seen some other cases here on stackoverflow but I want to give the key to the AES not to generate it...
Can somebody help me with this? If there is other encryption method to use but without using another jars or external classes and to let me give the key.
Thank you very much!
The error message makes it perfectly clear: your encryption key must be of certain size: 128, 192 or 256 bits. And your key is 104 bits. Note, that as you want to use only printable characters in your key, the length of the key should be 192 or longer bits, cause your alphabet (set of characters that you use) makes encryption weaker.
Usual practice is such:
Get password (in your case String key = "FMVWf8d_sm#fz";)
Generate using some hash function key with length 128, 192 or 256
Put it into encryption algorithm
Have fun
So you are missing key generation stage. Do smth like:
// Get the KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted =
cipher.doFinal((args.length == 0 ?
"This is just an example" : args[0]).getBytes());
System.out.println("encrypted string: " + asHex(encrypted));
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] original =
cipher.doFinal(encrypted);
String originalString = new String(original);
System.out.println("Original string: " +
originalString + " " + asHex(original));