範例
可以參考一下我簡單寫的範例 KeyStoreDemo
使用情境
有時候我們會希望將一些資訊存在App中,比方說當使用者勾取「記住我的帳號」,那麼下次重新開啟App時,使用者帳號應該就要直接出現在相對應的欄位上。
在Android手機上,大致上有三個地方可以存取資料:
若直接將這些敏感性資料以明文的方式存入SharedPreferences / SQLite / Internal Storage中,即是讓這些資料暴露於風險之中,因為只要駭客擁有硬體的權限(Root),即可將這些資訊一覽無遺。
這邊要強調一點,「沒有什麼事情是100%安全的,只要有存,就有風險。」如果不想要這樣的風險發生,最好的方式就是不要將敏感性資料(帳號、密碼、信用卡號碼、Token⋯⋯)存在手機上,但⋯⋯很多時候我們還是會需要存放這些敏感性資料的,那到底該怎麼做呢 ?通常我們會「先加密這些資訊之後再存放到手機上,至少讓這些資訊不是明文可見的」。
三種產生加解密Key的方式(依照容易破解程度排序):
- 將Key寫死在程式碼中 (Hard Code)
- Android KeyStore
- Key由Server端產生
這邊會介紹第二種方式Android KeyStore
Android Keystore System
主要提供了三種功能:
- 隨機產生Key
- 限制Key的用途(加密、解密、認證⋯⋯)
- 安全地存放Key(至少官方是這麼說的)
知道KeyStore的功能之後,我們就可以透過以下方式存取敏感性資料。
實作方式
- 存:使用Keystore 加密資訊後存入SharedPreferences
- 取:從SharedPreferences取出資訊並使用Keystore 解密
建議做法
1. 使用對稱式加解密,但只能在Api Level 23+使用
對稱式加解密(AES)速度較快,但是對稱式的Key若要存在KeyStore裡,Api level一定要在23以上才有支援,23以下是無法存入KeyStore的,非對稱式的Key則不在此限。
2. 想兼容各Api版本(23以下也能用)
- 若要存取的東西不多、字串長度也不長:直接使用非對稱式加解密即可
- 若要存取的東西很多或字串長度很長:由於非對稱式加解密速度較慢,使用非對稱式+對稱式加解密可以解決此問題。
考慮到加解密效能、版本兼容,下面會介紹用非對稱式+對稱式來加解密。
KeyStore非對稱+對稱式加解密流程
- 使用KeyStore產生隨機的RSA Key
- 產生AES Key,並用RSA Public Key加密後存入SharedPrefs
- 從SharedPrefs取出AES Key,並用RSA Private Key解密,用這把AES Key來加解密資料
產生隨機的RSA Key
首先,使用KeyStore產生RSA的Key(如果這個Key不存在KeyStore才產生)
產生RSA Key會使用到KeyPairGenerator,其中KeyPairGeneratorSpec在Api 23以上已經Deprecated了,Api level 23以上改使用KeyGenParameterSpec。
產生AES Key
產生要用來加解密的AES Key及IV,用RSA的Public Key加密後存入SharedPrefrences(IV也可以一起加密),建議AES Key長度至少要128 bits以上才比較安全。
取出AES Key
從SharedPrefs取出AES Key,並用RSA Private Key解密,之後就可以用這把AES Key來加/解密囉!
加密
這邊用Cipher來加/解密,用法很簡單,初始化好之後呼叫doFinal即可完成加/解密。doFinal回傳的byte即為將加/解密過後的結果,這邊我把結果轉成Base64並存入SharedPrefs。
解密
跟加密大同小異,先從SharedPrefs取出編碼為Base64的加密字串,並解碼Base64,再使用Cipher來解密。
番外篇
使用RSA加解密時,在較低版本的手機上可能無法選擇OAEP這個模式,因此可以改使用RSA_PKCS1_PADDING模式,使用這個模式的話,輸入必須比RSA的Key至少少11個字元,如果需要被加密的字串過長的話,可以在產生Key時指定Key Size長度,或是將字串分段加密。
以預設Key Size = 2048bit(256byte)來說,輸入最長只能到256–11=245byte,我們可以透過setKeySize(int keySize)指定Key的長度,但是Key Size越大,加解密時速度就越慢。