本博客写给自己记录,也供大家参考(Android 9 = Android P=API 28)
通常的AES加密方式不再重复,很多博客都有些,基本上都是这样:
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance(AES);
//for android
SecureRandom sr = null;
// 在4.2以上版本中,SecureRandom获取方式发生了改变
int sdk_version = android.os.Build.VERSION.SDK_INT;
if (sdk_version > 23) { // Android 6.0 以上
sr = SecureRandom.getInstance(SHA1PRNG, new CryptoProvider());
} else if (sdk_version >= 17) { //4.2及以上
sr = SecureRandom.getInstance(SHA1PRNG, "Crypto");
} else {
sr = SecureRandom.getInstance(SHA1PRNG);
}
// for Java
// secureRandom = SecureRandom.getInstance(SHA1PRNG);
sr.setSeed(seed);
kgen.init(128, sr); //256 bits or 128 bits,192bits
//AES中128位密钥版本有10个加密循环,192比特密钥版本有12个加密循环,256比特密钥版本则有14个加密循环。
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
大多数博客写到的SecureRandom.getInstance为空的解决方案都是用CryptoProvider处理,如下
public static class CryptoProvider extends Provider {
/**
* Creates a Provider and puts parameters
*/
CryptoProvider() {
super("Crypto", 1.0, "HARMONY (SHA1 digest; SecureRandom; SHA1withDSA signature)");
put("SecureRandom.SHA1PRNG",
"org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl");
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
}
}
但在android 9(API28)开始,这样调用也会获取不到SecureRandom,这是谷歌的坑,好了,怎么改,看下面
获取SecureRandom实际上也就是为了获取SecretKeySpec,这里android 9可以直接获取,如下
private static SecretKeySpec deriveKeyInsecurely(String password) {
byte[] passwordBytes = password.getBytes(StandardCharsets.US_ASCII);
return new SecretKeySpec(InsecureSHA1PRNGKeyDerivator.deriveInsecureKey(passwordBytes, 32), "AES");
}
所以这里的代码就改成(只举例加密):
private static byte[] encrypt(String key, byte[] clear) throws Exception {
LogUtils.d("加密", "API::" + android.os.Build.VERSION.SDK_INT);
SecretKeySpec skeySpec = null;
if (android.os.Build.VERSION.SDK_INT >= 28) {
skeySpec = deriveKeyInsecurely(key);
} else {
byte[] raw = getRawKey(key.getBytes());
skeySpec = new SecretKeySpec(raw, AES);
}
Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
好了,有人复制过去代码了,没问题,只是你可能会找不到InsecureSHA1PRNGKeyDerivator这个类,怎么办?
这里:
package com.hly.gps.unique;
/**
* Stripped-down version of the SHA1PRNG provided by the Crypto provider.
* <p>
* The Crypto provider that offers this functionality was deprecated on Android.
* <p>
* Use this class only to retrieve encrypted data that couldn't be retrieved otherwise.
*/
public class InsecureSHA1PRNGKeyDerivator {
/**
* Only public method. Derive a key from the given seed.
* <p>
* Use this method only to retrieve encrypted data that couldn't be retrieved otherwise.
*
* @param seed seed used for the random generator, usually coming from a password
* @param keySizeInBytes length of the array returned
*/
public static byte[] deriveInsecureKey(byte[] seed, int keySizeInBytes) {
InsecureSHA1PRNGKeyDerivator derivator = new InsecureSHA1PRNGKeyDerivator();
derivator.setSeed(seed);
byte[] key = new byte[keySizeInBytes];
derivator.nextBytes(key);
return key;
}
// constants to use in expressions operating on bytes in int and long variables:
// END_FLAGS - final bytes in words to append to message;
// see "ch.5.1 Padding the Message, FIPS 180-2"
// RIGHT1 - shifts to right for left half of long
// RIGHT2 - shifts to right for right half of long
// LEFT - shifts to left for bytes
// MASK - mask to select counter's bytes after shift to right
private static final int[] END_FLAGS = {0x80000000, 0x800000, 0x8000, 0x80};
private static final int[] RIGHT1 = {0, 40, 48, 56};
private static final int[] RIGHT2 = {0, 8, 16, 24};
private static final int[] LEFT = {0, 24, 16, 8};
private static final int[] MASK = {0xFFFFFFFF, 0x00FFFFFF, 0x0000FFFF,
0x000000FF};
// HASHBYTES_TO_USE defines # of bytes returned by "computeHash(byte[])"
// to use to form byte array returning by the "nextBytes(byte[])" method
// Note, that this implementation uses more bytes