使用Android KeyStore 儲存敏感性資料

Joe Tsai
Joe Blog
Published in
5 min readJun 8, 2017

範例

可以參考一下我簡單寫的範例 KeyStoreDemo

使用情境

有時候我們會希望將一些資訊存在App中,比方說當使用者勾取「記住我的帳號」,那麼下次重新開啟App時,使用者帳號應該就要直接出現在相對應的欄位上。

在Android手機上,大致上有三個地方可以存取資料:

  1. SharedPreferences
  2. SQLite
  3. Internal Storage

若直接將這些敏感性資料以明文的方式存入SharedPreferences / SQLite / Internal Storage中,即是讓這些資料暴露於風險之中,因為只要駭客擁有硬體的權限(Root),即可將這些資訊一覽無遺。

這邊要強調一點,「沒有什麼事情是100%安全的,只要有存,就有風險。」如果不想要這樣的風險發生,最好的方式就是不要將敏感性資料(帳號、密碼、信用卡號碼、Token⋯⋯)存在手機上,但⋯⋯很多時候我們還是會需要存放這些敏感性資料的,那到底該怎麼做呢 ?通常我們會「先加密這些資訊之後再存放到手機上,至少讓這些資訊不是明文可見的」

三種產生加解密Key的方式(依照容易破解程度排序):

  1. 將Key寫死在程式碼中 (Hard Code)
  2. Android KeyStore
  3. Key由Server端產生

這邊會介紹第二種方式Android KeyStore

Android Keystore System

主要提供了三種功能:

  1. 隨機產生Key
  2. 限制Key的用途(加密、解密、認證⋯⋯)
  3. 安全地存放Key(至少官方是這麼說的)

知道KeyStore的功能之後,我們就可以透過以下方式存取敏感性資料。

實作方式

建議做法

1. 使用對稱式加解密,但只能在Api Level 23+使用

對稱式加解密(AES)速度較快,但是對稱式的Key若要存在KeyStore裡,Api level一定要在23以上才有支援,23以下是無法存入KeyStore的,非對稱式的Key則不在此限。

2. 想兼容各Api版本(23以下也能用)

  • 若要存取的東西不多、字串長度也不長:直接使用非對稱式加解密即可
  • 若要存取的東西很多或字串長度很長:由於非對稱式加解密速度較慢,使用非對稱式+對稱式加解密可以解決此問題。

考慮到加解密效能、版本兼容,下面會介紹用非對稱式+對稱式來加解密。

KeyStore非對稱+對稱式加解密流程

  1. 使用KeyStore產生隨機的RSA Key
  2. 產生AES Key,並用RSA Public Key加密後存入SharedPrefs
  3. 從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

23以上使用 KeyGenParameterSpec,23以下使用KeyPairGeneratorSpec

產生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越大,加解密時速度就越慢。

RSA Supported sizes: 512, 768, 1024, 2048, 3072, 4096

--

--