Android APP加解密解決方案

最近搞加密,APP需要將一些數據加密存儲,顯示的時候解密顯示。對於我們這種小衆app來說,自己寫個加密算法,雖然沒有主流的那麼安全,但誰會破解這麼個app啊。算了,老闆說萬一火起來呢,也對,那就用主流加密算法吧。

首選AES對稱加密算法,AES解密的時候需要加密的密鑰,爲了不被反編譯看到密鑰,肯定不能硬編碼在代碼中,而且最好每次對同一個數據加密後生成的祕文是不一樣的。網上基本都是算法調用,具體用到應用中可能就不那麼實用。

所以當下要解決的問題有如下幾點:

  1. AES密鑰如何保存才能不被泄漏
  2. 每次加密的時候用同一個密鑰,但要使每次加密後的祕文不一樣

先解決第二點,每次加密的祕文不一樣。明文不變,密鑰不變,要使密文改變,只能每次改變向量IV值。AES加密時需要一個向量IV,只要確保每次加密時它是變的,解密時依舊是加密時的IV就可以,我是用到ByteBuffer來處理,這裏應該還可以用其他的方式處理。代碼如下:

public class AES {

    private static final String AES_MODE = "AES/GCM/NoPadding";

    public static String encrypt(String txt, String alias) throws Exception{
        Cipher cipher = Cipher.getInstance(AES_MODE);

        //每次加密生成不同的IV
        byte[] iv = new SecureRandom().generateSeed(12);
        cipher.init(Cipher.ENCRYPT_MODE, KeyStoreManagement.getAESKey(alias), new IvParameterSpec(iv));

        // 加密
        byte[] encryptedBytes = cipher.doFinal(Base64.decode(txt, Base64.DEFAULT));

        //將密文和iv放一塊
        ByteBuffer byteBuffer = ByteBuffer.allocate(12 + encryptedBytes.length);
        byteBuffer.put(iv);
        byteBuffer.put(encryptedBytes);
        byte[] all = byteBuffer.array();

        // 編碼成string
        return Base64.encodeToString(all, Base64.DEFAULT);
    }

    private String decrypt(String encryptedText, String alias) throws Exception {
        // 使用AES解密
        Cipher cipher = Cipher.getInstance(AES_MODE);

        // 解碼成byte[]
        byte[] encryptedBytes = Base64.decode(encryptedText.getBytes(), Base64.DEFAULT);
        //分解成iv和真正的密文
        ByteBuffer byteBuffer = ByteBuffer.wrap(encryptedBytes);
        byte[] iv = new byte[12];
        byteBuffer.get(iv);
        byte[] realEncryptedBytes = new byte[byteBuffer.remaining()];
        byteBuffer.get(realEncryptedBytes);

        // 解密
        cipher.init(Cipher.DECRYPT_MODE, KeyStoreManagement.getAESKey(alias), new IvParameterSpec(iv));
        byte[] result = cipher.doFinal(realEncryptedBytes);

        //編碼成祕文
        return Base64.encodeToString(result, Base64.DEFAULT);

    }

}

再解決第一點,其實Android系統提供了一套密鑰保存機制KeyStore,獨立於APP,也就是不保存在APP的存儲中,這樣即使手機被ROOT也拿不到密鑰,但是卸載APP或者清除緩存依舊會刪除的。但是在實際開發的時候,發現KeyStore中的AES密鑰管理只支持6.0以上的系統,wtf,這不是歧視低版本手機嗎,不行,換方案。眼角一瞄,發現RSA算法支持6.0以下的系統,

難道用RSA進行加密,有點不符合常理啊。然後一想,可以用RSA對AES密鑰進行管理,這樣就適配所有的版本了。

總結起來,用KeyStore來創建和管理RSA密鑰,用RSA來管理自己生成的AES密鑰。使用時,先用系統KeyStore生成RSA公司鑰,再用安全隨機數生成用於加密普通數據的AES密鑰,用RSA公鑰加密該AES密鑰並保存,加密數據時,取出保存加密過的AES密鑰,用RSA私鑰解密成AES密鑰,然後用該密鑰加密我們需要加密的數據。

萬事具備,代碼完成後,各個版本自測都OK。但測試說你用的RSA填充模式是RSA/ECB/PKCS1Padding,但是這種模式已經被列爲不安全,雖然很想反駁,但是畢竟不安全畢竟要靠這個發一筆呢。你說不安全,那就換模式吧,就用RSA/ECB/OAEPWithSHA-1AndMGF1Padding吧,但是這種只能在6.0以上使用,沒辦法,那就分版本,6.0以上的用安全的填充方式,以下的依舊用原來的,這下OK了。我還是太年輕,在8.0版本翻車了,RSA直接解密失敗,找了一天原因沒有找到(網上說是8.0以上改了加密方式什麼的),官網百度谷歌搜索反正最後沒有解決,如果恰巧你知道或者研究出來,請一定告訴我。

那就換方案,主框架不變,換加密思路。最終解決方法如下(工作密鑰即加密數據的密鑰;根密鑰即加密密鑰的密鑰):

step1.初始化時用KeyStore生成根密鑰,6.0以下生成RSA密鑰,6.0以上生成AES密鑰。

step2.初始化工作密鑰,加密數據用AES加密,用安全隨機數生成AES密鑰即工作密鑰,把工作密鑰用step1中生成的根密鑰加密然後保存。可能會由於業務數據的複雜性,不同類的用不同工作密鑰加密,可能會生成多組工作密鑰。

step3.加密數據時,取出保存的工作密鑰,用step1中的根密鑰解密工作密鑰,代碼中KeyStoreManagement.getAESKey(alias),然後再加密數據。

step4.解密數據時,取出保存的工作密鑰,用step1中的根密鑰解密工作密鑰,代碼中KeyStoreManagement.getAESKey(alias),然後再解密數據。

到此方案結束,如果有什麼問題或建議,歡迎提出。

 

 

 

 

 

 

 

發佈了6 篇原創文章 · 獲贊 0 · 訪問量 2130
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章