Android使用KeyStore對(duì)數(shù)據(jù)進(jìn)行加密的示例代碼
談到 Android 安全性話題,Android Developers 官方網(wǎng)站給出了許多很好的建議和講解,涵蓋了存儲(chǔ)數(shù)據(jù)、權(quán)限、網(wǎng)絡(luò)、處理憑據(jù)、輸入驗(yàn)證、處理用戶數(shù)據(jù)、加密等方方面面
密鑰的保護(hù)以及網(wǎng)絡(luò)傳輸安全 應(yīng)該是移動(dòng)應(yīng)用安全最關(guān)鍵的內(nèi)容。Android 提供大量用來保護(hù)數(shù)據(jù)的加密算法,例如 Cipher 類中提供了 AES 和 RSA 算法,再例如安全隨機(jī)數(shù)生成器 SecureRandom 給 KeyGenerator 提供了更加可靠的初始化參數(shù),避免離線攻擊等等。
而如果需要存儲(chǔ)密鑰以供重復(fù)使用,Android 提供了 KeyStore 等可以長期存儲(chǔ)和檢索加密密鑰的機(jī)制,Android KeyStore 系統(tǒng)特別適合于存儲(chǔ)加密密鑰。”AndroidKeyStore” 是 KeyStore 的一個(gè)子集,存進(jìn) AndroidKeyStore 的 key 將受到簽名保護(hù),并且這些 key 是存在系統(tǒng)里的,而不是在 App 的 data 目錄下,依托于硬件的 KeyChain 存儲(chǔ),可以做到 private key 一旦存入就無法取出,總之,每個(gè) App 自己創(chuàng)建的 key,別的應(yīng)用是訪問不到的。
KeyStore 提供了兩個(gè)能力:
有了這兩個(gè)能力,我們的密鑰保護(hù)就變得很容易了,你只需要:
在應(yīng)用安裝后第一次運(yùn)行時(shí),生成一個(gè)隨機(jī)密鑰,并存入 KeyStore
當(dāng)你想存儲(chǔ)一個(gè)數(shù)據(jù),便從 KeyStore 中取出之前生成的隨機(jī)密鑰,對(duì)你的數(shù)據(jù)進(jìn)行加密,加密完成后,已完成加密的數(shù)據(jù)可以隨意存儲(chǔ)在任意地方,比如 SharePreferences,此時(shí)即使它被他人讀取到,也無法解密出你的原數(shù)據(jù),因?yàn)樗巳〔坏侥愕拿荑€
當(dāng)你需要拿到你的原數(shù)據(jù),只需要從 SharePreferences 中讀取你加密后的數(shù)據(jù),并從 KeyStore 取出加密密鑰,使用加密密鑰對(duì) “加密后的數(shù)據(jù)” 進(jìn)行解密即可
其中加密算法可以使用 Cipher AES 來保證安全性,不要使用自己創(chuàng)造的加密算法。
這就是使用 KeyStore 的一整套流程,另外 KeyStore 還可以用來做數(shù)據(jù)簽名和簽名驗(yàn)證,就像一個(gè)黑匣子一樣,具體可以自行搜索了解。
KeyStore 適于存儲(chǔ)運(yùn)行時(shí)生產(chǎn)獲取到的數(shù)據(jù),比如運(yùn)行時(shí),用戶輸入的密碼,或者服務(wù)端傳下來的 token,但無法用于存儲(chǔ)我們需要預(yù)設(shè)在 App 內(nèi)的 API key / secret,對(duì)于這類需要預(yù)設(shè)的固定密鑰,我將介紹一種十分安全、難破解的保護(hù)方式。
加密:
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 "";
}
源碼下載地址,我已經(jīng)將加密解密封裝進(jìn)了工具類,并對(duì)Android 7.0的兼容也處理了
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android通過ViewModel保存數(shù)據(jù)實(shí)現(xiàn)多頁面的數(shù)據(jù)共享功能
這篇文章主要介紹了Android通過ViewModel保存數(shù)據(jù)實(shí)現(xiàn)多頁面的數(shù)據(jù)共享功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-11-11
Android Mouse實(shí)現(xiàn)過程詳細(xì)筆記
鼠標(biāo)的實(shí)現(xiàn)有兩個(gè)步驟,一個(gè)是所有層上面的一個(gè)圖標(biāo),還有一個(gè)就是事件控制2013-09-09
Android系統(tǒng)的五種數(shù)據(jù)存儲(chǔ)形式實(shí)例(二)
Android系統(tǒng)有五種數(shù)據(jù)存儲(chǔ)形式,分別是文件存儲(chǔ)、SP存儲(chǔ)、數(shù)據(jù)庫存儲(chǔ)、contentprovider 內(nèi)容提供者、網(wǎng)絡(luò)存儲(chǔ)。本文介紹了Android系統(tǒng)的五種數(shù)據(jù)存儲(chǔ)形式,有興趣的可以了解一下。2016-12-12
Android高級(jí)開發(fā)之性能優(yōu)化典范
本文從電量,視圖,內(nèi)存三個(gè)性能方面的知識(shí)點(diǎn)給大家介紹android高級(jí)開發(fā)之性能優(yōu)化的相關(guān)知識(shí),希望對(duì)大家有所幫助2016-05-05
Android ListView與getView調(diào)用卡頓問題解決辦法
這篇文章主要介紹了Android ListView與getView調(diào)用卡頓問題解決辦法的相關(guān)資料,這里提供實(shí)例及解決辦法幫助大家解決這種問題,需要的朋友可以參考下2017-08-08
Android中監(jiān)聽判斷網(wǎng)絡(luò)連接狀態(tài)的方法
這篇文章主要介紹了Android中監(jiān)聽判斷網(wǎng)絡(luò)連接狀態(tài)的方法,介紹了是否有網(wǎng)絡(luò)連接判斷、連接的類型和監(jiān)聽網(wǎng)絡(luò)狀態(tài)的方法,需要的朋友可以參考下2014-06-06
android 幀動(dòng)畫,補(bǔ)間動(dòng)畫,屬性動(dòng)畫的簡單總結(jié)
本文主要對(duì)android 幀動(dòng)畫,補(bǔ)間動(dòng)畫,屬性動(dòng)畫進(jìn)行了簡單總結(jié),具有一定的參考價(jià)值,下面跟著小編一起來看下吧2017-01-01
Android中使用TabHost 與 Fragment 制作頁面切換效果
這篇文章主要介紹了Android中使用TabHost 與 Fragment 制作頁面切換效果的相關(guān)資料,需要的朋友可以參考下2016-03-03

