前言

  在Android都开发中,通常我们都会在本地存储一些数据,如果我们不对这些数据进行加密存储,很容易将一些敏感数据暴露给黑客,从而给我们的产品带来一些影响。
  这里使用AES算法来加解密数据,在使用AES算法中,最主要的就是key的生成,如果我们直接硬编码在程序,程序被反编译后也很容易看到。那么如果保证key的安全以及在不同的手机上使用不同的key呢?这篇文章结合项目中的使用经验,分享key的安全生成并提供部分参考代码。

一、MD5介绍

  MD5摘要算法广泛的应用中我们的程序当中,如用户密码的存储,文件来源的安全校验等。具体的介绍可以参考MD5百度百科。
  任何数据经过MD5后产生的16位的byte,并且同一数据经过MD5后的内容不会变,这是后期key生成的保障。

private byte[] md5(String data) throws NoSuchAlgorithmException {    MessageDigest messageDigest = MessageDigest.getInstance("MD5");    messageDigest.update(data.getBytes());    return messageDigest.digest();}

二、位运算和生成key

为了增加破解key的难度,我们对md5后的数据做位运算,代码如下:

public class HashUtils {    private static final char[] TARCODE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',            'f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' };    public static String getHash(String data, String type) {        String str = "";        if (data != null) {            try {                MessageDigest messageDigest = MessageDigest.getInstance(type);                messageDigest.update(data.getBytes());                str = handleBytes(messageDigest.digest());            } catch (Exception e) {                // TODO: handle exception                e.printStackTrace();            }        }        return str;    }    //生成key,这一步是重点    private static String handleBytes(byte[] data) {        int i = data.length;        StringBuilder sb = new StringBuilder(i * 2);        for (int j = 0; j < i; j++) {            //对每个字节位运算并且和16进制62做&运算,保证产生的索引在TARCODE的内            sb.append(TARCODE[(data[j] >> 4 & 0x3D)]);            sb.append(TARCODE[(data[j] & 0x3D)]);        }        return sb.toString();    }}

有了上面key的以后,接下来使用AES算法进行加解密。

三、AES加解密代码

public class SafeAESTool {    private static final String ALGORITHM = "AES";    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";    public static String encrypt(String key,String data){        // TODO Auto-generated method stub        String str = null;        try {            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), ALGORITHM);            Cipher cipher = Cipher.getInstance(TRANSFORMATION);            IvParameterSpec ivParameterSpec = new IvParameterSpec(key.getBytes());            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);            byte[] ret = cipher.doFinal(data.getBytes());            str = Base64.encodeToString(ret, Base64.NO_WRAP);        } catch (Exception e) {            e.printStackTrace();        }        return str;    }    public static String decrypt(String key,String data){        // TODO Auto-generated method stub        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), ALGORITHM);        try {            Cipher cipher = Cipher.getInstance(TRANSFORMATION);            IvParameterSpec ivParameterSpec = new IvParameterSpec(key.getBytes());            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);            byte[] retByte = cipher.doFinal(Base64.decode(data,Base64.NO_WRAP));            return new String(retByte);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }}

四、封装工具类

采用获取设备的IMEI号和应用的包名作为生产key的标识,主要是保证能够获取IMEI的情况下每个手机的AES Key都不一样,从而保证存储的安全。

public class SafeStorageUtil {    private static String AESKEY;    /**     * 获得应用程序包名     * @param context     * @return     */    private static String getPackageName(Context context) {        return context.getPackageName();    }    /**     * 设备IMEI号     * @param mContext     * @return     */    private static String getDeviceID(Context mContext) {        TelephonyManager tm = (TelephonyManager) mContext                .getSystemService(Context.TELEPHONY_SERVICE);        return tm.getDeviceId();    }    /**     * 获取唯一标识     * @param mContext     * @return     */    private static String getAppUnique(Context mContext){        return getDeviceID(mContext)+getPackageName(mContext);    }    private static String getAESKey(Context context) {        if (!TextUtils.isEmpty(AESKEY))return AESKEY;        //取16位作为密钥key        AESKEY = HashUtils.getHash(getAppUnique(context), "MD5").substring(0, 16);        return AESKEY;    }    public static String encrypt(Context context,String data){        return SafeAESTool.encrypt(getAESKey(context), data);    }    public static String decrypt(Context context,String data){        return SafeAESTool.decrypt(getAESKey(context), data);    }}

五、测试-使用SharedPreference存取数据

private static SharedPreferences getSharedPreferences(Context context) {    return context.getSharedPreferences("test.properties", 0);}/** * 安全存储数据 * @param context * @param key * @param value */public static void saveData(Context context,String key,String value){    if(TextUtils.isEmpty(value))return;    SharedPreferences sp = getSharedPreferences(context);    Editor editor = sp.edit();    editor.putString(key, SafeStorageUtil.encrypt(context, value));    editor.commit();}/** * 获取数据 * @param context * @param key * @return */public static String getData(Context context,String key){    SharedPreferences sp = getSharedPreferences(context);    String tem = sp.getString(key, "");    if(!TextUtils.isEmpty(tem)){        tem = SafeStorageUtil.decrypt(context, tem);    }    return tem;}

6、小结

平时做加解密的时候都是直接把key硬编码在代码里面,一直担心这种写法不安全,但是没有找到合适的解决方案。最近了解了阿里聚安全的一些实现思路后才脑洞大开,结合MD5的特性去生成DES key,这是一种比较好的解决办法,后期也会基于这个思路实现一套客户端和服务端的加解密组件。
生活从不缺少美,只是缺少发现美的眼睛。只要静下心里去思考问题,任何困难都会有解决办法的。

更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. 一句话锁定MySQL数据占用元凶
  3. Android位置策略(五)
  4. Java中输入流的read()为阻塞式方法的相关实例
  5. android中常用的数据单位和尺寸
  6. Android程序签名详解、打包,分别使用keytool工具和Android(安卓)S
  7. Android二维码的扫描和生成(ZXing)
  8. Android(安卓)Developers:传感器概述
  9. Android(安卓)-- Parcelable 序列化操作数据(上篇)

随机推荐

  1. android通过蓝牙实现两台手机传输数据
  2. 仿淘宝商品详情页面Android
  3. Android简单记录和恢复ListView滚动位置
  4. Android(安卓)init.rc文件解析过程详解(
  5. Android(安卓)studio 自动生成带系统权限
  6. Android(安卓)使用PDF.js浏览pdf的方法示
  7. AndroidManifest.xml中android:label标签
  8. Android(安卓)Jetpack 之 LifeCycle
  9. Android(安卓)获取判断是否有悬浮窗权限
  10. 【Android】Material Design 之二 Bottom