40

I want to encrypt image from the sd card and store it again in SD card again using AES. The main idea is the application browse an image, then encrypt it when I push a button, then store it in sd card. so my image would be secure.

I already succeed do string encryption using AES from this tutorial http://www.androidsnippets.com/encryptdecrypt-strings, but I don't have idea how to do this with an image, not string.

This is how I do it with a string:

public static String encrypt(String seed, String cleartext) throws Exception  
{
    byte[] rawKey = getRawKey(seed.getBytes());
    byte[] result = encrypt(rawKey, cleartext.getBytes()); 
    return toHex(result);
}

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;
}

Can anyone help me give example code how to encrypt an image with AES?

maybe it must using I/O file stream but I don't have an idea how to implement with this code.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
user1421273
  • 403
  • 1
  • 5
  • 5
  • Don't use that snippet. It's one of the worst out there. I tried to contact the authors & website, but I could not get in. Terrible example. – Maarten Bodewes May 28 '12 at 23:15
  • @user1421273 can u pls tell me how can i encrypt image files or folder that contains images – Erum Dec 15 '14 at 04:56
  • @user1421273 if i encrypt image files then can i share those image files after decryption directly or may i need to save decrypted image files on sdcard and then share it from app ? – Erum Dec 15 '14 at 05:03
  • @user1421273 can u pls reply me – user3233280 Dec 22 '14 at 18:22
  • @MaartenBodewes I'm seeing you say this code snippet is not secure and safe, I got it, thanks. But why don't you write more secure and simple example instead of commenting? – Lazy Aug 24 '15 at 17:57

2 Answers2

73

If you take user input for the password make sure to read this answer.

You should take a look at: CipherInputStream and CipherOutputStream. They are used to encrypt and decrypt byte streams.

I have a file named cleartext. The file contains:

Hi, I'm a clear text.
How are you?
That's awesome!

Now, you have an encrypt() function:

static void encrypt() throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
    // Here you read the cleartext.
    FileInputStream fis = new FileInputStream("data/cleartext");
    // This stream write the encrypted text. This stream will be wrapped by another stream.
    FileOutputStream fos = new FileOutputStream("data/encrypted");

    // Length is 16 byte
    // Careful when taking user input!!! https://stackoverflow.com/a/3452620/1188357
    SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(), "AES");
    // Create cipher
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, sks);
    // Wrap the output stream
    CipherOutputStream cos = new CipherOutputStream(fos, cipher);
    // Write bytes
    int b;
    byte[] d = new byte[8];
    while((b = fis.read(d)) != -1) {
        cos.write(d, 0, b);
    }
    // Flush and close streams.
    cos.flush();
    cos.close();
    fis.close();
}

After you execute this function, there should be a file names encrypted. The file contains the encrypted characters.

For decryption you have the decrypt function:

static void decrypt() throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
    FileInputStream fis = new FileInputStream("data/encrypted");

    FileOutputStream fos = new FileOutputStream("data/decrypted");
    SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(), "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, sks);
    CipherInputStream cis = new CipherInputStream(fis, cipher);
    int b;
    byte[] d = new byte[8];
    while((b = cis.read(d)) != -1) {
        fos.write(d, 0, b);
    }
    fos.flush();
    fos.close();
    cis.close();
}

After the execution of decrypt, there should be a file named decrypted. This file contains the free text.

You write you're a "noob" but depending on the use-case of encryption you could do a lot of harm if you're not doing it the right way. Know your tools!

Usage of CipherOutputStream Oracle documentation:

SecretKeySpec skeySpec = new SecretKeySpec(y.getBytes(), "AES");

FileInputStream fis;
FileOutputStream fos;
CipherOutputStream cos;
// File you are reading from
fis = new FileInputStream("/tmp/a.txt");
// File output
fos = new FileOutputStream("/tmp/b.txt");

// Here the file is encrypted. The cipher1 has to be created.
// Key Length should be 128, 192 or 256 bit => i.e. 16 byte
SecretKeySpec skeySpec = new SecretKeySpec("MyDifficultPassw".getBytes(), "AES"); 
Cipher cipher1 = Cipher.getInstance("AES");  
cipher1.init(Cipher.ENCRYPT_MODE, skeySpec);
cos = new CipherOutputStream(fos, cipher1);
// Here you read from the file in fis and write to cos.
byte[] b = new byte[8];
int i = fis.read(b);
while (i != -1) {
    cos.write(b, 0, i);
    i = fis.read(b);
}
cos.flush();

Thus, the encryption should work. When you reverse the process, you should be able to read the decrypted bytes.

Community
  • 1
  • 1
Kiril
  • 6,009
  • 13
  • 57
  • 77
  • Thank you for the answer Kiril. I still dont have an idea how to implement this using CipherInputStream. Could you give me a few example for working with that? – user1421273 May 28 '12 at 09:50
  • i already edit the source, it contained no error but still cant encrypt the file. anything wrong with my code Kiril? – user1421273 May 29 '12 at 05:00
  • Hi, I'll provide a clean snippet! But check the length of your SecretKey. Should be i.e. 16 byte. => 16 String characters. – Kiril May 29 '12 at 12:36
  • hi Kiril, i follow your update source and i found this error: java.io.FileNotFoundException: /data/encrypted (Permission denied) – user1421273 Jun 04 '12 at 02:07
  • 4
    edited: i try to edit permission (android.permission.WRITE_EXTERNAL_STORAGE) in android manifest and the encryption/decryption works. Thank you Kiril :) – user1421273 Jun 04 '12 at 02:29
  • Um, yeah! Forgot about the Android aspect! Glad I could help. – Kiril Jun 07 '12 at 09:01
  • 1
    What about encrypting large file such as video or audio? With 8 bytes buffer it will last forever – Manza Jul 16 '14 at 15:42
  • 1
    When we decrypt the file, wouldn't it be somewhere on the SD card? Isn't it unsafe? What if someone wants on his app should be able to read the file? – Adil Malik Sep 27 '14 at 15:13
  • how can i encrypt folder that contains images? @Kiril – user3233280 Dec 14 '14 at 09:08
  • @Kiril can u pls tell me how can i encrypt images using its path – user3233280 Dec 22 '14 at 18:20
  • 1
    thank for posting such a nice answer.But is it possible to check file ("MyDifficultPassw" ) password before decryption ??? I am getting exception if using wrong password after decryption,so it will take too much time to know.Is there is anyway to verify password quickly without decryption before???...Thanks – Ashish Sahu Feb 27 '15 at 19:46
  • Does this work when decrypting from another device than the one that encrypted the content? – Benjamin Piette Mar 03 '15 at 17:21
  • i want to encrypt some audio files. its for a diary app. is it wise to play as decrypting ? – hadi Sep 28 '15 at 07:40
  • 2
    General advice: **Always use a fully qualified Cipher string.** `Cipher.getInstance("AES");` may result in different ciphers depending on the default security provider. It most likely results in `"AES/ECB/PKCS5Padding"`, but it doesn't have to be. If it changes, you'll lose compatibility between different JVMs. – Artjom B. Jun 14 '16 at 17:11
  • 1
    **Never use [ECB mode](http://crypto.stackexchange.com/q/14487/13022)**. It's deterministic and therefore not semantically secure. You should at the very least use a randomized mode like [CBC](http://crypto.stackexchange.com/q/22260/13022) or [CTR](http://crypto.stackexchange.com/a/2378/13022). It is better to authenticate your ciphertexts so that attacks like a [padding oracle attack](http://crypto.stackexchange.com/q/18185/13022) are not possible. This can be done with authenticated modes like GCM or EAX, or with an [encrypt-then-MAC](http://crypto.stackexchange.com/q/202/13022) scheme. – Artjom B. Jun 14 '16 at 17:11
  • @ArtjomB can you show us how your suggestion should be in code snippet? – HendraWD Aug 07 '17 at 12:08
  • 1
    @HendraWD [Here](https://stackoverflow.com/a/44891805/1816580) is some of my code without authentication. I would suggest the usage of a library like [tozny/java-aes-crypto](https://github.com/tozny/java-aes-crypto) which uses encrypt-then-MAC and is secure. – Artjom B. Aug 07 '17 at 17:18
  • I tried encrypting using it but the encrypted file size is limited to 16byte. How can I encrypt large file like 4-5 mb. Please help – MrinmoyMk Mar 31 '20 at 07:09
-1

Starting android 10, there have been huge storage restrictions, so DocumentFile is going to be common compared to File class. So I am also providing answer using Uri.

fun Activity.encrypt(curLocUri: Uri, newLocUri: Uri, password: String, salt: String) : Boolean{
    // opening file input/outputStreams
    val fis = contentResolver.openInputStream(curLocUri) ?: return false
    val fos = contentResolver.openOutputStream(newLocUri) ?: return false

    try {
        var key: ByteArray = ("$salt$password").toByteArray(Charsets.UTF_8)
        val sha: MessageDigest = MessageDigest.getInstance("SHA-1")
        key = sha.digest(key)
        key = key.copyOf(16)
        val sks = SecretKeySpec(key, "AES")
        val cipher: Cipher = Cipher.getInstance("AES")
        cipher.init(Cipher.ENCRYPT_MODE, sks)
        val cos = CipherOutputStream(fos, cipher)
        var b: Int
        val d = ByteArray(8)
        while (fis.read(d).also { b = it } != -1) {
            cos.write(d, 0, b)
        }
        cos.flush()
        cos.close()
        return true
    } catch (e: Throwable){
        fis.close()
        fos.close()
        return false
    }
}

fun Activity.decrypt(curLocUri: Uri, newLocUri: Uri, password: String, salt: String): Boolean {
    // opening file input/outputStreams
    val fis = contentResolver.openInputStream(curLocUri) ?: return false
    val fos = contentResolver.openOutputStream(newLocUri) ?: return false

    try {
        var key: ByteArray = ("$salt$password").toByteArray(Charsets.UTF_8)
        val sha = MessageDigest.getInstance("SHA-1")
        key = sha.digest(key)
        key = Arrays.copyOf(key, 16)
        val sks = SecretKeySpec(key, "AES")
        val cipher = Cipher.getInstance("AES")
        cipher.init(Cipher.DECRYPT_MODE, sks)
        val cis = CipherInputStream(fis, cipher)
        var b: Int
        val d = ByteArray(8)
        while (cis.read(d).also { b = it } != -1) {
            fos.write(d, 0, b)
        }
        fos.flush()
        fos.close()
        cis.close()
        return true
    } catch (e: Throwable){
        fos.flush()
        fos.close()
        return false
    }
}

Here Uri s are the one received from file picker from onActivityResult.

For examples, you can find it here

Khamidjon Khamidov
  • 6,783
  • 6
  • 31
  • 62