Android N中不再支持“Crypto”的解決方案

這個問題的相關說明你可以看看這篇博文

按照官方說明,我寫了一個工具類,下面是代碼:

/**
 * AES 加密工具類,適配Android7.0,相對老的加密工具,提升了安全性
 */
public class AESUtil {
    /** iv大小(位) **/
    private static final int IV_SIZE = 16;
    /** 密鑰大小(位) **/
    private static final int KEY_SIZE = 32;
    /** iv文件名 **/
    private static final String IV_FILE_NAME = "AES_IV";
    /** salt文件名 **/
    private static final String SALT_FILE_NAME = "AES_SALT";
    // iv文件和salt文件位於外置儲存卡的Android/data/com.example.name(你的App的包名)/files/Download目錄下,文件名如上面所示

    /**
     * 生成一個安全的密鑰
     * @param password 生成密鑰的seed
     * @param keySizeInBytes 密鑰大小(位)
     * @return 密鑰
     */
    private static SecretKey deriveKeySecurely(Context context, String password, int keySizeInBytes) {
        // Use this to derive the key from the password:
        KeySpec keySpec = new PBEKeySpec(password.toCharArray(), retrieveSalt(context),
                100 /* iterationCount */, keySizeInBytes * 8 /* key size in bits */);
        try {
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
            return new SecretKeySpec(keyBytes, "AES");
        } catch (Exception e) {
            throw new RuntimeException("Deal with exceptions properly!", e);
        }
    }

    private static byte[] retrieveIv(Context context) {
        byte[] iv = new byte[IV_SIZE];
        // Ideally your data should have been encrypted with a random iv. This creates a random iv
        // if not present, in order to encrypt our mock data.
        readFromFileOrCreateRandom(context, IV_FILE_NAME, iv);
        return iv;
    }

    private static byte[] retrieveSalt(Context context) {
        // Salt must be at least the same size as the key.
        byte[] salt = new byte[KEY_SIZE];
        // Create a random salt if encrypting for the first time, and save it for future use.
        readFromFileOrCreateRandom(context, SALT_FILE_NAME, salt);
        return salt;
    }

    private static void readFromFileOrCreateRandom(Context context, String fileName, byte[] bytes) {
        if (fileExists(context, fileName)) {
            readBytesFromFile(context, fileName, bytes);
            return;
        }
        SecureRandom sr = new SecureRandom();
        sr.nextBytes(bytes);
        writeToFile(context, fileName, bytes);
    }

    private static boolean fileExists(Context context, String fileName) {
        File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName);
        return file.exists();
    }

    @SuppressWarnings("unused")
    private static void removeFile(Context context, String fileName) {
        File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName);
        //noinspection ResultOfMethodCallIgnored
        file.delete();
    }

    private static void writeToFile(Context context, String fileName, byte[] bytes) {
        File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName);

        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(bytes);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Couldn't write to " + fileName, e);
        }
    }

    private static void readBytesFromFile(Context context, String fileName, byte[] bytes) {
        File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName);

        try {
            FileInputStream fis = new FileInputStream(file);
            int numBytes = 0;
            while (numBytes < bytes.length) {
                int n = fis.read(bytes, numBytes, bytes.length - numBytes);
                if (n <= 0) {
                    throw new RuntimeException("Couldn't read from " + fileName);
                }
                numBytes += n;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Couldn't read from " + fileName, e);
        }
    }

    private static byte[] encryptOrDecrypt(
            byte[] data, SecretKey key, byte[] iv, boolean isEncrypt) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");
            cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key,
                    new IvParameterSpec(iv));
            return cipher.doFinal(data);
        } catch (GeneralSecurityException e) {
            throw new RuntimeException("This is unconceivable!", e);
        }
    }

    private static byte[] encryptData(byte[] data, byte[] iv, SecretKey key) {
        return encryptOrDecrypt(data, key, iv, true);
    }

    private static byte[] decryptData(byte[] data, byte[] iv, SecretKey key) {
        return encryptOrDecrypt(data, key, iv, false);
    }

    // 本工具類唯一提供的兩個公共方法,用來加密和加密數據

    /**
     * 加密數據
     * @param data 待加密的數據
     * @param password 密鑰
     * @return 已加密的數據
     */
    public static byte[] encrypt(Context context, String data, String password) {
        return encryptData(data.getBytes(), retrieveIv(context), deriveKeySecurely(context, password, KEY_SIZE));
    }

    /**
     * 解密數據
     * @param data 待解密的數據
     * @param password 密鑰
     * @return 已解密的數據
     */
    public static byte[] decrypt(Context context, byte[] data, String password) {
        return decryptData(data, retrieveIv(context), deriveKeySecurely(context, password, KEY_SIZE));
    }

使用方法:

調用AESUtil.encrypt(Context context, String data, String password)來加密數據,調用AESUtil.decrypt(Context context, byte[] data, String password)來解密數據。

Enjoy it

轉載地址: http://celerysoft.github.io/2016-09-14.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章