0

Well, I was about to encrypt a file from SDcard on user's order and Of course decrypt it back after that. well after a bit of searching I found out how to do it in android using "AES". everything was fine until users give me feedback about decryption Failure on Android Nougat! I checked and Unfortunately they were right :(

App was doing fine on Lollipop and Kitkat but not "NOUGAT"!

I checked the Log (as the first thing to do which comes to mind) and I saw this:

And I kind of freaked out because I was exactly using Crypto-Provider!!!

public static byte[] generateKey(String password) throws Exception {
    byte[] keyStart = password.getBytes("UTF-8");
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    SecureRandom random = SecureRandom.getInstance("[SHA1PRNG][1]", "Crypto");
    random.setSeed(keyStart);
    kgen.init(128, random);
    SecretKey skey = kgen.generateKey();
    return skey.getEncoded();
}

Then I find some Talks about this on SO: [1], [2]

developer site says we should no longer use crypto-provider for generation of Key for AES because:

This provider only provided an implementation of the algorithm “SHA1PRNG” for instances of SecureRandom. The problem is that the SHA1PRNG algorithm is not cryptographically strong.

and instead we should generate key like this:

public byte[] generateKeyForAES(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {
    /* 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  
    /* 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");

    return key.getEncoded();
}

As a good boy I replaced new way of generating key but it didn't work!!! (bad google) then I start to take a good look at this code and I realized that because every time we run this method, "salt" is generating "RANDOM"ly and we couldn't generate a same key twice. So you either save salt! after first time it is generated Or use an static salt. well I used an static salt :)

byte[] salt = {(byte) 0xA4, (byte) 0x0B, (byte) 0xC8, (byte) 0x34, (byte) 0xD6, (byte) 0x95, (byte) 0xF3, (byte) 0x13};

As you may guess, Yes it did NOT worked either! what I mean is it did decrypt the file but it was corrupted! NOT the same file was retrieved!

It's been a WEEK since I face this problem and gone through a LOT of way to solve it but none of them work.

There are a lot of Talks on SO which is about "being Unable to decrypt via AES" but there were either suggesting a way that didn't work or were irrelevant!

some says its a problem from Transformation Algorithm in initializing Cipher after generating key and instead of this:

Cipher cipher = Cipher.getInstance("AES");

we should do it like:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

but after that decryption even Fails to Decrypt from the beginning and it is worse than decrypting some corrupted file (with the same size of course)!

After I became really exhausted I think that forget about all Security risks and just make it work!!!
Then as @artjom-b say in his ANSWER I tried to import CryptoProvider class locally in my project and then even if Android delete it completely, it will work in my project!!!

I just copy CryptoProvider, SHA1PRNG_SecureRandomImpl and of course all related class and put it in a package in my source code, then ,back to earlier way that I use to generate key for AES, I change generate key method as follows:

public static byte[] generateKey(String password) throws Exception {
    byte[] keyStart = password.getBytes("UTF-8");
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    SecureRandom random = SecureRandom.getInstance("SHA1PRNG", new CryptoProvider());
    random.setSeed(keyStart);
    kgen.init(128, random);
    SecretKey skey = kgen.generateKey();
    return skey.getEncoded();
}

As you may noticed the way SucureRandom is being initialized is changed! and insted of "crypto" I reference to my local provider and turn it like:

SecureRandom random = SecureRandom.getInstance("SHA1PRNG", new CryptoProvider());

As the Serial of my failures in getting a Simple encryption on Android N to work follows, again it didn't work. I mean it did decrypting but the file was Corrupted again!

I really really appretiate any Help and I wonder If anyone face a same problem and knows how to fix it!?

Jalal Aghazadeh
  • 164
  • 4
  • 18
  • 2
    Your existing approach for deriving keys from passwords is a broken approach. When you switch to a new approach of deriving keys, don't expect the new keys to be able to decrypt files encrypted with keys derived with the old approach. – Ebbe M. Pedersen Nov 01 '17 at 09:25
  • @EbbeM.Pedersen if I get you corrent you mean that a file which is encrypted via "crypto" way of getting key won't be decrypted via new way of deriving key using salt! But I did both Encrypting and Decrypting Again using new Approach of deriving a Key. – Jalal Aghazadeh Nov 01 '17 at 11:20
  • 1
    How you have generated the keys, don't impact your capability to decrypt what you just encrypted. But do you actually derive the same key ? If you method for deriving the key involves a salt, you then need to use the same salt for that key each time, so that you generate the same key. – Ebbe M. Pedersen Nov 01 '17 at 14:42
  • @EbbeM.Pedersen Yes, I'm pretty sure that keys are the same because I use same salt (a fix array of bytes). – Jalal Aghazadeh Nov 01 '17 at 15:05

0 Answers0