最近在做项目加密,用的是AES,有一个很麻烦的坑,之前加密用的md5和des没有说适配7.0,甚至于Android9.0都不支持SHA1PRNG算法和Crypto,同样的代码在main()方法跑出来的结果和后台的加解密方式一样,这里加密的字段和方法都是一样的,但是跑9.0真机就报错抛异常.
1.先看抛出的异常信息如下:
错误代码为:SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
google官方博客信息为:The Crypto provider has been deleted in Android P (and was deprecated in Android N), so the code will crash. less... (Ctrl+F1)
The Crypto provider has been completely removed in Android P (and was deprecated in an earlier release). This means that the code will throw a NoSuchProviderException and the app will crash. Even if the code catches that exception at a higher level, this is not secure and should not be used.
2.翻译过来就是:加密提供程序已在Android P中删除(在Android N中已弃用),因此代码将崩溃。 更少...(Ctrl + F1)
Crypto提供程序已在Android P中完全删除(在较早的版本中已弃用)。 这意味着代码将引发NoSuchProviderException,并且应用程序将崩溃。 即使代码在更高级别捕获了该异常,这也不安全,因此不应使用。
3.加密参考代码:
byte[] decryptFrom = parseHexStr2Byte(content);
KeyGenerator kgt = KeyGenerator.getInstance(AES);
SecureRandom secureRandom = SecureRandom.getInstance(SP);
secureRandom.setSeed(key.getBytes());
kgt.init(128, secureRandom);
SecretKey secretKey = kgt.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, AES);
Cipher cipher = Cipher.getInstance(AES);// 创建密码器
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);// 初始化
byte[] result = cipher.doFinal(decryptFrom)
4.有以下两种情况:
(1), targetSdkVersion < 24 时,在Android P 以上的设备会出现: java.security.NoSuchProviderException: no such provider: Crypto
(2),targetSdkVersion >= 24 时,在Android N 以上的设备会出现: java.security.NoSuchProviderException: no such provider: Crypto
5.解决方法:判断设备的版本号,根据版本进行不同的加密
@SuppressLint({"DeletedProvider", "GetInstance"})
public static String des(String content, String password, @AESType int type) {
if (TextUtils.isEmpty(content) || TextUtils.isEmpty(password)) {
return null;
}
try {
SecretKeySpec secretKeySpec;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
secretKeySpec = deriveKeyInsecurely(password);
} else {
secretKeySpec = fixSmallVersion(password);
}
Cipher cipher = Cipher.getInstance("AES");
cipher.init(type, secretKeySpec);
if (type == Cipher.ENCRYPT_MODE) {
byte[] byteContent = content.getBytes("utf-8");
return parseByte2HexStr(cipher.doFinal(byteContent));
} else {
byte[] byteContent = parseHexStr2Byte(content);
return new String(cipher.doFinal(byteContent));
}
} catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException |
UnsupportedEncodingException | InvalidKeyException | NoSuchPaddingException |
NoSuchProviderException e) {
e.printStackTrace();
}
return null;
}
@SuppressLint("DeletedProvider")
private static SecretKeySpec fixSmallVersion(String password) throws NoSuchAlgorithmException, NoSuchProviderException {
KeyGenerator generator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
secureRandom = SecureRandom.getInstance(SHA1_PRNG, new CryptoProvider());
} else {
secureRandom = SecureRandom.getInstance(SHA1_PRNG, "Crypto");
}
secureRandom.setSeed(password.getBytes());
generator.init(128, secureRandom);
byte[] enCodeFormat = generator.generateKey().getEncoded();
return new SecretKeySpec(enCodeFormat, "AES");
}
private static SecretKeySpec deriveKeyInsecurely(String password) {
byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8);
return new SecretKeySpec(InsecureSHA1PRNGKeyDerivator.deriveInsecureKey(passwordBytes, AesSignUtils.KEY_SIZE), "AES");
}
@IntDef({Cipher.ENCRYPT_MODE, Cipher.DECRYPT_MODE})
@interface AESType {
}
private static final class CryptoProvider extends Provider {
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");
}
}
6.加解密的完整代码如下:
package com.example.tvrecyclerview.util;
import android.annotation.SuppressLint;
import android.os.Build;
import android.text.TextUtils;
import androidx.annotation.IntDef;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
/**
* @author: njb
* @date: 2020/7/19 0019 0:25
* @desc:
*/
public class AesSignUtils {
private final static String SHA1_PRNG = "SHA1PRNG";
private static final int KEY_SIZE = 32;
/**
* Aes加密/解密
*
* @param content 字符串
* @param password 密钥
* @param type 加密:{@link Cipher#ENCRYPT_MODE},解密:{@link Cipher#DECRYPT_MODE}
* @return 加密/解密结果字符串
*/
@SuppressLint({"DeletedProvider", "GetInstance"})
public static String des(String content, String password, @AESType int type) {
if (TextUtils.isEmpty(content) || TextUtils.isEmpty(password)) {
return null;
}
try {
SecretKeySpec secretKeySpec;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
secretKeySpec = deriveKeyInsecurely(password);
} else {
secretKeySpec = fixSmallVersion(password);
}
Cipher cipher = Cipher.getInstance("AES");
cipher.init(type, secretKeySpec);
if (type == Cipher.ENCRYPT_MODE) {
byte[] byteContent = content.getBytes("utf-8");
return parseByte2HexStr(cipher.doFinal(byteContent));
} else {
byte[] byteContent = parseHexStr2Byte(content);
return new String(cipher.doFinal(byteContent));
}
} catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException |
UnsupportedEncodingException | InvalidKeyException | NoSuchPaddingException |
NoSuchProviderException e) {
e.printStackTrace();
}
return null;
}
@SuppressLint("DeletedProvider")
private static SecretKeySpec fixSmallVersion(String password) throws NoSuchAlgorithmException, NoSuchProviderException {
KeyGenerator generator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
secureRandom = SecureRandom.getInstance(SHA1_PRNG, new CryptoProvider());
} else {
secureRandom = SecureRandom.getInstance(SHA1_PRNG, "Crypto");
}
secureRandom.setSeed(password.getBytes());
generator.init(128, secureRandom);
byte[] enCodeFormat = generator.generateKey().getEncoded();
return new SecretKeySpec(enCodeFormat, "AES");
}
private static SecretKeySpec deriveKeyInsecurely(String password) {
byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8);
return new SecretKeySpec(InsecureSHA1PRNGKeyDerivator.deriveInsecureKey(passwordBytes, AesSignUtils.KEY_SIZE), "AES");
}
/**
* 将二进制转换成16进制
* @param buf
* @return
*/
@SuppressLint("NewApi")
private static String parseByte2HexStr(byte buf[]) {
return new String(Base64.getEncoder().encodeToString(buf));
}
/**
* 将16进制转换为二进制
* @param hexStr
* @return
*/
@SuppressLint("NewApi")
private static byte[] parseHexStr2Byte(String hexStr) {
return Base64.getDecoder().decode(hexStr);
}
@IntDef({Cipher.ENCRYPT_MODE, Cipher.DECRYPT_MODE})
@interface AESType {
}
private static final class CryptoProvider extends Provider {
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");
}
}
}
7.修复问题的关键:用谷歌的提供的类InsecureSHA1PRNGKeyDerivator去派生出密钥,将获取SecretKey的方法全部替换成如下就ok了.
完整代码如下:
package com.example.tvrecyclerview.util;
/**
* @author: njb
* @date: 2020/7/19 0019 0:33
* @desc:
*/
/**
* Stripped-down version of the SHA1PRNG provided by the Crypto provider.
*
* The Crypto provider that offers this functionality was deprecated on Android.
*
* 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.
*
* 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 than it is defined
// in the above specification.
private static final int HASHBYTES_TO_USE = 20;
// value of 16 defined in the "SECURE HASH STANDARD", FIPS PUB 180-2
private static final int FRAME_LENGTH = 16;
// miscellaneous constants defined in this implementation:
// COUNTER_BASE - initial value to set to "counter" before computing "nextBytes(..)";
// note, that the exact value is not defined in STANDARD
// HASHCOPY_OFFSET - offset for copy of current hash in "copies" array
// EXTRAFRAME_OFFSET - offset for extra frame in "copies" array;
// as the extra frame follows the current hash frame,
// EXTRAFRAME_OFFSET is equal to length of current hash frame
// FRAME_OFFSET - offset for frame in "copies" array
// MAX_BYTES - maximum # of seed bytes processing which doesn't require extra frame
// see (1) comments on usage of "seed" array below and
// (2) comments in "engineNextBytes(byte[])" method
//
// UNDEFINED - three states of engine; initially its state is "UNDEFINED"
// SET_SEED call to "engineSetSeed" sets up "SET_SEED" state,
// NEXT_BYTES call to "engineNextByte" sets up "NEXT_BYTES" state
private static final int COUNTER_BASE = 0;
private static final int HASHCOPY_OFFSET = 0;
private static final int EXTRAFRAME_OFFSET = 5;
private static final int FRAME_OFFSET = 21;
private static final int MAX_BYTES = 48;
private static final int UNDEFINED = 0;
private static final int SET_SEED = 1;
private static final int NEXT_BYTES = 2;
// Structure of "seed" array:
// - 0-79 - words for computing hash
// - 80 - unused
// - 81 - # of seed bytes in current seed frame
// - 82-86 - 5 words, current seed hash
private transient int[] seed;
// total length of seed bytes, including all processed
private transient long seedLength;
// Structure of "copies" array
// - 0-4 - 5 words, copy of current seed hash
// - 5-20 - extra 16 words frame;
// is used if final padding exceeds 512-bit length
// - 21-36 - 16 word frame to store a copy of remaining bytes
private transient int[] copies;
// ready "next" bytes; needed because words are returned
private transient byte[] nextBytes;
// index of used bytes in "nextBytes" array
private transient int nextBIndex;
// variable required according to "SECURE HASH STANDARD"
private transient long counter;
// contains int value corresponding to engine's current state
private transient int state;
/**
* constant defined in "SECURE HASH STANDARD"
*/
private static final int H0 = 0x67452301;
/**
* constant defined in "SECURE HASH STANDARD"
*/
private static final int H1 = 0xEFCDAB89;
/**
* constant defined in "SECURE HASH STANDARD"
*/
private static final int H2 = 0x98BADCFE;
/**
* constant defined in "SECURE HASH STANDARD"
*/
private static final int H3 = 0x10325476;
/**
* constant defined in "SECURE HASH STANDARD"
*/
private static final int H4 = 0xC3D2E1F0;
/**
* offset in buffer to store number of bytes in 0-15 word frame
*/
private static final int BYTES_OFFSET = 81;
/**
* offset in buffer to store current hash value
*/
private static final int HASH_OFFSET = 82;
/**
* # of bytes in H0-H4 words;
* in this implementation # is set to 20 (in general # varies from 1 to 20)
*/
private static final int DIGEST_LENGTH = 20;
// The "seed" array is used to compute both "current seed hash" and "next bytes".
//
// As the "SHA1" algorithm computes a hash of entire seed by splitting it into
// a number of the 512-bit length frames (512 bits = 64 bytes = 16 words),
// "current seed hash" is a hash (5 words, 20 bytes) for all previous full frames;
// remaining bytes are stored in the 0-15 word frame of the "seed" array.
//
// As for calculating "next bytes",
// both remaining bytes and "current seed hash" are used,
// to preserve the latter for following "setSeed(..)" commands,
// the following technique is used:
// - upon getting "nextBytes(byte[])" invoked, single or first in row,
// which requires computing new hash, that is,
// there is no more bytes remaining from previous "next bytes" computation,
// remaining bytes are copied into the 21-36 word frame of the "copies" array;
// - upon getting "setSeed(byte[])" invoked, single or first in row,
// remaining bytes are copied back.
private InsecureSHA1PRNGKeyDerivator() {
seed = new int[HASH_OFFSET + EXTRAFRAME_OFFSET];
seed[HASH_OFFSET] = H0;
seed[HASH_OFFSET + 1] = H1;
seed[HASH_OFFSET + 2] = H2;
seed[HASH_OFFSET + 3] = H3;
seed[HASH_OFFSET + 4] = H4;
seedLength = 0;
copies = new int[2 * FRAME_LENGTH + EXTRAFRAME_OFFSET];
nextBytes = new byte[DIGEST_LENGTH];
nextBIndex = HASHBYTES_TO_USE;
counter = COUNTER_BASE;
state = UNDEFINED;
}
/*
* The method invokes the SHA1Impl's "updateHash(..)" method
* to update current seed frame and
* to compute new intermediate hash value if the frame is full.
*
* After that it computes a length of whole seed.
*/
private void updateSeed(byte[] bytes) {
// on call: "seed" contains current bytes and current hash;
// on return: "seed" contains new current bytes and possibly new current hash
// if after adding, seed bytes overfill its buffer
updateHash(seed, bytes, 0, bytes.length - 1);
seedLength += bytes.length;
}
/**
* Changes current seed by supplementing a seed argument to the current seed,
* if this already set;
* the argument is used as first seed otherwise.
*
* The method overrides "engineSetSeed(byte[])" in class SecureRandomSpi.
*
* @param
* seed - byte array
* @throws
* NullPointerException - if null is passed to the "seed" argument
*/
private void setSeed(byte[] seed) {
if (seed == null) {
throw new NullPointerException("seed == null");
}
if (state == NEXT_BYTES) { // first setSeed after NextBytes; restoring hash
System.arraycopy(copies, HASHCOPY_OFFSET, this.seed, HASH_OFFSET,
EXTRAFRAME_OFFSET);
}
state = SET_SEED;
if (seed.length != 0) {
updateSeed(seed);
}
}
/**
* Writes random bytes into an array supplied.
* Bits in a byte are from left to right.
*
* To generate random bytes, the "expansion of source bits" method is used,
* that is,
* the current seed with a 64-bit counter appended is used to compute new bits.
* The counter is incremented by 1 for each 20-byte output.
*
* The method overrides engineNextBytes in class SecureRandomSpi.
*
* @param
* bytes - byte array to be filled in with bytes
* @throws
* NullPointerException - if null is passed to the "bytes" argument
*/
protected synchronized void nextBytes(byte[] bytes) {
int i, n;
long bits; // number of bits required by Secure Hash Standard
int nextByteToReturn; // index of ready bytes in "bytes" array
int lastWord; // index of last word in frame containing bytes
// This is a bug since words are 4 bytes. Android used to keep it this way for backward
// compatibility.
final int extrabytes = 7;// # of bytes to add in order to computer # of 8 byte words
if (bytes == null) {
throw new NullPointerException("bytes == null");
}
// This is a bug since extraBytes == 7 instead of 3. Android used to keep it this way for
// backward compatibility.
lastWord = seed[BYTES_OFFSET] == 0 ? 0
: (seed[BYTES_OFFSET] + extrabytes) >> 3 - 1;
if (state == UNDEFINED) {
throw new IllegalStateException("No seed supplied!");
} else if (state == SET_SEED) {
System.arraycopy(seed, HASH_OFFSET, copies, HASHCOPY_OFFSET,
EXTRAFRAME_OFFSET);
// possible cases for 64-byte frame:
//
// seed bytes < 48 - remaining bytes are enough for all, 8 counter bytes,
// 0x80, and 8 seedLength bytes; no extra frame required
// 48 < seed bytes < 56 - remaining 9 bytes are for 0x80 and 8 counter bytes
// extra frame contains only seedLength value at the end
// seed bytes > 55 - extra frame contains both counter's bytes
// at the beginning and seedLength value at the end;
// note, that beginning extra bytes are not more than 8,
// that is, only 2 extra words may be used
// no need to set to "0" 3 words after "lastWord" and
// more than two words behind frame
for (i = lastWord + 3; i < FRAME_LENGTH + 2; i++) {
seed[i] = 0;
}
bits = (seedLength << 3) + 64; // transforming # of bytes into # of bits
// putting # of bits into two last words (14,15) of 16 word frame in
// seed or copies array depending on total length after padding
if (seed[BYTES_OFFSET] < MAX_BYTES) {
seed[14] = (int) (bits >>> 32);
seed[15] = (int) (bits & 0xFFFFFFFF);
} else {
copies[EXTRAFRAME_OFFSET + 14] = (int) (bits >>> 32);
copies[EXTRAFRAME_OFFSET + 15] = (int) (bits & 0xFFFFFFFF);
}
nextBIndex = HASHBYTES_TO_USE; // skipping remaining random bits
}
state = NEXT_BYTES;
if (bytes.length == 0) {
return;
}
nextByteToReturn = 0;
// possibly not all of HASHBYTES_TO_USE bytes were used previous time
n = (HASHBYTES_TO_USE - nextBIndex) < (bytes.length - nextByteToReturn) ? HASHBYTES_TO_USE
- nextBIndex
: bytes.length - nextByteToReturn;
if (n > 0) {
System.arraycopy(nextBytes, nextBIndex, bytes, nextByteToReturn, n);
nextBIndex += n;
nextByteToReturn += n;
}
if (nextByteToReturn >= bytes.length) {
return; // return because "bytes[]" are filled in
}
n = seed[BYTES_OFFSET] & 0x03;
for (;;) {
if (n == 0) {
seed[lastWord] = (int) (counter >>> 32);
seed[lastWord + 1] = (int) (counter & 0xFFFFFFFF);
seed[lastWord + 2] = END_FLAGS[0];
} else {
seed[lastWord] |= (int) ((counter >>> RIGHT1[n]) & MASK[n]);
seed[lastWord + 1] = (int) ((counter >>> RIGHT2[n]) & 0xFFFFFFFF);
seed[lastWord + 2] = (int) ((counter << LEFT[n]) | END_FLAGS[n]);
}
if (seed[BYTES_OFFSET] > MAX_BYTES) {
copies[EXTRAFRAME_OFFSET] = seed[FRAME_LENGTH];
copies[EXTRAFRAME_OFFSET + 1] = seed[FRAME_LENGTH + 1];
}
computeHash(seed);
if (seed[BYTES_OFFSET] > MAX_BYTES) {
System.arraycopy(seed, 0, copies, FRAME_OFFSET, FRAME_LENGTH);
System.arraycopy(copies, EXTRAFRAME_OFFSET, seed, 0,
FRAME_LENGTH);
computeHash(seed);
System.arraycopy(copies, FRAME_OFFSET, seed, 0, FRAME_LENGTH);
}
counter++;
int j = 0;
for (i = 0; i < EXTRAFRAME_OFFSET; i++) {
int k = seed[HASH_OFFSET + i];
nextBytes[j] = (byte) (k >>> 24); // getting first byte from left
nextBytes[j + 1] = (byte) (k >>> 16); // getting second byte from left
nextBytes[j + 2] = (byte) (k >>> 8); // getting third byte from left
nextBytes[j + 3] = (byte) (k); // getting fourth byte from left
j += 4;
}
nextBIndex = 0;
j = HASHBYTES_TO_USE < (bytes.length - nextByteToReturn) ? HASHBYTES_TO_USE
: bytes.length - nextByteToReturn;
if (j > 0) {
System.arraycopy(nextBytes, 0, bytes, nextByteToReturn, j);
nextByteToReturn += j;
nextBIndex += j;
}
if (nextByteToReturn >= bytes.length) {
break;
}
}
}
/**
* The method generates a 160 bit hash value using
* a 512 bit message stored in first 16 words of int[] array argument and
* current hash value stored in five words, beginning OFFSET+1, of the array argument.
* Computation is done according to SHA-1 algorithm.
*
* The resulting hash value replaces the previous hash value in the array;
* original bits of the message are not preserved.
*
* No checks on argument supplied, that is,
* a calling method is responsible for such checks.
* In case of incorrect array passed to the method
* either NPE or IndexOutOfBoundException gets thrown by JVM.
*
* @params
* arrW - integer array; arrW.length >= (BYTES_OFFSET+6);
* only first (BYTES_OFFSET+6) words are used
*/
private static void computeHash(int[] arrW) {
int a = arrW[HASH_OFFSET ];
int b = arrW[HASH_OFFSET +1];
int c = arrW[HASH_OFFSET +2];
int d = arrW[HASH_OFFSET +3];
int e = arrW[HASH_OFFSET +4];
int temp;
// In this implementation the "d. For t = 0 to 79 do" loop
// is split into four loops. The following constants:
// K = 5A827999 0 <= t <= 19
// K = 6ED9EBA1 20 <= t <= 39
// K = 8F1BBCDC 40 <= t <= 59
// K = CA62C1D6 60 <= t <= 79
// are hex literals in the loops.
for ( int t = 16; t < 80 ; t++ ) {
temp = arrW[t-3] ^ arrW[t-8] ^ arrW[t-14] ^ arrW[t-16];
arrW[t] = ( temp<<1 ) | ( temp>>>31 );
}
for ( int t = 0 ; t < 20 ; t++ ) {
temp = ( ( a<<5 ) | ( a>>>27 ) ) +
( ( b & c) | ((~b) & d) ) +
( e + arrW[t] + 0x5A827999 ) ;
e = d;
d = c;
c = ( b<<30 ) | ( b>>>2 ) ;
b = a;
a = temp;
}
for ( int t = 20 ; t < 40 ; t++ ) {
temp = ((( a<<5 ) | ( a>>>27 ))) + (b ^ c ^ d) + (e + arrW[t] + 0x6ED9EBA1) ;
e = d;
d = c;
c = ( b<<30 ) | ( b>>>2 ) ;
b = a;
a = temp;
}
for ( int t = 40 ; t < 60 ; t++ ) {
temp = (( a<<5 ) | ( a>>>27 )) + ((b & c) | (b & d) | (c & d)) +
(e + arrW[t] + 0x8F1BBCDC) ;
e = d;
d = c;
c = ( b<<30 ) | ( b>>>2 ) ;
b = a;
a = temp;
}
for ( int t = 60 ; t < 80 ; t++ ) {
temp = ((( a<<5 ) | ( a>>>27 ))) + (b ^ c ^ d) + (e + arrW[t] + 0xCA62C1D6) ;
e = d;
d = c;
c = ( b<<30 ) | ( b>>>2 ) ;
b = a;
a = temp;
}
arrW[HASH_OFFSET ] += a;
arrW[HASH_OFFSET +1] += b;
arrW[HASH_OFFSET +2] += c;
arrW[HASH_OFFSET +3] += d;
arrW[HASH_OFFSET +4] += e;
}
/**
* The method appends new bytes to existing ones
* within limit of a frame of 64 bytes (16 words).
*
* Once a length of accumulated bytes reaches the limit
* the "computeHash(int[])" method is invoked on the array to compute updated hash,
* and the number of bytes in the frame is set to 0.
* Thus, after appending all bytes, the array contain only those bytes
* that were not used in computing final hash value yet.
*
* No checks on arguments passed to the method, that is,
* a calling method is responsible for such checks.
*
* @params
* intArray - int array containing bytes to which to append;
* intArray.length >= (BYTES_OFFSET+6)
* @params
* byteInput - array of bytes to use for the update
* @params
* from - the offset to start in the "byteInput" array
* @params
* to - a number of the last byte in the input array to use,
* that is, for first byte "to"==0, for last byte "to"==input.length-1
*/
private static void updateHash(int[] intArray, byte[] byteInput, int fromByte, int toByte) {
// As intArray contains a packed bytes
// the buffer's index is in the intArray[BYTES_OFFSET] element
int index = intArray[BYTES_OFFSET];
int i = fromByte;
int maxWord;
int nBytes;
int wordIndex = index >>2;
int byteIndex = index & 0x03;
intArray[BYTES_OFFSET] = ( index + toByte - fromByte + 1 ) & 077 ;
// In general case there are 3 stages :
// - appending bytes to non-full word,
// - writing 4 bytes into empty words,
// - writing less than 4 bytes in last word
if ( byteIndex != 0 ) { // appending bytes in non-full word (as if)
for ( ; ( i <= toByte ) && ( byteIndex < 4 ) ; i++ ) {
intArray[wordIndex] |= ( byteInput[i] & 0xFF ) << ((3 - byteIndex)<<3) ;
byteIndex++;
}
if ( byteIndex == 4 ) {
wordIndex++;
if ( wordIndex == 16 ) { // intArray is full, computing hash
computeHash(intArray);
wordIndex = 0;
}
}
if ( i > toByte ) { // all input bytes appended
return ;
}
}
// writing full words
maxWord = (toByte - i + 1) >> 2; // # of remaining full words, may be "0"
for ( int k = 0; k < maxWord ; k++ ) {
intArray[wordIndex] = ( ((int) byteInput[i ] & 0xFF) <<24 ) |
( ((int) byteInput[i +1] & 0xFF) <<16 ) |
( ((int) byteInput[i +2] & 0xFF) <<8 ) |
( ((int) byteInput[i +3] & 0xFF) ) ;
i += 4;
wordIndex++;
if ( wordIndex < 16 ) { // buffer is not full yet
continue;
}
computeHash(intArray); // buffer is full, computing hash
wordIndex = 0;
}
// writing last incomplete word
// after writing free byte positions are set to "0"s
nBytes = toByte - i +1;
if ( nBytes != 0 ) {
int w = ((int) byteInput[i] & 0xFF) <<24 ;
if ( nBytes != 1 ) {
w |= ((int) byteInput[i +1] & 0xFF) <<16 ;
if ( nBytes != 2) {
w |= ((int) byteInput[i +2] & 0xFF) <<8 ;
}
}
intArray[wordIndex] = w;
}
return ;
}
}
8.验证一下加解密方法是否正常:从下图日志可以看出加解密的参数一致,至此AES加密适配9.0问题解决。
调试截图如下:加密和解密的内容也是一致的
9.总结:这次加密真心和后台没关系,后台也很奇怪,哈哈~~在main()控制台也是正常的,但是到Android8.0、9.0系统手机就报错了,只能说Android系统太坑,连加解密也要适配不同的系统,踩完一坑又一坑,总算前进了。最后感谢大佬的文章和资料,小伙伴们有兴趣可以自己尝试一下.
参考文章链接为:https://www.jianshu.com/p/4533f43022f7
https://7449.github.io/2018/12/11/android-p-aes.html