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

2016-09-14

这个问题的相关说明你可以看看这篇博文

按照官方说明,我写了一个工具类,下面是代码:

/**
* 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

noBorder

除非另有声明,本博客所有文章采用的授权方式为 自由转载-非商用-非衍生-保持署名 ,转载请务必注明出处,谢谢。

文章评论



章节列表