Android Keystore 對稱-非對稱加密

Android數據加密:

Anroid數據加密方式html

Android 提供了 KeyStore 等能夠長期存儲和檢索加密密鑰的機制,Android KeyStore 系統特別適合於存儲加密密鑰。

「AndroidKeyStore」 是 KeyStore 的一個子集,存進 AndroidKeyStore 的 key 將受到簽名保護,而且這些 key 是存在系統裏的,而不是在 App 的 data 目錄下,依託於硬件的 KeyChain 存儲,能夠作到 private key 一旦存入就沒法取出,

每一個 App 本身建立的 key,別的應用是訪問不到的。

它提供了限制什麼時候以何種方式使用密鑰的方法,好比使用密鑰時須要用戶認證或限制密鑰只能在加密模式下使用。

一個應用程式只能編輯、保存、取出本身的密鑰。

App能夠生成或者接收一個公私密鑰對,並存儲在Android的Keystore系統中。公鑰能夠用於在應用數據放置到特定文件夾前對數據進行加密,私鑰能夠在須要的時候解密相應的數據。

做用:java

KeyStore 適用於生成和存儲密鑰,這些密鑰能夠用來加密運行時獲取到的數據,好比運行時,用戶輸入的密碼,或者服務端傳下來的 token。android

操做方式

建議作法

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

對稱式加解密(AES)速度較快,可是對稱式的Key若要存在KeyStore裡,Api level必定要在23以上才支持,23如下是沒法存入KeyStore的,非對稱式的Key則不在此限。git

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

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

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

 

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

  1. 使用KeyStore產生隨機的RSA Key;
  2. 產生AES Key,並用RSA Public Key加密後存入SharedPrefs;
  3. 從SharedPrefs取出AES Key,並用RSA Private Key解密,用這把AES Key來加解密信息;

 

主流的加密方式有:(對稱加密)AES、DES        (非對稱加密)RSA、DSAapi

工做模式:緩存

DES一共有:服務器

電子密碼本模式(ECB)、加密分組連接模式(CBC)、加密反饋模式(CFB)、輸出反饋模式(OFB);app

AES一共有:dom

電子密碼本模式(ECB)、加密分組連接模式(CBC)、加密反饋模式(CFB)、輸出反饋模式(OFB)、計數器模式(CTR),伽羅瓦計數器模式(GCM

PKCS5Padding是填充模式,還有其它的填充模式;

對於初始化向量iv: 初始化向量參數,AES 爲16bytes. DES 爲8bytes

 

1 private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";
2 private static final String AES_MODE = "AES/GCM/NoPadding";
3 private static final String RSA_MODE = "RSA/ECB/PKCS1Padding";
4 
5 private static final String KEYSTORE_ALIAS = "KEYSTORE_DEMO";
6 
7 KeyStore mKeyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
8 mKeyStore.load(null);

(1)產生隨機的RSA Key

產生RSA Key會使用到KeyPairGenerator

其中KeyPairGeneratorSpec在Api 23以上已經Deprecated了;

Api level 23以上改使用KeyGenParameterSpec

1 private void genKeyStoreKey(Context context) throws Exception {
2         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
3             generateRSAKey_AboveApi23();
4         } else {
5             generateRSAKey_BelowApi23(context);
6         }
7     }

api23 以上使用 KeyGenParameterSpec

 1 @RequiresApi(api = Build.VERSION_CODES.M)
 2     private void generateRSAKey_AboveApi23() throws Exception {
 3         KeyPairGenerator keyPairGenerator = KeyPairGenerator
 4                 .getInstance(KeyProperties.KEY_ALGORITHM_RSA, KEYSTORE_PROVIDER);
 5 
 6 
 7         KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec
 8                 .Builder(KEYSTORE_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
 9                 .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
10                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
11                 .build();
12 
13         keyPairGenerator.initialize(keyGenParameterSpec);
14         keyPairGenerator.generateKeyPair();
15 
16     }

api23 如下使用 KeyPairGeneratorSpec

 1 private void generateRSAKey_BelowApi23(Context context) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
 2         Calendar start = Calendar.getInstance();
 3         Calendar end = Calendar.getInstance();
 4         end.add(Calendar.YEAR, 30);
 5 
 6         KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
 7                 .setAlias(KEYSTORE_ALIAS)
 8                 .setSubject(new X500Principal("CN=" + KEYSTORE_ALIAS))
 9                 .setSerialNumber(BigInteger.TEN)
10                 .setStartDate(start.getTime())
11                 .setEndDate(end.getTime())
12                 .build();
13 
14         KeyPairGenerator keyPairGenerator = KeyPairGenerator
15                 .getInstance(KeyProperties.KEY_ALGORITHM_RSA, KEYSTORE_PROVIDER);
16 
17         keyPairGenerator.initialize(spec);
18         keyPairGenerator.generateKeyPair();
19     }

(2)產生AES Key後, 並用RSA Public Key加密後存入SharedPrefs

 1 private void genAESKey() throws Exception {
 2         // Generate AES-Key
 3         byte[] aesKey = new byte[16];
 4         SecureRandom secureRandom = new SecureRandom();
 5         secureRandom.nextBytes(aesKey);
 6 
 7         // Generate 12 bytes iv then save to SharedPrefs
 8         byte[] generated = secureRandom.generateSeed(12);
 9         String iv = Base64.encodeToString(generated, Base64.DEFAULT);
10         prefsHelper.setIV(iv);
12 
13         // Encrypt AES-Key with RSA Public Key then save to SharedPrefs
14         String encryptAESKey = encryptRSA(aesKey);
15         prefsHelper.setAESKey(encryptAESKey);
16     }

  1] 加密存儲:使用RSA Public Key 加密 AES Key,存入緩存中。

  2]  解密使用:使用RSA Private Key 解密 獲得 AES Key。

 1 private String encryptRSA(byte[] plainText) throws Exception {
 2         PublicKey publicKey = keyStore.getCertificate(KEYSTORE_ALIAS).getPublicKey();
 3 
 4         Cipher cipher = Cipher.getInstance(RSA_MODE);
 5         cipher.init(Cipher.ENCRYPT_MODE, publicKey);
 6 
 7         byte[] encryptedByte = cipher.doFinal(plainText);
 8         return Base64.encodeToString(encryptedByte, Base64.DEFAULT);
 9    }
11 
12     private byte[] decryptRSA(String encryptedText) throws Exception {
13         PrivateKey privateKey = (PrivateKey) keyStore.getKey(KEYSTORE_ALIAS, null);
14 
15         Cipher cipher = Cipher.getInstance(RSA_MODE);
16         cipher.init(Cipher.DECRYPT_MODE, privateKey);
17 
18         byte[] encryptedBytes = Base64.decode(encryptedText, Base64.DEFAULT);
19         byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
20 
21         return decryptedBytes;
22     }

獲取AES :

1 private SecretKeySpec getAESKey() throws Exception {
2         String encryptedKey = prefsHelper.getAESKey(); 3 byte[] aesKey = decryptRSA(encryptedKey); 4 5 return new SecretKeySpec(aesKey, AES_MODE); 6 }

再使用AES 加解密內容:

對於:Cipher 初始化

1 //實例化加密類,參數爲加密方式,要寫全
2 Cipher cipher = Cipher.getIntance("AES/CBC/PKCS5Padding");
3 
4 //初始化,此方法能夠採用三種方式,按服務器要求來添加。
5 //(1)無第三個參數
6 //(2)第三個參數爲SecureRandom random = new SecureRandom();
7 // 中random對象,隨機數。(AES不可採用這種方法)
8 //(3)第三個參數:IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes); 
9 cipher.init(Cipher.ENCRYPT_MODE,  keySpec,  ivSpec/random);

具體使用:

 1 /**
 2      * AES Encryption
 3      * @param plainText: A string which needs to be encrypted.
 4      * @return A base64's string after encrypting.
 5      */
 6     private String encryptAES(String plainText) throws Exception {
 7         Cipher cipher = Cipher.getInstance(AES_MODE);
 8         cipher.init(Cipher.ENCRYPT_MODE, getAESKey(), new IvParameterSpec(getIV()));
 9 
10         // 加密過後的byte
11         byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
12 
13         // 將byte轉為base64的string編碼
14         return Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
15     }
17 
18     private String decryptAES(String encryptedText) throws Exception {
19         // 將加密過後的Base64編碼格式 解碼成 byte
20         byte[] decodedBytes = Base64.decode(encryptedText.getBytes(), Base64.DEFAULT);
21 
22         // 將解碼過後的byte 使用AES解密
23         Cipher cipher = Cipher.getInstance(AES_MODE);
24         cipher.init(Cipher.DECRYPT_MODE, getAESKey(), new IvParameterSpec(getIV()));
25 
26         return new String(cipher.doFinal(decodedBytes));
27     }

iv 初始化向量

1 private byte[] getIV() {
2      String prefIV = prefsHelper.getIV();
3      return Base64.decode(prefIV, Base64.DEFAULT);
4 }

 

關於RSA:

使用RSA加解密時,在較低版本的手機上可能沒法選擇OAEP(最優非對稱加密填充,RSA的加密解密是基於OAEP的)這個模式;

所以能夠改使用RSA_PKCS1_PADDING模式,使用這個模式的話,輸入必須比RSA的Key至少11個字節,若是須要被加密的字串過長的話,能夠在產生Key時指定Key Size長度,或是將字串分段加密。

以預設Key Size = 2048bit(256byte)來說,輸入最長只能到256–11=245byte,我們能夠透過setKeySize(int keySize)指定Key的長度,可是Key Size越大,加解密時速度就越慢。

1 KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec
2         .Builder(KEYSTORE_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
3         .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
4         .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
5         .setKeySize(4096)
6         .build();

 

引言:

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

RSA PKCS1 填充方式

Where is the best place to store a password in your Android app?

Securely Storing Secrets in an Android Application

區塊加密法工做模式

相關文章
相關標籤/搜索