Android Asset 加解密 Gradle 脚本
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.IllegalBlockSizeException
import javax.crypto.NoSuchPaddingException
import javax.crypto.spec.SecretKeySpec
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
apply plugin: 'com.android.application'
def aesKey = "\"aes0011223344key\""
def aesKeyCommon = "aes0011223344key"
def aesEnabled = true
def aesDirs = [
(rootProject.file('app/src/main/assets')):(rootProject.file('../build/assets_tmp_dir/main_assets')),
(rootProject.file('../assets')):(rootProject.file('../build/assets_tmp_dir/flutter_assets'))
]
android {
defaultConfig {
buildConfigField "String", "AES_KEY", aesKey
}
}
void copyFolder(String oldPath, String newPath) {
try {
(new File(newPath)).mkdirs() //如果文件夹不存在 则建立新文件夹
File a = new File(oldPath)
String[] file = a.list()
File temp = null
for (int i = 0; i < file.length; i++) {
if (oldPath.endsWith(File.separator)) {
temp = new File(oldPath + file[i])
} else {
temp = new File(oldPath + File.separator + file[i])
}
if (temp.isFile()) {
FileInputStream input = new FileInputStream(temp)
FileOutputStream output = new FileOutputStream(newPath + "/" +
(temp.getName()).toString())
byte[] b = new byte[1024 * 5]
int len
while ((len = input.read(b)) != -1) {
output.write(b, 0, len)
}
output.flush()
output.close()
input.close()
}
if (temp.isDirectory()) {//如果是子文件夹
copyFolder(oldPath + "/" + file[i], newPath + "/" + file[i])
}
}
}
catch (Exception e) {
println("复制整个文件夹内容操作出错")
e.printStackTrace()
}
}
void deleteAllFilesOfDir(String path) {
File file=new File(path)
if (!file.exists())
return
if (file.isFile()) {
file.delete()
return
}
File[] files = file.listFiles()
for (int i = 0; i < files.length; i++) {
deleteAllFilesOfDir(files[i].getAbsolutePath())
}
file.delete()
}
private static byte[] encrypt(byte[] content, String password) {
try {
byte[] keyStr = password.getBytes()
SecretKeySpec key = new SecretKeySpec(keyStr, "AES")
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding") //algorithmStr
cipher.init(Cipher.ENCRYPT_MODE, key)
byte[] result = cipher.doFinal(content)
return result
} catch (NoSuchAlgorithmException e) {
e.printStackTrace()
} catch (NoSuchPaddingException e) {
e.printStackTrace()
} catch (InvalidKeyException e) {
e.printStackTrace()
} catch (UnsupportedEncodingException e) {
e.printStackTrace()
} catch (IllegalBlockSizeException e) {
e.printStackTrace()
} catch (BadPaddingException e) {
e.printStackTrace()
}
return null
}
private static byte[] decrypt(byte[] content, String password) {
try {
byte[] keyStr = password.getBytes()
SecretKeySpec key = new SecretKeySpec(keyStr, "AES")
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, key)
byte[] result = cipher.doFinal(content)
return result //
} catch (NoSuchAlgorithmException e) {
e.printStackTrace()
} catch (NoSuchPaddingException e) {
e.printStackTrace()
} catch (InvalidKeyException e) {
e.printStackTrace()
} catch (IllegalBlockSizeException e) {
e.printStackTrace()
} catch (BadPaddingException e) {
e.printStackTrace()
}
return null
}
/**
* 获得指定文件的byte数组
*/
static byte[] file2bytes(String filePath) {
byte[] buffer = null;
try {
File file = new File(filePath);
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
byte[] b = new byte[1024];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return buffer;
}
void encodeDir(String rawDir, String aesKey){
println "\n===> encodeDir " + rawDir
File searchPlug = new File(rawDir)
if (searchPlug.exists() && searchPlug.isDirectory()) {
File[] files = searchPlug.listFiles()
for (File file : files) {
if(!file.name.endsWith(".json")) { continue }
def data= file2bytes(file.getAbsolutePath())
def content = encrypt(data, aesKey)
def stream = file.newOutputStream()
stream.write(content)
stream.flush()
println "===> encodeDir file ok: " + file.name
}
}
}
gradle.addBuildListener(new BuildListener() {
@Override
void buildStarted(Gradle gradle) {
println "buildStarted"
}
@Override
void settingsEvaluated(Settings settings) {
println "settingsEvaluated"
}
@Override
void projectsLoaded(Gradle gradle) {
println "projectsLoaded"
}
boolean canDoCrypt(Gradle gradle) {
def taskNames = gradle.getStartParameter().taskNames
if (taskNames.contains(":app:assembleDebug")
|| taskNames.contains(":app:assembleRelease")
|| taskNames.contains("assembleDebug")
|| taskNames.contains("assembleRelease")) {
return true
}
return false
}
@Override
void projectsEvaluated(Gradle gradle) {
println "projectsEvaluated : " + gradle.getStartParameter().taskNames.toString()
if (!aesEnabled || !canDoCrypt(gradle)) {
return
}
aesDirs.eachWithIndex{ def rawDir, def tmpDir, int i ->
//先复制加密前文件夹
copyFolder(rawDir.absolutePath, tmpDir.absolutePath)
encodeDir(rawDir.absolutePath, aesKeyCommon)
}
}
@Override
void buildFinished(BuildResult buildResult) {
println "buildFinished : " + gradle.getStartParameter().taskNames.toString()
if (!aesEnabled || !canDoCrypt(gradle)) {
return
}
aesDirs.eachWithIndex{ def rawDir, def tmpDir, int i ->
//apk打包成功后回滚文件夹
copyFolder(tmpDir.absolutePath, rawDir.absolutePath)
deleteAllFilesOfDir(tmpDir.absolutePath)
println "===> recoveryDir " + rawDir.absolutePath
}
}
})