天天看點

android studio實作raw資源檔案加密

本文将介紹android studio利用gradle進行raw資源檔案加密的一種方式(所有代碼均寫在build.gradle中):

第一步:首先要需要了解的是gradle打包監聽器BuildListener的兩個方法:

  • projectsEvaluated()方法執行在生成apk前: 是以在projectsEvaluated方法進行apk資源檔案copy工作和加密工作。
  • buildFinished()方法執行在apk生成之後: 在buildFinished方法中進行加密資源檔案的恢複和臨時檔案的删除工作(臨時檔案用于未加密代碼的臨時存儲,打包結束後需要放會raw檔案夾中)
  • 需copy如下代碼:
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"

    }

    @Override
    void projectsEvaluated(Gradle gradle) {
        println "projectsEvaluated"
        copyFolder(rawDir,tempDir)
        encodeDir(rawDir,aesKeyCommen)
    }
    @Override
    void buildFinished(BuildResult buildResult) {
        copyFolder(tempDir,rawDir);
        deleteAllFilesOfDir(tempDir);
    }
})      

第二步:在build.gradle中實作上步驟的方法copyFolder,encodeDir,deleteAllFileofDir

gradle打包相容java代碼,下面貼出這些方法的實作:

//加密的資源檔案路徑
    def rawDir ='./app/src/main/res/raw/'
    //資源檔案臨時存儲檔案夾名稱
    def tempDir ='./tempDir'
    //加密的key
    def aesKey = "\"abcdefgabcdefg12\""
    
    def aesKeyCommen = "abcdefgabcdefg12"
    
    //拷貝檔案夾
    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();
    }
    
    //讀取檔案到string
    static String file2String(File file, String encoding) {
        InputStreamReader reader = null;
        StringWriter writer = new StringWriter();
        try {
            if (encoding == null || "".equals(encoding.trim())) {
                reader = new InputStreamReader(new FileInputStream(file), encoding);
            } else {
                reader = new InputStreamReader(new FileInputStream(file));
            }
            //将輸入流寫入輸出流
            char[] buffer = new char[1024];
            int n = 0;
            while (-1 != (n = reader.read(buffer))) {
                writer.write(buffer, 0, n);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (reader != null)
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
        //傳回轉換結果
        if (writer != null)
            return writer.toString();
        else return null;
    }
    //加密算法
    private static byte[] encrypt(String content, String password) {
        try {
            byte[] keyStr = getKey(password);
            SecretKeySpec key = new SecretKeySpec(keyStr, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//algorithmStr
            byte[] byteContent = content.getBytes("utf-8");
            cipher.init(Cipher.ENCRYPT_MODE, key);//   ʼ
            byte[] result = cipher.doFinal(byteContent);
            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 = getKey(password);
            SecretKeySpec key = new SecretKeySpec(keyStr, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//algorithmStr
            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;
    }
    
    private static byte[] getKey(String password) {
        byte[] rByte = null;
        if (password!=null) {
            rByte = password.getBytes();
        }else{
            rByte = new byte[24];
        }
        return rByte;
    }
    
    //二進制轉16進制
    static String parseByte2HexStr(byte[] buf) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }
    
    static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return null;
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2),
                    16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }
    
    //加密方法
    static String encodeContent(String content, String keyBytes){
        //加密之後的位元組數組,轉成16進制的字元串形式輸出
        return parseByte2HexStr(encrypt(content, keyBytes));
    }
    
    //解密方法
    static String decodeContent(String content, String keyBytes){
        //解密之前,先将輸入的字元串按照16進制轉成二進制的位元組數組,作為待解密的内容輸入
        byte[] b = decrypt(parseHexStr2Byte(content), keyBytes);
        return new String(b);
    }
    
    //projectsEvaluated裡調用的方法  aesKey: key  rawDir:加密的檔案夾
    void encodeDir(String rawDir, String aesKey){
        println "do 加密代碼"
        File searchPlug = new File(rawDir);
        if (searchPlug.exists() && searchPlug.isDirectory()) {
            print "檔案夾存在"
            File[] files = searchPlug.listFiles()
            for (File file : files) {
                if(!file.name.endsWith(".glsl")){
                    continue
                }
                String str=file2String(file,"utf-8")
                def content = encodeContent(str, aesKey)
    //            def result = decodeContent(content, aesKey)
    //            println(" 原始檔案:content"+str)
    //            println(" 加密後檔案:content"+content)
    //            println(" 解密後檔案:content"+result)
                def stream = file.newOutputStream()
                stream.write(content.bytes)
                stream.flush()
            }
        }
    }      

第三步:在gradle和android程式中實作加密key的共享

  1. 在gradle檔案中增加代碼
defaultConfig {
             buildConfigField "String", "AES_KEY",aesKey
            }      
  1. 在android代碼中利用BuildConfig.AES_KEY擷取build.gradle檔案中配置的key。
  2. 注:在gradle檔案中使用的key用def aesKeyCommen = "abcdefgabcdefg12"

    在 buildConfigField 裡需要傳入的String為: ""abcdefgabcdefg12""

    因為gradle生成java類時會預設省掉“”。

第四步:android程式裡解密

1 . 建立工具類:

public class AESUtils {


    private static byte[] encrypt(String content, String password) {
        try {
            byte[] keyStr = getKey(password);
            SecretKeySpec key = new SecretKeySpec(keyStr, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//algorithmStr
            byte[] byteContent = content.getBytes("utf-8");
            cipher.init(Cipher.ENCRYPT_MODE, key);//   ʼ
            byte[] result = cipher.doFinal(byteContent);
            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 = getKey(password);
            SecretKeySpec key = new SecretKeySpec(keyStr, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//algorithmStr
            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;
    }

    private static byte[] getKey(String password) {
        byte[] rByte = null;
        if (password!=null) {
            rByte = password.getBytes();
        }else{
            rByte = new byte[24];
        }
        return rByte;
    }

    /**
     * 将二進制轉換成16進制
     * @param buf
     * @return
     */
    public static String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 将16進制轉換為二進制
     * @param hexStr
     * @return
     */
    public static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return null;
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2),
                    16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }

    /**
     *加密
     */
    public static String encode(String content,String keyBytes){
        //加密之後的位元組數組,轉成16進制的字元串形式輸出
        return parseByte2HexStr(encrypt(content, keyBytes));
    }

    /**
     *解密
     */
    public static String decode(String content,String keyBytes){
        //解密之前,先将輸入的字元串按照16進制轉成二進制的位元組數組,作為待解密的内容輸入
        byte[] b = decrypt(parseHexStr2Byte(content), keyBytes);
        return new String(b);
    }

   

}      

2 . 讀取raw資源檔案,并進行解密

public static String readShaderFromRawResource(final int resourceId) {
        final InputStream inputStream = CameraApplication.getInstance().getResources().openRawResource(
                resourceId);
        final InputStreamReader inputStreamReader;
        try {
            inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String nextLine;
            final StringBuilder body = new StringBuilder();

            try {
                while ((nextLine = bufferedReader.readLine()) != null) {
                    body.append(nextLine);
                    body.append('\n');
                }
            } catch (IOException e) {
                return null;
            }
            //AESUtils.decode是解密方法,BuildConfig.AES_KEY,為gradle中配置的key
            String decrypt = AESUtils.decode(body.toString(), BuildConfig.AES_KEY);
            return decrypt;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "";

    }      

第五步:大功告成,解壓生成的apk,然後檢視raw資源檔案