AES encrypted files supporting Android 7.0

Keywords: Java Android less

Reprint by jqorz
Source: https://blog.csdn.net/baidu_27419681/article/details/81587232

Originally, I have sorted out a method for Android to encrypt files using AES. See AES encryption / decryption available on Android has been directly encapsulated as file encryption , but when it is used recently, it is found that the compiler will report an error and prompt

Then I looked for the information on the Internet and referred to it
Android: post-7.0 Crypto: an abandoned climbing Guide
, combined with the previous code, reorganized the AES file encryption available after Android 7.0.
Among them, the part of randomly generated key will be changed to the byte key obtained by password for convenience. For details, please refer to the comments.

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * @author jqorz
 * @since 2018/8/4
 */
public class AES {
    private static final String TAG = AES.class.getSimpleName();
    private static String mSeed = "dfdas7894513xc21asd878ds4c5x1v32df4g56wr7qw89d43c1324165wef4w";


    /**
     * Encrypt the file and change the suffix to consvalue.lock'ext
     */
    public static boolean lock(File file) {
        String sourcePath = file.getPath();
        return AESCipher(Cipher.ENCRYPT_MODE, sourcePath, file.getPath(), mSeed);
    }

    public static boolean unlock(File file) {
        String sourcePath = file.getPath();
        return AESCipher(Cipher.DECRYPT_MODE, sourcePath, file.getPath(), mSeed);
    }

    public static boolean AESCipher(int cipherMode, String sourceFilePath,
                                    String targetFilePath, String seed) {
        boolean result = false;
        FileChannel sourceFC = null;
        FileChannel targetFC = null;

        try {

            if (cipherMode != Cipher.ENCRYPT_MODE
                    && cipherMode != Cipher.DECRYPT_MODE) {
                return false;
            }

            Cipher mCipher = Cipher.getInstance("AES/CFB/NoPadding");

            byte[] rawkey = getRawKey(seed);
            File sourceFile = new File(sourceFilePath);
            File targetFile = new File(targetFilePath);

            sourceFC = new RandomAccessFile(sourceFile, "r").getChannel();
            targetFC = new RandomAccessFile(targetFile, "rw").getChannel();

            SecretKeySpec secretKey = new SecretKeySpec(rawkey, "AES");

            mCipher.init(cipherMode, secretKey, new IvParameterSpec(
                    new byte[mCipher.getBlockSize()]));

            ByteBuffer byteData = ByteBuffer.allocate(1024);
            while (sourceFC.read(byteData) != -1) {
                // Read and write across channels.
                // Prepare buffer for data outgoing state
                byteData.flip();

                byte[] byteList = new byte[byteData.remaining()];
                byteData.get(byteList, 0, byteList.length);
                //Here, if you do not use array encryption and decryption, it will fail, because when byteData is less than 1024, the processing of blank bytes is not the same with different encryption methods, resulting in success and failure.
                byte[] bytes = mCipher.doFinal(byteList);
                targetFC.write(ByteBuffer.wrap(bytes));
                byteData.clear();
            }

            result = true;
        } catch (IOException | NoSuchAlgorithmException | InvalidKeyException
                | InvalidAlgorithmParameterException
                | IllegalBlockSizeException | BadPaddingException
                | NoSuchPaddingException | InvalidKeySpecException e) {
            e.printStackTrace();
        } finally {
            try {
                if (sourceFC != null) {
                    sourceFC.close();
                }
                if (targetFC != null) {
                    targetFC.close();
                }
            } catch (IOException e) {
                e.printStackTrace();

            }
        }

        return result;
    }

    /**
     * Use a secure random number to generate a key, which is encrypted using
     *
     * @param seed secret key
     * @return Security key obtained
     */
    private static byte[] getRawKey(String seed) throws NoSuchAlgorithmException, InvalidKeySpecException {

        // The bit number of the key. Note that this is the bit number
        // AES supports 128, 192 and 256 bit length keys
        int keyLength = 256;
        // Length of byte array of salt value, note that here is the length of byte array
        // Its length value needs to be consistent with the length of the final output key byte array
        // Since the length of the key here is 256 bits, the final key will exist as a byte array of 256 / 8 = 32-bit length
        // So the byte array length of the salt value should also be 32

        // Get a random salt value first
        // You need to save the salt value generated this time to disk and pass in the next time you convert the key from string
        // If the salt values are inconsistent, the converted key values will be different
        // The logic to save the key is not officially written. It needs to be implemented by itself
//        int saltLength = 32;
//        SecureRandom random = new SecureRandom();
//        byte[] salt = new byte[saltLength];
//        random.nextBytes(salt);

        //To save time, use the bytes of the password directly
        byte[] salt = seed.getBytes();
        // Using a new method to convert the password plaintext, salt value, etc
        int iterationCount = 1000;
        KeySpec keySpec = new PBEKeySpec(seed.toCharArray(), salt,
                iterationCount, keyLength);
        SecretKeyFactory keyFactory = SecretKeyFactory
                .getInstance("PBKDF2WithHmacSHA1");
        // You can get a secure key here
        byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
        SecretKey key = new SecretKeySpec(keyBytes, "AES");
        return key.getEncoded();
    }


}

After testing, it can be used normally

Posted by tonyk11 on Sat, 04 Jan 2020 15:59:57 -0800