Android登录记住密码,AES加密保存密码

Android登录记住密码,最常见的方式是用SharedPreferences。

SharedPreference是Android提供的一种轻量级的数据存储方式,主要用来存储一些简单的配置信息,例如,默认欢迎语,登录用户名和密码等。其以键值对的方式存储,使得我们能很方便进行读取和存入。

文章中的记住密码功能,也是用的SharedPreference实现的,其中保存的密码用AES算法加密。不多说,页面如下图:


先来看布局文件,布局中有很多string资源和drawable资源的引用,可在完整代码文件中找到。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/padding1"
    android:gravity="center"
    android:orientation="vertical"
    android:background="@color/login_bg"
    tools:context="com.example.remenberpassword.MainActivity" >
    
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:orientation="vertical">

        <LinearLayout
	        android:layout_width="fill_parent"
	        android:layout_height="wrap_content"
	        android:orientation="horizontal"
	        android:padding="@dimen/padding2"
	        android:gravity="center_vertical">

            <ImageView
                android:id="@+id/img_username"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:src="@drawable/username" />

            <EditText
                android:id="@+id/edit_username"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/margin1"
                android:layout_weight="7"
                android:hint="@string/username"
                android:background="@null"/>
	        
	    </LinearLayout>
    
        <View 
            android:layout_width="fill_parent"
            android:layout_height="1dp"
            android:background="@color/gainsboro"/>
        
        <LinearLayout
	        android:layout_width="fill_parent"
	        android:layout_height="wrap_content"
	        android:orientation="horizontal"
	        android:padding="@dimen/padding2"
	        android:gravity="center_vertical">

            <ImageView
                android:id="@+id/img_password"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:src="@drawable/password" />

            <EditText
                android:id="@+id/edit_password"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="7"
                android:layout_marginLeft="@dimen/margin1"
                android:inputType="textPassword"
                android:hint="@string/password"
                android:background="@null"/>
            
	    </LinearLayout> 
    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="@dimen/margin1"
        android:gravity="center_vertical">
        
	    <CheckBox
	        android:id="@+id/check_remenber_password"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:textColor="@color/white"
	        android:checked="true"
	        android:text="@string/remember_password" />
    </LinearLayout>

    <Button
        android:id="@+id/btn_login"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/margin1"
        android:textSize="@dimen/btn_textsize1"
        android:textColor="@color/white"
        android:background="@drawable/btn_selector"
        android:text="@string/login" />

</LinearLayout>


下面是Activity代码。

package com.example.remenberpassword;

import com.example.common.CommonUtils;
import com.example.common.EncrypAES;
import com.example.common.User;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;

public class MainActivity extends ActionBarActivity {
	/**
	 * 用了保存用户名和密码的文件名,文件是xml文件,但是不用加后缀,系统会自动加上。。
	 */
	private static final String USER_INFO="User_Info";
	
	/**
	 * 文件中记录用户名的关键字。
	 */
	private static final String USER_NAME="User_Name";
	
	/**
	 * 文件中记录密码的关键字。
	 */
	private static final String PASSWORD="Password";
	
	/**
	 * 文件中记录是否选择记住密码功能的关键字
	 */
	private static final String IS_REMENBER_PASSWORD="Is_Remenber_Password";
	
	private User mUser;
	
	private EditText editUsername;
	private EditText editPassword;
	private CheckBox checkRememberPassword;
	private Button btnLogin;
	private SharedPreferences mSharedPreferences;
	
	/**
	 *对用户名和密码进行加解密 
	 */
	private EncrypAES mAes;
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        
        InitData();
        InitWidget();
    }

	public void InitData(){
    	mUser=new User();
    	
    	//取得保存用户名和密码的xml文件,如果文件不存在,系统会自动创建
    	mSharedPreferences=getSharedPreferences(USER_INFO, Context.MODE_PRIVATE);
    	
		mAes=new EncrypAES();
	}
    
    public void InitWidget(){
    	editUsername=(EditText)findViewById(R.id.edit_username);
    	editPassword=(EditText)findViewById(R.id.edit_password);
    	checkRememberPassword=(CheckBox)findViewById(R.id.check_remenber_password);
    	btnLogin=(Button)findViewById(R.id.btn_login);
    	
    	//如果选中了记住密码,则从记住的密码中获取用户名和密码
    	if(mSharedPreferences.getBoolean(IS_REMENBER_PASSWORD, true)){
    		editUsername.setText(mSharedPreferences.getString(USER_NAME, ""));
    		
    		//对读取到的密码进行解密 
    		String password=mSharedPreferences.getString(PASSWORD, "");
    		if(0!=password.length()){
    			password=mAes.DecryptorString(password);
    		}
    		editPassword.setText(password);
    	}
    	
    	//记住密码CheckBox监听函数
    	checkRememberPassword.setOnCheckedChangeListener(new OnCheckedChangeListener() {
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
				mSharedPreferences.edit().putBoolean(IS_REMENBER_PASSWORD, isChecked).commit();
			}
		});
    	
    	//登录按钮监听函数
    	btnLogin.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				//判断是否打开网络连接
				if(!CommonUtils.CheckNetwork(MainActivity.this)){
					CommonUtils.ShowToast(MainActivity.this, R.string.network_hint);
					return ;
				}
				
				if(!CheckUserInfoInput()){
					return ;
				}
				
				savaUserNameAndPassword(mUser);
				
				Intent intent=new Intent(MainActivity.this,SecondActivity.class);
				startActivity(intent);
			}
		});
    }
    
    
    /**
     * 检查用户名和密码输入
     * @return
     */
    public boolean CheckUserInfoInput(){
    	String strUserName;
    	String strPassword;
    	
    	strUserName=editUsername.getText().toString();
    	strPassword=editPassword.getText().toString();
    	if(0==strUserName.length() | 0==strPassword.length()){
    		CommonUtils.ShowToast(MainActivity.this,R.string.username_password_inputhint);
    		return false;
    	}
    	
    	//设置用户名和密码
    	mUser.setUserName(strUserName);
    	mUser.setPassword(strPassword);

    	return true;
    }

	/**
	 * 如果选中了“记住密码”功能,则保存用户名和密码
	 * @param user 包含了用户名和密码
	 */
	public void savaUserNameAndPassword(User user){
		if(checkRememberPassword.isChecked()){
			Editor editor=mSharedPreferences.edit();
			editor.putBoolean(IS_REMENBER_PASSWORD, true);
			editor.putString(USER_NAME, user.getUserName());
			
			//对密码进行加密保存
			String password=mAes.EncryptorString(user.getPassword());
			editor.putString(PASSWORD, password);
			editor.commit();
		}
	}
 
}


接下来是AES算法加密代码。

代码中,SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");这一条语句是需要特别注意的,在代码中有详细的说明。

另外,用c.doFinal(buff)函数加解密得到的结果都是byte[]型的,不能对加密得到的byte[]型结果直接new String(byte[]);保存,然后在解密时用str.getBytes();转换。

所以,借用了网上其他网友的方法,有toHex、fromHex、toByte、toHex、appendHex。可以确保byte[]和String之间的转换不出问题。

package com.example.common;
import java.security.InvalidKeyException;
import java.security.SecureRandom;
import java.security.Security;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import android.util.Log;

/**
 * AES算法加密类
 * @author Administrator
 *
 */
public class EncrypAES {
	private static final String TAG="EncryAES";

	/**
	 * Cipher负责完成加密或解密工作
	 */
	private Cipher c;
	
	/**
	 * 该字节数组负责保存加密的结果
	 */
	private byte[] cipherByte;
	
	/**
	 * 用于生产密钥
	 */
	private static final String SECRETKET="AESDemo";
	private SecretKeySpec deskey;
	
	public EncrypAES(){
		Security.addProvider(new com.sun.crypto.provider.SunJCE());
		
		try {
			deskey = new SecretKeySpec(getRawKey(SECRETKET.getBytes()),"AES");
			
			//生成Cipher对象,指定其支持的DES算法
			c = Cipher.getInstance("AES");
		} catch (Exception e) {
			Log.e(TAG, "EnrypAES construct failed.",e);
		}
	}
	
	/**
	 * 对字符串加密
	 * 
	 * @param str
	 * @return
	 * @throws InvalidKeyException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	private byte[] Encrytor(String str) throws InvalidKeyException,
			IllegalBlockSizeException, BadPaddingException {
		
		// 根据密钥,对Cipher对象进行初始化,ENCRYPT_MODE表示加密模式
		c.init(Cipher.ENCRYPT_MODE, deskey);
		byte[] src = str.getBytes();
		
		cipherByte = c.doFinal(src);		// 加密,结果保存进cipherByte
		return cipherByte;
	}

	/**
	 * 对字符串解密
	 * 
	 * @param buff
	 * @return
	 * @throws InvalidKeyException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	private byte[] Decryptor(byte[] buff) throws InvalidKeyException,
			IllegalBlockSizeException, BadPaddingException {
		
		// 根据密钥,对Cipher对象进行初始化,DECRYPT_MODE表示加密模式
		c.init(Cipher.DECRYPT_MODE, deskey);
		cipherByte = c.doFinal(buff);
		
		return cipherByte;
	}
	
	/**
	 * 对字符串进行加密
	 * @param string 要加密的字符串
	 * @return 加密后的字符串
	 */
	public String EncryptorString(String string){
		String result =null;
		byte[] encontent;
		try {
			encontent = Encrytor(string);
			result=toHex(encontent);
		} catch (InvalidKeyException e) {
			Log.e(TAG, "EncryptorString",e);
		} catch (IllegalBlockSizeException e) {
			Log.e(TAG, "EncryptorString",e);
		} catch (BadPaddingException e) {
			Log.e(TAG, "EncryptorString",e);
		}
		
		return result;
	}
	
	/**
	 * 对字符串进行解密
	 * @param string 要解密的字符串
	 * @return 解密后的字符串
	 */
	public String DecryptorString(String string){
		byte[] cryptcontent=toByte(string);
		byte[] decontent;
		String result=null;
		
		try {
			decontent=Decryptor(cryptcontent);
			result=new String(decontent);
			
		} catch (InvalidKeyException e) {
			Log.e(TAG,"DecryptorString failed.",e);
		} catch (IllegalBlockSizeException e) {
			Log.e(TAG,"DecryptorString failed.",e);
		} catch (BadPaddingException e) {
			Log.e(TAG,"DecryptorString failed.",e);
		}
		
		return result;
	}

	private static byte[] getRawKey(byte[] seed) throws Exception {
		KeyGenerator kgen = KeyGenerator.getInstance("AES");
		
		/**
		 * 这一句很关键。
		 * <br>网上有的代码是这一句SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
		 * <br>在getInstance函数中少了一个String参数。
		 * 这个参数是不能少的,因为如果少了这个参数的话,生成的密钥是随机的,而加密和解密必须是用同样的密钥的
		 * 所以会出现不能解密的问题(总是抛出BadPaddingException异常)。而且这种方法中,对比少了一个参数的加密结果,
		 * 会发现每一次加密的结果都是不一样的。
		 * <br>而用下面的语句得到的密钥加密,同样的字符串任何时候得到的加密结果都是一样的。
		 */
		SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
		sr.setSeed(seed);
		kgen.init(128, sr); // 192 and 256 bits may not be available
		SecretKey skey = kgen.generateKey();
		byte[] raw = skey.getEncoded();
		return raw;
	}
	
	public static String toHex(String txt) {  
        return toHex(txt.getBytes());  
    }
	
    public static String fromHex(String hex) {  
        return new String(toByte(hex));  
    }  
      
    public static byte[] toByte(String hexString) {  
        int len = hexString.length()/2;  
        byte[] result = new byte[len];  
        for (int i = 0; i < len; i++)  
            result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();  
        return result;  
    }  
 
    public static String toHex(byte[] buf) {  
        if (buf == null)  
            return "";  
        StringBuffer result = new StringBuffer(2*buf.length);  
        for (int i = 0; i < buf.length; i++) {  
            appendHex(result, buf[i]);  
        }  
        return result.toString();  
    }
    
    private final static String HEX = "0123456789ABCDEF";
    private static void appendHex(StringBuffer sb, byte b) {  
        sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));  
    }
}


至于代码中其他的内容,比如把用户名和密码封装在了User对象中,大家自己去看完整的代码。

完整代码下载链接:http://download.csdn.net/detail/wlwh90/8741953

展开阅读全文

没有更多推荐了,返回首页