Android使用KeyStore對資料進行加密
談到 Android 安全性話題,Android Developers 官方網站給出了許多很好的建議和講解,涵蓋了儲存資料、許可權、網路、處理憑據、輸入驗證、處理使用者資料、加密等方方面面
金鑰的保護以及網路傳輸安全 應該是移動應用安全最關鍵的內容。Android 提供大量用來保護資料的加密演算法,例如 Cipher 類中提供了 AES 和 RSA 演算法,再例如安全隨機數生成器 SecureRandom 給 KeyGenerator 提供了更加可靠的初始化引數,避免離線攻擊等等。
而如果需要儲存金鑰以供重複使用,Android 提供了 KeyStore 等可以長期儲存和檢索加密金鑰的機制,Android KeyStore 系統特別適合於儲存加密金鑰。”AndroidKeyStore” 是 KeyStore 的一個子集,存進 AndroidKeyStore 的 key 將受到簽名保護,並且這些 key 是存在系統裡的,而不是在 App 的 data 目錄下,依託於硬體的 KeyChain 儲存,可以做到 private key 一旦存入就無法取出,總之,每個 App 自己建立的 key,別的應用是訪問不到的。
KeyStore 提供了兩個能力:
有了這兩個能力,我們的金鑰保護就變得很容易了,你只需要:
在應用安裝後第一次執行時,生成一個隨機金鑰,並存入 KeyStore
當你想儲存一個數據,便從 KeyStore 中取出之前生成的隨機金鑰,對你的資料進行加密,加密完成後,已完成加密的資料可以隨意儲存在任意地方,比如 SharePreferences,此時即使它被他人讀取到,也無法解密出你的原資料,因為他人取不到你的金鑰
當你需要拿到你的原資料,只需要從 SharePreferences 中讀取你加密後的資料,並從 KeyStore 取出加密金鑰,使用加密金鑰對 “加密後的資料” 進行解密即可
其中加密演算法可以使用 Cipher RSA 來保證安全性,不要使用自己創造的加密演算法。
這就是使用 KeyStore 的一整套流程,另外 KeyStore 還可以用來做資料簽名和簽名驗證,就像一個黑匣子一樣,具體可以自行搜尋瞭解。
KeyStore 適於儲存執行時生產獲取到的資料,比如執行時,使用者輸入的密碼,或者服務端傳下來的 token,但無法用於儲存我們需要預設在 App 內的 API key / secret,對於這類需要預設的固定金鑰,我將介紹一種十分安全、難破解的保護方式。
加密:
public String encryptString(String needEncryptWord, String alias) {
if(!"".equals (alias)&&!"".equals(needEncryptWord)){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
initKeyStore(alias);
}
String encryptStr="";
byte [] vals=null;
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
// RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();
if(needEncryptWord.isEmpty()) {
// Toast.makeText(this, "Enter text in the 'Initial Text' widget", Toast.LENGTH_LONG).show();
return encryptStr;
}
// Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// inCipher.init(Cipher.ENCRYPT_MODE, publicKey);
inCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, inCipher);
cipherOutputStream.write(needEncryptWord.getBytes("UTF-8"));
cipherOutputStream.close();
vals = outputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return Base64.encodeToString(vals, Base64.DEFAULT);
}
return "";
}
解密:
public String decryptString(String needDecryptWord, String alias) {
if(!"".equals(alias)&&!"".equals(needDecryptWord)){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
initKeyStore(alias);
}
String decryptStr="";
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
// RSAPrivateKey privateKey = (RSAPrivateKey) privateKeyEntry.getPrivateKey();
// Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// output.init(Cipher.DECRYPT_MODE, privateKey);
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(needDecryptWord, Base64.DEFAULT)), output);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte)nextByte);
}
byte[] bytes = new byte[values.size()];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i).byteValue();
}
decryptStr = new String(bytes, 0, bytes.length, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return decryptStr;
}
return "";
}
原始碼下載地址,我已經將加密解密封裝進了工具類,相容最新的安卓sdk