Android数据本地安全存储
2017-04-24 21:59
453 查看
前言
在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,这是一种比较好的解决办法,后期也会基于这个思路实现一套客户端和服务端的加解密组件。生活从不缺少美,只是缺少发现美的眼睛。只要静下心里去思考问题,任何困难都会有解决办法的。
相关文章推荐
- 理解 Android 本地数据存储 API
- [转载]使用Vitamio打造自己的Android万能播放器(4)——本地播放(快捷搜索、数据存储)
- android-exploitme(五):不安全的数据存储
- 使用Vitamio打造自己的Android万能播放器(4)——本地播放(快捷搜索、数据存储)
- Android 本地数据存储 API
- Android 本地数据存储 API
- 理解 Android 本地数据存储 API
- iOS应用程序安全(20)-本地数据存储及其安全性(NSUserDefaults, CoreData, Sqlite, Plist 文件)
- 大话无线客户端安全之数据存储安全——Android篇
- 理解 Android 本地数据存储 API
- 使用Vitamio打造自己的Android万能播放器(4)——本地播放(快捷搜索、数据存储)
- 社区开放任务指南-3104-Android本地数据安全的解决方案
- Android本地数据存储之SQLite关系型数据库 ——SQLiteDatabase
- 理解 Android 本地数据存储 API
- android存储本地数据大全
- 使用Vitamio打造自己的Android万能播放器(4)——本地播放(快捷搜索、数据存储)
- 使用Vitamio打造自己的Android万能播放器(4)——本地播放(快捷搜索、数据存储)
- 使用Vitamio打造自己的Android万能播放器(4)——本地播放(快捷搜索、数据存储)
- Android本地数据存储之SQLite关系型数据库 ——SQLiteDatabase
- iOS应用程序安全(20)-本地数据存储及其安全性(NSUserDefaults, CoreData, Sqlite, Plist 文件)