How to encrypt and decrypt file in Android? - android

I want to encrypt file and store it in SD card. I want to decrypt that encrypted file and store it in SD card again. I have tried to encrypt file by opening it as file stream and encrypt it but it is not working. I want some idea on how to do this.

Use a CipherOutputStream or CipherInputStream with a Cipher and your FileInputStream / FileOutputStream.
I would suggest something like Cipher.getInstance("AES/CBC/PKCS5Padding") for creating the Cipher class. CBC mode is secure and does not have the vulnerabilities of ECB mode for non-random plaintexts. It should be present in any generic cryptographic library, ensuring high compatibility.
Don't forget to use a Initialization Vector (IV) generated by a secure random generator if you want to encrypt multiple files with the same key. You can prefix the plain IV at the start of the ciphertext. It is always exactly one block (16 bytes) in size.
If you want to use a password, please make sure you do use a good key derivation mechanism (look up password based encryption or password based key derivation). PBKDF2 is the most commonly used Password Based Key Derivation scheme and it is present in most Java runtimes, including Android. Note that SHA-1 is a bit outdated hash function, but it should be fine in PBKDF2, and does currently present the most compatible option.
Always specify the character encoding when encoding/decoding strings, or you'll be in trouble when the platform encoding differs from the previous one. In other words, don't use String.getBytes() but use String.getBytes(StandardCharsets.UTF_8).
To make it more secure, please add cryptographic integrity and authenticity by adding a secure checksum (MAC or HMAC) over the ciphertext and IV, preferably using a different key. Without an authentication tag the ciphertext may be changed in such a way that the change cannot be detected.
Be warned that CipherInputStream may not report BadPaddingException, this includes BadPaddingException generated for authenticated ciphers such as GCM. This would make the streams incompatible and insecure for these kind of authenticated ciphers.

I had a similar problem and for encrypt/decrypt i came up with this solution:
public static byte[] generateKey(String password) throws Exception
{
byte[] keyStart = password.getBytes("UTF-8");
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
return skey.getEncoded();
}
public static byte[] encodeFile(byte[] key, byte[] fileData) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(fileData);
return encrypted;
}
public static byte[] decodeFile(byte[] key, byte[] fileData) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(fileData);
return decrypted;
}
To save a encrypted file to sd do:
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "your_folder_on_sd", "file_name");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
byte[] yourKey = generateKey("password");
byte[] filesBytes = encodeFile(yourKey, yourByteArrayContainigDataToEncrypt);
bos.write(fileBytes);
bos.flush();
bos.close();
To decode a file use:
byte[] yourKey = generateKey("password");
byte[] decodedData = decodeFile(yourKey, bytesOfYourFile);
For reading in a file to a byte Array there a different way out there. A Example: http://examples.javacodegeeks.com/core-java/io/fileinputstream/read-file-in-byte-array-with-fileinputstream/

You could use java-aes-crypto or Facebook's Conceal
java-aes-crypto
Quoting from the repo
A simple Android class for encrypting & decrypting strings, aiming to
avoid the classic mistakes that most such classes suffer from.
Facebook's conceal
Quoting from the repo
Conceal provides easy Android APIs for performing fast encryption and
authentication of data

Related

Using AES with AndroidKeyStore

Android M provides AES support via the AndroidKeyStore however I can not find any combination of generating a key that provides a method of full encryption and decryption without a user password/device locking. It appears that my current approach is appropriate for these requirements as the key store is storing my key, I can load the key and perform encryption and in the case that I hold onto the IV from the encryption process I can decrypt the data.
Unfortunately in the real world use case I can not hold onto the IV for decryption at a later time without writing it to disk, maybe this is what I should be doing?
I have glanced at the updated key store and related testing in the SDK but was unable to find any test cases I could use as example. The examples also seem to be devoid of actually using AndroidKeyStore generated SecretKeys without tying them to device locking/finger prints.
I have created a repository to try and highlight what I have done along with some comments explaining where my issue is. The relevant code is also included below.
For clarity, my question is simply how can I generate an AndroidKeyStore backed AES SecretKey that allows me to encrypt and decrypt via cipher input/output streams without writing the IV to disk or using the fingerprint/device locking approaches?
https://github.com/ToxicBakery/AES-Testing/blob/master/app/src/androidTest/java/com/toxicbakery/app/aes/AesTest.java
final String suchAlphabet = "abcdefghijklmnopqrstuvwxyz";
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
/*
KEY GENERATION
*/
// Define the key spec
KeyGenParameterSpec aesSpec = new KeyGenParameterSpec.Builder(ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setKeySize(128)
.build();
// Create the secret key in the key store
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
keyGenerator.init(aesSpec);
keyGenerator.generateKey();
Cipher cipher;
SecretKey secretKey;
/*
ENCRYPTION
*/
// Load the secret key and encrypt
secretKey = ((KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null)).getSecretKey();
cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
cipherOutputStream.write(suchAlphabet.getBytes());
cipherOutputStream.flush();
cipherOutputStream.close();
/*
DECRYPTION
*/
// Load the secret key and decrypt
secretKey = ((KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null)).getSecretKey();
// The following two lines attempt to represent real world usage in that the previous line loaded
// the key from the store and the next two lines attempt to create the cipher and then initialize
// the cipher such that an IV can be extracted as it does not seem that you can use the spec or the
// parameters. Interestingly, the following two lines only 'half' such that a-p fail to decrypt and
// q-z decrypt successfully 100% of the time. Leaving the lines commented results an in a successful
// decryption of the alphabet but this is not a usable scenario
//
// cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
// cipher.init(Cipher.ENCRYPT_MODE, secretKey);
IvParameterSpec ivParameterSpec = new IvParameterSpec(cipher.getIV());
cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] in = new byte[suchAlphabet.getBytes().length];
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
IOUtils.readFully(cipherInputStream, in);
cipherInputStream.close();
/*
VERIFY
*/
String muchWow = new String(in);
assertEquals(suchAlphabet, muchWow);
Alex's answer above is the best one, but note that there is another option: Set the IV yourself to a fixed value, or a value you can consistently derive. Encrypting a given piece of data more than once with the same IV is insecure, so AndroidKeyStore discourages it. But, if you are sure you want to, you can use setRandomizedEncryptionRequired, like:
KeyGenParameterSpec aesSpec = new KeyGenParameterSpec.Builder(ALIAS, //...
// ...
.setRandomizedEncryptionRequired(false)
.build()
to allow you to provide the IV, then in the Cipher init call you can add third argument, an IvParameterSpec object. For example:
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(myIv));
Do the same for decryption, with the same IV value, and it will decrypt correctly.
To reiterate, this approach is NOT RECOMMENDED. Unless you understand exactly why it's a bad idea and have very specific reason to know it's okay in your case, it's better to let the keystore generate a random IV. It's usually not hard to find a place to store the IV with the ciphertext.
Save IV next to the ciphertext and then use the IV to decrypt the ciphertext later. Why is it that in your real world scenario you cannot save the IV persistently?

Is it possible to access a runtime decrypted file in android?

I want to decrypt a file stored at my app's res folder. This file is distributed with the app, and I'm trying to decrypt it only once during app start.
So far, I've found some answers (this one, for instance) about how to write the decrypted file into sdcard, but won't that file be available to malicious access at the sdcard?
I wish I could write the CipherInputStream into a java.io.InputStream, so I could use it without writing any decrypted data to disk. Is it possible?
I think you want something like this
private InputStream getDecodedInputStream (InputStream eis) {
Cipher cipher = Cipher.getInstance("your cipher definition");
cipher.init(Cipher.DECRYPT_MODE, "your keySpec", new IvParameterSpec("your IV parameter spec"));
InputStream decryptedInputStream = new CipherInputStream(is, cipher);
return decryptedInputStream;
}
where eis is your encrypted input stream

Encrypt data in Android and decrypt in Ruby using AESCrypt

I'm trying to encrypt a JSON string in Android and decrypt it in Ruby using AESCrypt.
AESCrypt.decrypt(dataToDecrypt, secret)
With this Java code I could decrypt second half of the data!
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(secret.getBytes("UTF-8"));
byte[] digest = md.digest();
SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = null;
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, newKey);
byte[] encryptedData = cipher.doFinal(textBytes);
String encryptedDataStr = Base64.encodeToString(encryptedData, Base64.DEFAULT)
Raw data is:
{"device_id":"863438021956196","imei":"863438021956196"}
And decrypted data in Ruby is:
\xEE\x99\x95\xC5p\x17\x8A\xFB\xF0\xA5\xC7\x1D7\x98\xBD\xD93438021956196\",\"imei\":\"863438021956196\"}
What is the problem?
CBC mode requires an IV. By failing to explicitly specify one you are relying on defaults. It looks like the default Java IV is different than the default IV for the Ruby code. Don't use defaults.
I had this same issue, as #GreyS mentions the default IV's are different, AFAIK Java creates a random IV if you don't supply one. Also pretty sure AESCrypt uses PKCS7Padding. In any case, to help to work between AESCrypt and Android I recently created AESCrypt-Android

Copying bytes from FileInputStream to CipherOutputStream (or vice versa) extremely slow

I have been following the tutorial "How do I encrypt and decrypt files using DES?" to add a simple file encryption to an existing Android 4+ app.
Everything works fine, except that the encryption and decryption is very, very slow.
The following two methods are pretty much the complete tutorial:
public static void encryptOrDecrypt(String key, int mode, InputStream is, OutputStream os) throws Throwable {
DESKeySpec dks = new DESKeySpec(key.getBytes());
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey desKey = skf.generateSecret(dks);
Cipher cipher = Cipher.getInstance("DES"); // DES/ECB/PKCS5Padding for SunJCE
if (mode == Cipher.ENCRYPT_MODE) {
cipher.init(Cipher.ENCRYPT_MODE, desKey);
CipherInputStream cis = new CipherInputStream(is, cipher);
doCopy(cis, os);
} else if (mode == Cipher.DECRYPT_MODE) {
cipher.init(Cipher.DECRYPT_MODE, desKey);
CipherOutputStream cos = new CipherOutputStream(os, cipher);
doCopy(is, cos);
}
}
public static void doCopy(InputStream is, OutputStream os) throws IOException {
byte[] bytes = new byte[64];
int numBytes;
// Why is this taking so long?
while ((numBytes = is.read(bytes)) != -1) {
os.write(bytes, 0, numBytes);
}
os.flush();
os.close();
is.close();
}
Quite simple and basic, but it takes about 20-30 seconds to de/encrypt a 1 MB file. In detail it is the while-loop that copies the bytes between the two streams which is so slow.
Changing the size of the byte[] to a bigger value like 65536 to read more bytes at once does not change anything. I thought reading more bytes at once would speed up the process but this is not the case.
Copying data between "normal" streams without encryption does not take so long. Is it really the encryption that is this expensive? I have been using similar encryptions on other plattforms and such delays have never been a problem.
The tutorial uses DES but changing the algorithm to something different, e.g. AES does not change anything either.
Any ideas how I can speed this up?
I ran some experiments on my Google LG Nexus 5 running Android 4.4.4 using basically your code and encrypting a file of 1000000 (one million) bytes, reading and writing to/from the /sdcard filesystem.
no crypt 1421 ms
AES ECB mode 2577 ms
DES ECB 3214
Next, I modified your code slightly to use BufferedInputStream and BufferedOutputStream
no crypt 88 ms
AES ECB mode 855 ms
DES ECB mode 1419 MS
This shows that the timings are sensitive to buffering, and that AES is faster than DES.
The provider name is 'AndroidOpenSSL'
You might have a problem some process blocking the input file. To test if the issue is this : Calculate the last part of the file that should be read and the count of full buffers that you will read. Replace the while with a for loop and add an additional read and write if there is remainder from filesize/buffer size.

Android: decrypt RSA text using a Public key stored in a file

I've been several days trying to do it without success.
There are plenty of similar questions here in StackOverflow and even two of them are exactly the same as mine but unanswered and unresolved:
1) Convert PHP RSA PublicKey into Android PublicKey
2) Android: how to decrypt an openssl encrypted file with RSA key?
My scenario:
I have some text encrypted using RSA (not encrypted by me). I have a "public.key" file in my res/raw folder with the public key needed to decrypt it (the public key related to the private key used to encrypt the message), with a format like the following example:
I see a lot of examples of how to decrypt a RSA text, like the following one:
public static byte[] decryptRSA( PublicKey key, byte[] text) throws Exception
{
byte[] dectyptedText = null;
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(text);
return dectyptedText;
}
But my question is, how to get the proper PublicKey instance from the file? No examples of this.
If I simply try:
InputStream is = getResources().openRawResource(R.raw.public);
DataInputStream dis = new DataInputStream(is);
byte [] keyBytes = new byte [(int) is.available()];
dis.readFully(keyBytes);
dis.close();
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(spec);
I get an InvalidKeyException in the return sentence.
Would I need to decode Hex or Base64? Aren't the first and last lines of the public key file a problem (the ones with "----BEGIN PUBLIC KEY----" and so)?
Maybe we could get the answer of this properly for the first time in StackOverflow:-)
Finally solved!!! Drums, trumpets and a symphony of enchanting sounds!!!
public static byte[] decryptRSA(Context mContext, byte[] message) throws Exception {
// reads the public key stored in a file
InputStream is = mContext.getResources().openRawResource(R.raw.sm_public);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
List<String> lines = new ArrayList<String>();
String line = null;
while ((line = br.readLine()) != null)
lines.add(line);
// removes the first and last lines of the file (comments)
if (lines.size() > 1 && lines.get(0).startsWith("-----") && lines.get(lines.size()-1).startsWith("-----")) {
lines.remove(0);
lines.remove(lines.size()-1);
}
// concats the remaining lines to a single String
StringBuilder sb = new StringBuilder();
for (String aLine: lines)
sb.append(aLine);
String keyString = sb.toString();
Log.d("log", "keyString:"+keyString);
// converts the String to a PublicKey instance
byte[] keyBytes = Base64.decodeBase64(keyString.getBytes("utf-8"));
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(spec);
// decrypts the message
byte[] dectyptedText = null;
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(Base64.decodeBase64(message));
return dectyptedText;
}
The solution was to Base64 decode not only the public key read from the file, but also the crypted message itself!
By the way, I read the public key from the file the way #Nikolay suggested (tnx again man).
Thank you all very much for your help. StackOverflow rocks!
You are missing a key point -- public and private keys are separate, and you cannot calculate one based on the other. That is kind of the point of public key encryption. Issues with using raw RSA aside, if you have something encrypted with the public key, you need to have the corresponding private key to decrypt it. And vice versa. So if you have public key file, you can only get a public key from it. That would be only useful if your data was encrypted with the corresponding private key.
As for the actual exception: remove the '---' lines at the start and end, the use Base64.decode() to get a byte array, and use this to create your X509EncodedKeySpec. One way to do it -- use something like a BufferedReader to read line by line, ignore the '---' lines and concat the rest into one big String.
Public keys can only encrypt data. Public keys cannot decrypt data. You can only decrypt data using the private key. The whole point is you can hand out the public key to any and everyone and they can send you encrypted messages that only the holder of the private key can see.
You really need to be very careful with using encryption technology. I fear you are simply going to distribute the private key to all of your devices which will weaken your security as everyone will have the same private key. So if I want to crack your security I just go to google play and download your app and pull the private key out of your app. Viola I can see everything.
So you have your answer why it won't work, but you need advice now about design which I can't give you with knowing why you are using encryption. What are you hiding?
Update:
Sounds like you are trying to perform encryption AND signature verification like how RSA works, but you are confused how that actually works. For that you need TWO sets of private/public keys. One set of keys for the client and one set of keys for the server.
The web server would send its public key to the client. The client could send an authenticated and encrypted message to the server by using the server's public key and then signing that message using the client's private key. And vice versa for the server. The server would use the client's public key to encrypt a message and sign it with his private key to send a message to the client. The client could then decrypt the message with the client's private key and verify the signature using the server's public key.
Now are you re-implementing SSL? Stop it. Use SSL.
Here is how SSL achieves secure channel. The client receives the PUBLIC key from the web server, and 1 or more names for symmetric encryption algorithms. It picks an algorithm they share in common, then generates a secret key to use for all messages going forward. It ENCRYPTS that secret key with the web server's public key and sends that along with the algorithm it selected. The web server DECRYPTS using the PRIVATE key to get the shared secret key. After that all encryption is symmetric encryption using the shared secret which is much faster than asymmetric encryption.
To generate a RSA public key from a PEM format like you provided (openssl gen. rsa key)
-----BEGIN PUBLIC KEY-----
SOMEDES3UNREADABLETEXT+PADDING==
-----END PUBLIC KEY
and use it to read some content signed with it?
-take a look at my answer in a similar question here:
https://stackoverflow.com/a/12101100/546054

Categories

Resources