記錄使用KeyStore的公鑰和私鑰進(jìn)行數(shù)據(jù)加密养篓,使用SharedPreferences進(jìn)行存儲
背景
在業(yè)務(wù)中碧注,我們可能需要面對在 Android 本地存儲用戶 token基茵、用戶信息等敏感數(shù)據(jù)進(jìn)行加密舱呻。本文將講述一種安全系數(shù)較高的Android本地存儲方案兆旬。
思路
整個方案的核心思路圍繞 KeyStore 展開由桌,如果不太了解 KeyStore 的小伙伴捌斧,請先閱讀Android 密鑰庫系統(tǒng)舆吮。
由于 KeyStore 在 Android 6.0 前后差異較大,并且 Android 4.3 以下系統(tǒng)并不支持 KeyStore编丘,方案需要根據(jù)不同的 Android 版本做不同的處理,以及需要提供降級方案。加解密算法采用 RSA/ECB/PKCS1Padding。
Android6.0及以上版本
這種情況下方案最簡單阳柔,隨機生成2048位RSA key和iv架诞,key存儲在KeyStore中均驶,設(shè)置alias赞警,iv存儲在SharedPreferences中。需要加解密的時候通過alias從KeyStore中取key虏两,從SharedPreferences中取iv愧旦。
1.首先初始化KeyStore
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
private static final String RSA = "RSA";
try {
mKeyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
mKeyStore.load(null);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
2.然后就是生成key了
try {
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setCertificateSubject(new X500Principal("CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US"))
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setKeySize(2048)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setRandomizedEncryptionRequired(false)
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE);
generator.initialize(spec);
generator.generateKeyPair();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
Android 4.3 - 5.1
算法采用 RSA/ECB/PKCS1Padding,隨機生成2048位RSA key和iv碘举,key存儲在KeyStore中忘瓦,設(shè)置alias,iv存儲在SharedPreferences中引颈。需要加解密的時候通過alias從KeyStore中取key耕皮,從SharedPreferences中取iv。
Calendar endDate = Calendar.getInstance();
endDate.add(Calendar.YEAR, 10);
try {
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(mContext.getApplicationContext())
.setAlias(alias)
.setSubject(new X500Principal("CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(Calendar.getInstance().getTime())
.setEndDate(endDate.getTime())
.setKeySize(2048)
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA, ANDROID_KEY_STORE);
generator.initialize(spec);
generator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
數(shù)據(jù)加密
try {
Key key = mKeyStore.getKey(alias, null);
if (null != key) {
//取出密鑰
PublicKey publicKey = mKeyStore.getCertificate(alias).getPublicKey();
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
} else {
Log.e("KeyStoreUtils", "key==null , 沒找到私鑰");
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
數(shù)據(jù)解密
try {
PrivateKey privateKey = (PrivateKey) mKeyStore.getKey(alias, null);
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}