日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Android外部文件加解密及應(yīng)用實(shí)踐

有這樣的應(yīng)用場(chǎng)景,當(dāng)我們把一些重要文件放到asset文件夾中時(shí),把.apk解壓是可以直接拿到這個(gè)文件的,一些涉及到重要信息的文件我們并不想被反編譯拿去,這個(gè)時(shí)候需要先對(duì)文件進(jìn)行加密,然后放到Android中的資源目錄下,用的時(shí)候再解密出來。

現(xiàn)代密碼學(xué)中,加密系統(tǒng)的安全性是基于密鑰的,而不是基于算法,現(xiàn)在介紹一整套加解密及應(yīng)用流程,這套加密流程從實(shí)用性和安全性上來講,我覺得還是很靠譜的,也是市面上比較常用的做法,核心邏輯其實(shí)比較簡(jiǎn)單,畢竟最難的加解密算法實(shí)現(xiàn)部分是現(xiàn)成的了,我司部分也用了這套流程,當(dāng)然會(huì)比我講的這個(gè)要復(fù)雜一些。

1、簡(jiǎn)介

主要涉及到一下幾個(gè)算法的應(yīng)用,RSA、AES,以及Base64編碼,基本思想是用[AES算法+AES密鑰]來加密文件,為了保證密鑰的安全性,會(huì)通過[RSA算法+RSA私鑰]對(duì)AES密鑰進(jìn)行加密。

對(duì)這幾種算法不熟悉可以看看我司大佬的‘常用的加密方式和應(yīng)用場(chǎng)景’這篇文章,知道大概的原理和使用方法就行,因?yàn)樗惴ㄔ趈ava中都是現(xiàn)成的,直接拿來用就是了。

Android加解密流程圖.png

把流程整理了一下,就是以上的流程圖,分成三塊:

  • 第1塊是把加密過程給封裝成一個(gè)小工具,用加密工具來對(duì)文件進(jìn)行加密;
  • 第2塊是把解密過程封裝成解密的小工具,用解密工具來解密我們的文件好進(jìn)行相關(guān)修改;
  • 第3塊使我們的目的,就是把加密文件和加解密的AES算法密鑰放到Android資源文件中進(jìn)行具體的使用。

有一點(diǎn)需要補(bǔ)充的,就是RSA算法的公私鑰,從第3塊中可以發(fā)現(xiàn),并沒有把RSA的公鑰和私鑰放到資源文件中,其實(shí)大家想想就知道了,如果被加密文件、加解密的AES密鑰、用于對(duì)AES密鑰進(jìn)行加密的RSA密鑰三者都放入文件夾中,那就沒有啥安全性可言了(注:加解密的算法可以改造成自己公司獨(dú)有的,我司就是這么做的),所以為了保證安全性,我們的RSA公私鑰是通過應(yīng)用的簽名(.keystore簽名文件)中代碼動(dòng)態(tài)獲取。感興趣的可以看這篇文章:[從Java Keystore文件中提取私鑰、證書]。

2、第1塊:加密工具進(jìn)行加密

工具的java界面開發(fā)是通過java的swing包來實(shí)現(xiàn)的,對(duì)swing感興趣的可以參考這篇Java Swing 圖形界面開發(fā)簡(jiǎn)介,講得非常詳細(xì)。

一開始的時(shí)候是沒有AES秘鑰的,需要我們生成一個(gè)安全的秘鑰,所以生成一個(gè)隨機(jī)AES秘鑰,然后保存,加密工具的操作頁界面:

加密

2.1、生成隨機(jī)秘鑰

生成隨機(jī)秘鑰主要分為幾步:

  • 通過UUID.randomUUID()生成隨機(jī)數(shù)作為seed種子;
  • seed種子提供給KeyGenerator生成AES秘鑰,只要seed種子生成的AES秘鑰就是一致的;
  • 通過應(yīng)用簽名獲取RSA算法需要的公鑰私鑰;
  • RSA通過私鑰來加密AES秘鑰;

因?yàn)樯傻拿罔€是byte[],所以通過Base64編碼展示出來給到界面上。

 
 
 
 
  1. /**  
  2. * 生成隨機(jī)密鑰  
  3. */  
  4. private void randomKey() {  
  5. try {  
  6. //生成隨機(jī)數(shù)作為seed種子  
  7. String uuid = UUID.randomUUID().toString();  
  8. byte[] seed = uuid.getBytes("UTF-8");  
  9. //生成AES秘鑰  
  10. byte[] rawkey = AES.getRawKey(seed);  
  11. //獲取應(yīng)用簽名的密鑰對(duì)  
  12. KeyPair pair = SignKey.getSignKeyPair();  
  13. //通過RSA私鑰來加密AES秘鑰  
  14. byte[] key = RSA.encrypt(rawkey, pair.getPrivate());  
  15. //Base64編碼成字符串展示  
  16. String base64Key = Base64.encode(key);  
  17. mKeyText.setText(base64Key);  
  18. } catch (Exception e) {  
  19. e.printStackTrace();  
  20. }  
  21. }  
  22. 其中AES.getRawKey(seed)中主要是通過AES密鑰生成器來生成128位的密鑰,具體實(shí)現(xiàn):  
  23. /** 
  24.  * 生成用AES算法來加密的密鑰流,這個(gè)密鑰會(huì)被應(yīng)用簽名{@link SignKey}的密鑰進(jìn)行二次加密  
  25. */  
  26. public static byte[] getRawKey(byte[] seed) throws Exception {  
  27. KeyGenerator kgen = KeyGenerator.getInstance("AES");  
  28. SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");  
  29. sr.setSeed(seed);  
  30. //192 and 256 bits may not be available  
  31. kgen.init(128, sr);  
  32. SecretKey skey = kgen.generateKey();  
  33. return skey.getEncoded();  

SignKey.getSignKeyPair()是獲得RSA算法所需的公私鑰,是從我們的應(yīng)用簽名來的,大家應(yīng)該都很熟悉了,應(yīng)用打包上傳是需要簽名打包的。

keystore

java提供了api獲取testkey.keystore文件(自己用studio生成一個(gè))的私鑰和證書,把testkey.keystore文件放到目錄中:

 
 
 
 
  1. /**  
  2. * Author:xishuang  
  3. * Date:2018.05.06  
  4. * Des:根據(jù)導(dǎo)入的應(yīng)用簽名,讀取其中的密鑰對(duì)和證書  
  5. */  
  6. public class SignKey {  
  7. //應(yīng)用簽名  
  8. private static final String keystoreName = "testkey.keystore";  
  9. private static final String keystorePassword = "123456";  
  10. //應(yīng)用簽名的別名  
  11. private static final String alias = "key0";  
  12. private static final String aliasPassword = "123456";  
  13. /**  
  14. * 獲取簽名的密鑰對(duì),用來給密鑰加密  
  15. */  
  16. public static KeyPair getSignKeyPair() {  
  17. try {  
  18. File storeFile = new File(keystoreName);  
  19. if (!storeFile.exists()) {  
  20. throw new IllegalArgumentException("還沒設(shè)置簽名文件!");  
  21. }  
  22. String keyStoreType = "JKS";  
  23. char[] keystorepasswd = keystorePassword.toCharArray();  
  24. char[] keyaliaspasswd = aliasPassword.toCharArray();  
  25. KeyStore keystore = KeyStore.getInstance(keyStoreType);  
  26. keystore.load(new FileInputStream(storeFile), keystorepasswd);  
  27. //拿私鑰  
  28. Key key = keystore.getKey(alias, keyaliaspasswd);  
  29. if (key instanceof PrivateKey) {  
  30. //拿公鑰  
  31. Certificate cert = keystore.getCertificate(alias);  
  32. PublicKey publicKey = cert.getPublicKey();  
  33. ///公私鑰存到KeyPair 
  34.  return new KeyPair(publicKey, (PrivateKey) key);  
  35. }  
  36. } catch (Exception e) {  
  37. e.printStackTrace();  
  38. }  
  39. return null;  
  40. }  

拿testkey.keystore所需的參數(shù)都在跟我們打包應(yīng)用簽名所需一樣,通過java提供的keystore類獲取。然后就是用剛拿到的testkey.keystore私鑰來加密AES密鑰,再通過Base64轉(zhuǎn)換一下編碼成字符串展示出來,只是為了把密鑰展示出來才轉(zhuǎn)換編碼的。

密鑰Base64編碼.png

2.2、導(dǎo)出密鑰

把密鑰導(dǎo)出成文件,下次直接導(dǎo)入密鑰用來解密文件,導(dǎo)出密鑰需要先用Base64把文本框里的Base64密鑰字符串轉(zhuǎn)換為Byte[]再存。

 
 
 
 
  1. byte[] key = Base64.decode(base64Key);  
  2. //將raw key輸出  
  3. File keyFile = new File(dir, "testkey.dat");  
  4. FileOutputStream fos = new FileOutputStream(keyFile); 

2.3、加密文件

密鑰已有,AES算法又是現(xiàn)成的,直接調(diào)用api加密就行了:

 
 
 
 
  1. private static final String AES = "AES";  
  2. /**  
  3. * AES算法加密文件  
  4. *  
  5. * @param rawKey AES密鑰  
  6. * @param fromFile 要加密的文件  
  7. * @param toFile 加密后文件  
  8. */  
  9. public static void encryptFile(byte[] rawKey, File fromFile, File toFile) throws Exception {  
  10. if (!fromFile.exists()) {  
  11. throw new NullPointerException("文件不存在");  
  12. }  
  13. if (toFile.exists()) {  
  14. toFile.delete();  
  15. }  
  16. SecretKeySpec skeySpec = new SecretKeySpec(rawKey, AES);  
  17. Cipher cipher = Cipher.getInstance(AES); 
  18.  //加密模式  
  19. cipher.init(Cipher.ENCRYPT_MODE, skeySpec);  
  20. FileInputStream fis = new FileInputStream(fromFile);  
  21. FileOutputStream fos = new FileOutputStream(toFile, true);  
  22. byte[] buffer = new byte[512 * 1024 - 16];  
  23. int offset; 
  24. //使用加密流來加密  
  25. CipherInputStream bis = new CipherInputStream(fis, cipher);  
  26. while ((offset = bis.read(buffer)) != -1) { 
  27.  fos.write(buffer, 0, offset);  
  28. fos.flush();  
  29. }  
  30. fos.close();  
  31. fis.close();  

選擇文件,通過AES算法和AES密鑰加密,最后效果如下,沒有密鑰能解密出來算我輸。

加密前后

3、第2塊:解密工具進(jìn)行解密

解密過程其實(shí)沒啥必要講了,因?yàn)榻饷苓^程是加密過程的逆過程。

這個(gè)解密不是在應(yīng)用中用的,是為了便于我們更新加密文件,修改文件之前必須要先把文件先解密。

解密

3.1、導(dǎo)入AES密鑰

這個(gè)密鑰就是我們前面生成的密鑰,導(dǎo)進(jìn)來后用應(yīng)用簽名的RSA公鑰解密AES密鑰即可:

 
 
 
 
  1. //獲取被加密的密鑰raw key  
  2. String keyStr = mKeyText.getText(); 
  3. byte[] key = Base64.decode(keyStr);  
  4. //獲取應(yīng)用簽名密鑰對(duì),公鑰解密raw key  
  5. KeyPair keypair = SignKey.getSignKeyPair();  
  6. byte[] rawkey = RSA.decrypt(key, keypair.getPublic());  
  7. //用raw key去解密文件  
  8. AES.decryptFile(rawkey, fromFile, toFile); 

3.2、解密文件

拿到純潔版AES密鑰之后就可以直接調(diào)用AES算法解密文件了:

 
 
 
 
  1. /**  
  2. * AES算法解密文件  
  3. *  
  4. * @param rawKey AES密鑰  
  5. * @param fromFile 被加密的文件  
  6. * @param toFile 解密后文件  
  7. */  
  8. public static void decryptFile(byte[] rawKey, File fromFile, File toFile) throws Exception {  
  9. if (!fromFile.exists()) {  
  10. throw new NullPointerException("文件不存在");  
  11. }  
  12. if (toFile.exists()) {  
  13. toFile.delete();  
  14. }  
  15. SecretKeySpec skeySpec = new SecretKeySpec(rawKey, AES);  
  16. Cipher cipher = Cipher.getInstance(AES);  
  17. //解密模式  
  18. cipher.init(Cipher.DECRYPT_MODE, skeySpec);  
  19. FileInputStream fis = new FileInputStream(fromFile);  
  20. FileOutputStream fos = new FileOutputStream(toFile, true);  
  21. byte[] buffer = new byte[512 * 1024 + 16];  
  22. int offset;  
  23. //使用解密流來解密  
  24. CipherInputStream cipherInputStream = new CipherInputStream(fis, cipher);  
  25. while ((offset = cipherInputStream.read(buffer)) != -1) {  
  26. fos.write(buffer, 0, offset);  
  27. fos.flush();  
  28. }  
  29. fos.close();  
  30. fis.close();  

和AES加密過程一對(duì)比,會(huì)發(fā)現(xiàn)只是切換一下AES算法模式。

3、第3塊:Android應(yīng)用中解密文件

要解密文件,需要在資源文件夾中加入被加密的AES密鑰,這個(gè)密鑰就是上面導(dǎo)出來的,還有就是被加密后的文件。能正確解密的前提是你應(yīng)用簽名和用來給文件加密過程中用到的簽名是同一個(gè)。

資源文件

3.1、解密AES密鑰

在Android應(yīng)用中解密文件與在java工具中解密文件,區(qū)別主要在于RSA密鑰的獲取,在java工具中應(yīng)用簽名testkey.keystore是開發(fā)者擁有的,可以拿到其中的全部信息,而在Android中應(yīng)用是要發(fā)布到應(yīng)用市場(chǎng)的,任何人都可以下載我們的包,應(yīng)用簽名只能通過Android提供的api拿到其公鑰。

 
 
 
 
  1. /**  
  2. * Author:xishuang  
  3. * Date:2018.05.06  
  4. * Des:應(yīng)用簽名讀取工具類  
  5. */  
  6. public class SignKey {  
  7. /**  
  8. * 獲取當(dāng)前應(yīng)用的簽名  
  9. *  
  10. * @param context 上下文  
  11. */  
  12. public static byte[] getSign(Context context) {  
  13. PackageManager pm = context.getPackageManager(); 
  14. try {  
  15. PackageInfo info = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);  
  16. Signature[] signatures = info.signatures;  
  17. if (signatures != null) {  
  18. return signatures[0].toByteArray();  
  19. }  
  20. } catch (NameNotFoundException e) {  
  21. e.printStackTrace();  
  22. }  
  23. return null;  
  24. }  
  25. /**  
  26. * 根據(jù)簽名去獲取公鑰  
  27. */  
  28. public static PublicKey getPublicKey(byte[] signature) {  
  29. try {  
  30. CertificateFactory certFactory = CertificateFactory  
  31. .getInstance("X.509");  
  32. X509Certificate cert = (X509Certificate) certFactory  
  33. .generateCertificate(new ByteArrayInputStream(signature));  
  34. return cert.getPublicKey();  
  35. } catch (CertificateException e) {  
  36. e.printStackTrace();  
  37.  return null;  
  38. }  

拿到應(yīng)用簽名testkey.keystore的公鑰之后的流程就和在java工具中的操作基本一致了,用RSA公鑰來解密AES密鑰。

 
 
 
 
  1. private static final String SIMPLE_KEY_DATA = "testkey.dat";  
  2. /**  
  3. * 獲取解密之后的文件加密密鑰  
  4. */  
  5. private static byte[] getRawKey(Context context) throws Exception {  
  6. //獲取應(yīng)用的簽名密鑰  
  7. byte[] sign = SignKey.getSign(context);  
  8. PublicKey pubKey = SignKey.getPublicKey(sign);  
  9. //獲取加密文件的密鑰  
  10. InputStream keyis = context.getAssets().open(SIMPLE_KEY_DATA);  
  11. byte[] key = getData(keyis);  
  12. //解密密鑰  
  13. return RSA.decrypt(key, pubKey);  

最后再用解密之后的AES密鑰來解密文件。

3.2、AES密鑰解密文件

通過資源管理器拿到加密文件的文件流,通過AES密鑰來用AES算法來解密文件流。

 
 
 
 
  1. /**  
  2. * 獲取解密之后的文件流  
  3. */  
  4. public static InputStream onObtainInputStream(Context context) {  
  5. try {  
  6. AssetManager assetmanager = context.getAssets();  
  7. InputStream is = assetmanager.open("encrypt_測(cè)試.txt");  
  8. byte[] rawkey = getRawKey(context);  
  9. //使用解密流,數(shù)據(jù)寫出到基礎(chǔ)OutputStream之前先對(duì)該會(huì)先對(duì)數(shù)據(jù)進(jìn)行解密  
  10. SecretKeySpec skeySpec = new SecretKeySpec(rawkey, "AES");  
  11. Cipher cipher = Cipher.getInstance("AES");  
  12. cipher.init(Cipher.DECRYPT_MODE, skeySpec);  
  13. return new CipherInputStream(is, cipher);  
  14. } catch (Exception e) {  
  15. e.printStackTrace();  
  16. }  
  17. return null;  

拿到加密后文件流之后就達(dá)成目的了,可以解析成字符串展示出來:

 
 
 
 
  1. private void inputData() {  
  2. InputStream in = DecryptUtil.onObtainInputStream(this);  
  3. try {  
  4. BufferedReader reader = new BufferedReader(new InputStreamReader(in, "GBK"));  
  5. StringBuilder sb = new StringBuilder();  
  6. String line;  
  7. while ((line = reader.readLine()) != null) {  
  8. sb.append(line + "\n");  
  9. }  
  10. contentTv.setText(sb.toString());  
  11. } catch (IOException e) {  
  12. e.printStackTrace();  
  13. } finally {  
  14. try {  
  15. in.close();  
  16. } catch (IOException e) {  
  17. e.printStackTrace();  
  18. }  
  19. }  

實(shí)例效果圖如下,請(qǐng)關(guān)注紅框里面內(nèi)容,因?yàn)閼械眯陆?xiàng)目,用原有項(xiàng)目測(cè)試了一下:

效果

目前工具使用的是市面上比較常見的加解密算法,可以換一下算法,比如DES或者其它的對(duì)稱和非對(duì)稱算法,甚至是自己改動(dòng)的算法,想運(yùn)行示例演示的話:

QQ截圖20180508234849.png

就是運(yùn)行一下java文件,就可以打開加解密小工具了,加解密工具界面是仿我司工具包中抽出來的小部分的,畢竟寫界面好煩,感謝我司大神多年前就寫出了如此工具。


文章名稱:Android外部文件加解密及應(yīng)用實(shí)踐
網(wǎng)站鏈接:http://m.5511xx.com/article/cocdgjg.html