当前位置: 代码迷 >> Android >> 使用AES加密进展Android的SharedPreferences存储
  详细解决方案

使用AES加密进展Android的SharedPreferences存储

热度:85   发布时间:2016-04-28 02:00:09.0
使用AES加密进行Android的SharedPreferences存储

1.概述
SharedPreferences是Android提供用来存储一些简单配置信息的机制,其以KEY-VALUE对的方式进行存储,以便我们可以方便进行读取和存储。主要可以用来存储应用程序的欢迎语、常量参数或登录账号密码等。
2.实例
(1)创建项目SharedPreferencesDemo项目

(2)编辑主界面的布局文件main.xml如下:

[xhtml] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7. <TextView   
  8.     android:layout_width="fill_parent"  
  9.     android:layout_height="wrap_content"  
  10.     android:text="SharedPreferences,是Android提供用来存储一些简单的配置信息的一种机制。"  
  11.     />  
  12.     <EditText android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/edtAccount" android:text=""></EditText>  
  13.     <EditText android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/edtPassword" android:text=""></EditText>  
  14.     <Button android:text="清空" android:id="@+id/btnClear" android:layout_width="fill_parent" android:layout_height="wrap_content">    </Button>  
  15.     <Button android:text="退出" android:id="@+id/btnExit" android:layout_width="fill_parent" android:layout_height="wrap_content"></Button>  
  16. </LinearLayout>  


(3)创建AES加解密工具类AESEncryptor.java
其中主要提供加密encrypt、解密decrypt两个方法。(AES加解密算法具体大家可以到网上搜索相关资料)
以下为该类文件的源码:
[java] view plaincopy
  1. package ni.demo.sharedpreferences;  
  2. import java.security.SecureRandom;  
  3. import javax.crypto.Cipher;  
  4. import javax.crypto.KeyGenerator;  
  5. import javax.crypto.SecretKey;  
  6. import javax.crypto.spec.SecretKeySpec;  
  7. /** 
  8.  * AES加密器 
  9.  * @author Eric_Ni 
  10.  * 
  11.  */  
  12. public class AESEncryptor {  
  13.     /** 
  14.      * AES加密 
  15.      */  
  16.     public static String encrypt(String seed, String cleartext) throws Exception {    
  17.         byte[] rawKey = getRawKey(seed.getBytes());    
  18.         byte[] result = encrypt(rawKey, cleartext.getBytes());    
  19.         return toHex(result);    
  20.     }    
  21.         
  22.     /** 
  23.      * AES解密 
  24.      */  
  25.     public static String decrypt(String seed, String encrypted) throws Exception {    
  26.         byte[] rawKey = getRawKey(seed.getBytes());    
  27.         byte[] enc = toByte(encrypted);    
  28.         byte[] result = decrypt(rawKey, enc);    
  29.         return new String(result);    
  30.     }    
  31.    
  32.     private static byte[] getRawKey(byte[] seed) throws Exception {    
  33.         KeyGenerator kgen = KeyGenerator.getInstance("AES");    
  34.         SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");    
  35.         sr.setSeed(seed);    
  36.         kgen.init(128, sr); // 192 and 256 bits may not be available    
  37.         SecretKey skey = kgen.generateKey();    
  38.         byte[] raw = skey.getEncoded();    
  39.         return raw;    
  40.     }    
  41.    
  42.         
  43.     private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {    
  44.         SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");    
  45.         Cipher cipher = Cipher.getInstance("AES");    
  46.         cipher.init(Cipher.ENCRYPT_MODE, skeySpec);    
  47.         byte[] encrypted = cipher.doFinal(clear);    
  48.         return encrypted;    
  49.     }    
  50.    
  51.     private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {    
  52.         SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");    
  53.         Cipher cipher = Cipher.getInstance("AES");    
  54.         cipher.init(Cipher.DECRYPT_MODE, skeySpec);    
  55.         byte[] decrypted = cipher.doFinal(encrypted);    
  56.         return decrypted;    
  57.     }    
  58.    
  59.     public static String toHex(String txt) {    
  60.         return toHex(txt.getBytes());    
  61.     }    
  62.     public static String fromHex(String hex) {    
  63.         return new String(toByte(hex));    
  64.     }    
  65.         
  66.     public static byte[] toByte(String hexString) {    
  67.         int len = hexString.length()/2;    
  68.         byte[] result = new byte[len];    
  69.         for (int i = 0; i < len; i++)    
  70.             result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();    
  71.         return result;    
  72.     }    
  73.    
  74.     public static String toHex(byte[] buf) {    
  75.         if (buf == null)    
  76.             return "";    
  77.         StringBuffer result = new StringBuffer(2*buf.length);    
  78.         for (int i = 0; i < buf.length; i++) {    
  79.             appendHex(result, buf[i]);    
  80.         }    
  81.         return result.toString();    
  82.     }    
  83.     private final static String HEX = "0123456789ABCDEF";    
  84.     private static void appendHex(StringBuffer sb, byte b) {    
  85.         sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));    
  86.     }    
  87. }  


(4)编辑SharedPreferencesDemo.java
源码如下:
[java] view plaincopy
  1. package ni.demo.sharedpreferences;  
  2. import android.app.Activity;  
  3. import android.content.SharedPreferences;  
  4. import android.content.SharedPreferences.Editor;  
  5. import android.os.Bundle;  
  6. import android.view.View;  
  7. import android.view.View.OnClickListener;  
  8. import android.widget.Button;  
  9. import android.widget.EditText;  
  10. import android.widget.Toast;  
  11. public class SharedPreferencesDemo extends Activity {  
  12.     public static final String MY_PREFERENCES = "MY_PREFERENCES";    //Preferences文件的名称  
  13.     public static final String MY_ACCOUNT = "MY_ACCOUNT";            //  
  14.     public static final String MY_PASSWORD = "MY_PASSWORD";  
  15.      
  16.     private EditText edtAccount;  
  17.     private EditText edtPassword;  
  18.     private Button btnClear;  
  19.     private Button btnExit;  
  20.      
  21.     @Override  
  22.     public void onCreate(Bundle savedInstanceState) {  
  23.         super.onCreate(savedInstanceState);  
  24.         setContentView(R.layout.main);  
  25.         
  26.         edtAccount = (EditText)findViewById(R.id.edtAccount);  
  27.         edtPassword = (EditText)findViewById(R.id.edtPassword);  
  28.         //获取名字为“MY_PREFERENCES”的参数文件对象,并获得MYACCOUNT、MY_PASSWORD元素的值。  
  29.         SharedPreferences sp = this.getSharedPreferences(MY_PREFERENCES, 0);  
  30.         String account = sp.getString(MY_ACCOUNT, "");  
  31.         String password = sp.getString(MY_PASSWORD, "");  
  32.         //对密码进行AES解密  
  33.         try{  
  34.             password = AESEncryptor.decrypt("41227677", password);  
  35.         }catch(Exception ex){  
  36.             Toast.makeText(this"获取密码时产生解密错误!", Toast.LENGTH_SHORT);  
  37.             password = "";  
  38.         }  
  39.         //将账号和密码显示在EditText控件上。  
  40.         edtAccount.setText(account);  
  41.         edtPassword.setText(password);  
  42.          
  43.         //获取"清空"按钮的对象,并为其绑定监听器,如被点击则清空账号和密码控件的值。  
  44.         btnClear = (Button)findViewById(R.id.btnClear);  
  45.         btnClear.setOnClickListener(new OnClickListener(){  
  46.             @Override  
  47.             public void onClick(View arg0) {  
  48.                 edtAccount.setText("");  
  49.                 edtPassword.setText("");  
  50.             }             
  51.         });  
  52.         //获取“退出”按钮的对象,并为其绑定监听,如被点击则退出程序。  
  53.         btnExit = (Button)findViewById(R.id.btnExit);  
  54.         btnExit.setOnClickListener(new OnClickListener(){  
  55.             @Override  
  56.             public void onClick(View arg0) {  
  57.                 SharedPreferencesDemo.this.finish();  
  58.             }     
  59.         });  
  60.     }  
  61.     @Override  
  62.     protected void onStop() {  
  63.         super.onStop();  
  64.         //获得账号、密码控件的值,并使用AES加密算法给密码加密。  
  65.         String account = edtAccount.getText().toString();  
  66.         String password = edtPassword.getText().toString();  
  67.         try{  
  68.             password = AESEncryptor.encrypt("41227677", password);  
  69.         }catch(Exception ex){  
  70.             Toast.makeText(this"给密码加密时产生错误!", Toast.LENGTH_SHORT);  
  71.             password = "";  
  72.         }  
  73.         //获取名字为“MY_PREFERENCES”的参数文件对象。  
  74.         SharedPreferences sp = this.getSharedPreferences(MY_PREFERENCES, 0);  
  75.         //使用Editor接口修改SharedPreferences中的值并提交。  
  76.         Editor editor = sp.edit();  
  77.         editor.putString(MY_ACCOUNT, account);  
  78.         editor.putString(MY_PASSWORD,password);  
  79.         editor.commit();  
  80.     }  
  81.      
  82.      
  83. }  


(5)效果测试
首先,在AVD我们可以看到如下界面,在两个控件上我们分别输入abc和123456。
 

接着,我们打开DDMS的File Explore可以看到在data->data->ni->shared_prefs下面产生了一个名字叫做MY_PREFERENCES.xml的文件,该文件就是用来存储我们刚才设置的账号和密码。
将其导出,并打开,可以看到如下内容:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="MY_ACCOUNT">abc</string>
<string name="MY_PASSWORD">04B75FAD36E907BE50CE3222B0052B79</string>
</map>
这说明我们可以成功将账号和加密后的密码保存下来了。
最后,我们点击“退出”按钮将应用程序结束掉,再重新打开。我们又再次看到我们退出前的界面,账号密码已经被重新读取出来。

文章的最后,我们进入Android API手册,看看关于SharedPreferences的介绍:
Interface for accessing and modifying preference data returned by getSharedPreferences(String, int). For any particular set of preferences, there is a single instance of this class that all clients share. Modifications to the preferences must go through an SharedPreferences.Editor object to ensure the preference values remain in a consistent state and control when they are committed to storage.


SharedPreferences是一个用来访问和修改选项数据的接口,通过getSharedPreferences(Stirng,int)来获得该接口。对于任何特别的选项集,只能有一个实例供所有客户端共享。针对选项参数的修改必须通过一个SharedPreferences.Editor对象来进行,以保证所有的选项值保持在一个始终如一的状态,并且通过该对象提交存储。

可见,SharedPreferences操作选项文件时是线程安全的。



在DES解密时候出现pad block corrupted错误



我这里遇见了两种情况的bug,分别用如下两种方法解决

1:              //KeyGenerator kgen = KeyGenerator.getInstance("AES"); //android4.1以后会有bug,替换下边一句
               SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");  

2:                //Cipher cipher = Cipher.getInstance("AES"); //4.3以上有bug,用下边一句ok
                Cipher cipher = Cipher.getInstance("AES/ECB/ZeroBytePadding");

  相关解决方案