浅谈android数据存储加密
来源:互联网 发布:安广网络含山分公司 编辑:程序博客网 时间:2024/06/04 17:55
写在开头
在移动端的开发中,数据安全的问题一直是大家备受关注的,数据加密技术也受到了大家的青睐。项目中也用到了一些,在这里学习总结下,完善下自己的知识,也分享给大家,一起交流!(末尾也会说下自己在项目中的使用)
- 按可逆性:加密可分为可逆算法和不可逆算法
- 按对称性:加密可分为对称算法和非对称算法
一般的加密分为以下几种,下面会分别简单讲解原理和其使用方法:
- Base64编码算法 (可逆)
- MD5加密 (不可逆)(还有一个sha1值,可能做过支付宝的会比较熟悉)
- Des加密 (对称,可逆)
- Aes加密 (对称,可逆)
- Rsa加密(非对称,可逆)
浅谈对称非对称
对称
对称加密算法是较传统的加密体制,即通信双方在加/解密过程中使用他们共享的单一密钥,鉴于其算法简单和加密速度快的优点,目前仍然在使用,但是安全性方面就差一点可能。最常用的对称密码算法是DES算法,而DES密钥长度较短,已经不适合当今分布式开放网络对数据加密安全性的要求。一种新的基于Rijndael算法(自己脑补,本人也不太熟悉)对称高级数据加密标准AES取代了数据加密标准DES,弥补了DES的缺陷,目前使用比较多一点
非对称
非对称加密由于加/解密钥不同(公钥加密,私钥解密),密钥管理简单,得到了很广泛的应用。RSA是非对称加密系统最著名的公钥密码算法。但是由于RSA算法进行的都是大数计算,使得RSA最快的情况也比AES慢上倍,这是RSA最大的缺陷。但是其安全性较高,这也是大家比较喜欢的地方吧!
算法讲解及使用
Base64算法
Base64其实并不是安全领域的加密算法,因为它的加密解密算法都是公开的,典型的防菜鸟不防程序猿的例子哈哈,Base64编码本质上是一种将二进制数据转成文本数据的方案。用处就是将一些不适合传输的数据内容进行编码来适合传输。
字符串进行Base64编码
String encodedString = Base64.encodeToString("wenwen".getBytes(), Base64.DEFAULT); 第一个参数就是字节数组
字符串进行Base64解码
String decodedString =new String(Base64.decode(encodedString,Base64.DEFAULT)); decodedString 就是wenwen
MD5算法
它是一种单向加密算法,只能加密、无法解密。多用于密码的存储等等。对于MD5的安全性,网上有关MD5解密的网站数不胜数,破解机制采用穷举法,就是手机所有可能的MD5值跑字典。所以常常采用对数据进行多次MD5加密或者采取加盐(就是加一段独有的字符串在进行加密)的操作。
使用:
public static String md5(String string) { if (TextUtils.isEmpty(string)) { return ""; } MessageDigest md5 = null; try { md5 = MessageDigest.getInstance("MD5"); byte[] bytes = md5.digest(string.getBytes()); String result = ""; for (byte b : bytes) { String temp = Integer.toHexString(b & 0xff); if (temp.length() == 1) { temp = "0" + temp; } result += temp; } return result; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return ""; }
对称加密(这里只说下应用广泛的AES)
对称加密秘钥是唯一的,加密解密都是一个秘钥。AES速度上占优于RSA,但是只有一个秘钥,安全性较低一些。
使用
//常量介绍 private final static String HEX = "0123456789ABCDEF"; //AES是加密方式 CBC是工作模式 PKCS5Padding是填充模式 private static final String CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding"; //AES 加密 private static final String AES = "AES"; // SHA1PRNG 强随机种子算法, 要区别4.2以上版本的调用方法 private static final String SHA1PRNG="SHA1PRNG";
//生成随机数,可以当做动态的密钥 加密和解密的密钥必须一致,不然将不能解密 public static String generateKey() { try { SecureRandom localSecureRandom = SecureRandom.getInstance(SHA1PRNG); byte[] bytes_key = new byte[20]; localSecureRandom.nextBytes(bytes_key); String str_key = toHex(bytes_key); return str_key; } catch (Exception e) { e.printStackTrace(); } return null; }
// 对密钥进行处理 private static byte[] getRawKey(byte[] seed) throws Exception { KeyGenerator kgen = KeyGenerator.getInstance(AES); //for android SecureRandom sr = null; // 在4.2以上版本中,SecureRandom获取方式发生了改变 if (android.os.Build.VERSION.SDK_INT >= 17) { sr = SecureRandom.getInstance(SHA1PRNG, "Crypto"); } else { sr = SecureRandom.getInstance(SHA1PRNG); } // for Java // secureRandom = SecureRandom.getInstance(SHA1PRNG); sr.setSeed(seed); kgen.init(128, sr); //256 bits or 128 bits,192bits //AES中128位密钥版本有10个加密循环,192比特密钥版本有12个加密循环,256比特密钥版本则有14个加密循环。 SecretKey skey = kgen.generateKey(); byte[] raw = skey.getEncoded(); return raw; }
//加密 public static String encrypt(String key, String cleartext) { if (TextUtils.isEmpty(cleartext)) { return cleartext; } try { byte[] result = encrypt(key, cleartext.getBytes()); return Base64Encoder.encode(result); } catch (Exception e) { e.printStackTrace(); } return null; } private static byte[] encrypt(String key, byte[] clear) throws Exception { byte[] raw = getRawKey(key.getBytes()); SecretKeySpec skeySpec = new SecretKeySpec(raw, AES); Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()])); byte[] encrypted = cipher.doFinal(clear); return encrypted; }
/* * 解密 */ public static String decrypt(String key, String encrypted) { if (TextUtils.isEmpty(encrypted)) { return encrypted; } try { byte[] enc = Base64Decoder.decodeToBytes(encrypted); byte[] result = decrypt(key, enc); return new String(result); } catch (Exception e) { e.printStackTrace(); } return null; } /* * 解密 */ private static byte[] decrypt(String key, byte[] encrypted) throws Exception { byte[] raw = getRawKey(key.getBytes()); SecretKeySpec skeySpec = new SecretKeySpec(raw, AES); Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING); cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()])); byte[] decrypted = cipher.doFinal(encrypted); return decrypted; }}
RSA加密
RSA算法是最流行的公钥密码算法,使用长度可以变化的密钥。RSA是第一个既能用于数据加密也能用于数字签名的算法。它在很多密码协议中都有应用,如SSL和S/MIME。RSA算法是基于大质数的因数分解的公匙体系。简单的讲,就是两个很大的质数,一个作为公钥,另一个作为私钥,如用其中一个加密,则用另一个解密。密钥长度从40到2048位可变,密钥越长,加密效果越好,但加密解密的开销也大。所以他在加密的速度上回小于AES等对称加密。
使用:
//全局变量 public static final String RSA = "RSA";// 非对称加密密钥算法 public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";//加密填充方式 public static final int DEFAULT_KEY_SIZE = 2048;//秘钥默认长度 public static final byte[] DEFAULT_SPLIT = "#PART#".getBytes(); // 当要加密的内容超过bufferSize,则采用partSplit进行分块加密 public static final int DEFAULT_BUFFERSIZE = (DEFAULT_KEY_SIZE / 8) - 11;// 当前秘钥支持加密的最大字节数
/** * 随机生成RSA密钥对 * @param keyLength 密钥长度,范围:512~2048 一般1024 */ public static KeyPair generateRSAKeyPair(int keyLength) { try { KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA); kpg.initialize(keyLength); return kpg.genKeyPair(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } }
公钥加密私钥解密
/** * 用公钥对字符串进行加密 * @param data 原文 */ public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception { // 得到公钥 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey); KeyFactory kf = KeyFactory.getInstance(RSA); PublicKey keyPublic = kf.generatePublic(keySpec); // 加密数据 Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING); cp.init(Cipher.ENCRYPT_MODE, keyPublic); return cp.doFinal(data); } /** * 使用私钥进行解密 */ public static byte[] decryptByPrivateKey(byte[] encrypted, byte[] privateKey) throws Exception { // 得到私钥 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey); KeyFactory kf = KeyFactory.getInstance(RSA); PrivateKey keyPrivate = kf.generatePrivate(keySpec); // 解密数据 Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING); cp.init(Cipher.DECRYPT_MODE, keyPrivate); byte[] arr = cp.doFinal(encrypted); return arr; }
私钥加密公钥解密
/** * 私钥加密 * @param data 待加密数据 * @param privateKey 密钥 * @return byte[] 加密数据 */ public static byte[] encryptByPrivateKey(byte[] data, byte[] privateKey) throws Exception { // 得到私钥 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey); KeyFactory kf = KeyFactory.getInstance(RSA); PrivateKey keyPrivate = kf.generatePrivate(keySpec); // 数据加密 Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING); cipher.init(Cipher.ENCRYPT_MODE, keyPrivate); return cipher.doFinal(data); } /** * 公钥解密 * * @param data 待解密数据 * @param publicKey 密钥 * @return byte[] 解密数据 */ public static byte[] decryptByPublicKey(byte[] data, byte[] publicKey) throws Exception { // 得到公钥 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey); KeyFactory kf = KeyFactory.getInstance(RSA); PublicKey keyPublic = kf.generatePublic(keySpec); // 数据解密 Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING); cipher.init(Cipher.DECRYPT_MODE, keyPublic); return cipher.doFinal(data); }
RSA所遇问题
关于加密填充方式:之前以为上面这些操作就能实现rsa加解密,以为万事大吉了,呵呵,这事还没完,悲剧还是发生了,Android这边加密过的数据,服务器端死活解密不了,原来android系统的RSA实现是”RSA/None/NoPadding”,而标准JDK实现是”RSA/None/PKCS1Padding” ,这造成了在android机上加密后无法在服务器上解密的原因,所以在实现的时候这个一定要注意。
实现分段加密:搞定了填充方式之后又自信的认为万事大吉了,可是意外还是发生了,RSA非对称加密内容长度有限制,1024位key的最多只能加密127位数据,否则就会报错(javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes) , RSA 是常用的非对称加密算法。最近使用时却出现了“不正确的长度”的异常,研究发现是由于待加密的数据超长所致。RSA 算法规定:待加密的字节数不能超过密钥的长度值除以 8 再减去 11(即:KeySize / 8 - 11),而加密后得到密文的字节数,正好是密钥的长度值除以 8(即:KeySize / 8)。
分段公钥加密私钥加密
/** * 用公钥对字符串进行分段加密 * */ public static byte[] encryptByPublicKeyForSpilt(byte[] data, byte[] publicKey) throws Exception { int dataLen = data.length; if (dataLen <= DEFAULT_BUFFERSIZE) { return encryptByPublicKey(data, publicKey); } List<Byte> allBytes = new ArrayList<Byte>(2048); int bufIndex = 0; int subDataLoop = 0; byte[] buf = new byte[DEFAULT_BUFFERSIZE]; for (int i = 0; i < dataLen; i++) { buf[bufIndex] = data[i]; if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) { subDataLoop++; if (subDataLoop != 1) { for (byte b : DEFAULT_SPLIT) { allBytes.add(b); } } byte[] encryptBytes = encryptByPublicKey(buf, publicKey); for (byte b : encryptBytes) { allBytes.add(b); } bufIndex = 0; if (i == dataLen - 1) { buf = null; } else { buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)]; } } } byte[] bytes = new byte[allBytes.size()]; { int i = 0; for (Byte b : allBytes) { bytes[i++] = b.byteValue(); } } return bytes; } /** * 使用私钥分段解密 * */ public static byte[] decryptByPrivateKeyForSpilt(byte[] encrypted, byte[] privateKey) throws Exception { int splitLen = DEFAULT_SPLIT.length; if (splitLen <= 0) { return decryptByPrivateKey(encrypted, privateKey); } int dataLen = encrypted.length; List<Byte> allBytes = new ArrayList<Byte>(1024); int latestStartIndex = 0; for (int i = 0; i < dataLen; i++) { byte bt = encrypted[i]; boolean isMatchSplit = false; if (i == dataLen - 1) { // 到data的最后了 byte[] part = new byte[dataLen - latestStartIndex]; System.arraycopy(encrypted, latestStartIndex, part, 0, part.length); byte[] decryptPart = decryptByPrivateKey(part, privateKey); for (byte b : decryptPart) { allBytes.add(b); } latestStartIndex = i + splitLen; i = latestStartIndex - 1; } else if (bt == DEFAULT_SPLIT[0]) { // 这个是以split[0]开头 if (splitLen > 1) { if (i + splitLen < dataLen) { // 没有超出data的范围 for (int j = 1; j < splitLen; j++) { if (DEFAULT_SPLIT[j] != encrypted[i + j]) { break; } if (j == splitLen - 1) { // 验证到split的最后一位,都没有break,则表明已经确认是split段 isMatchSplit = true; } } } } else { // split只有一位,则已经匹配了 isMatchSplit = true; } } if (isMatchSplit) { byte[] part = new byte[i - latestStartIndex]; System.arraycopy(encrypted, latestStartIndex, part, 0, part.length); byte[] decryptPart = decryptByPrivateKey(part, privateKey); for (byte b : decryptPart) { allBytes.add(b); } latestStartIndex = i + splitLen; i = latestStartIndex - 1; } } byte[] bytes = new byte[allBytes.size()]; { int i = 0; for (Byte b : allBytes) { bytes[i++] = b.byteValue(); } } return bytes; }
分段私钥加密公钥加密
/** * 私钥分段加密 * @param data 要加密的原始数据 * @param privateKey 秘钥 */ public static byte[] encryptByPrivateKeyForSpilt(byte[] data, byte[] privateKey) throws Exception { int dataLen = data.length; if (dataLen <= DEFAULT_BUFFERSIZE) { return encryptByPrivateKey(data, privateKey); } List<Byte> allBytes = new ArrayList<Byte>(2048); int bufIndex = 0; int subDataLoop = 0; byte[] buf = new byte[DEFAULT_BUFFERSIZE]; for (int i = 0; i < dataLen; i++) { buf[bufIndex] = data[i]; if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) { subDataLoop++; if (subDataLoop != 1) { for (byte b : DEFAULT_SPLIT) { allBytes.add(b); } } byte[] encryptBytes = encryptByPrivateKey(buf, privateKey); for (byte b : encryptBytes) { allBytes.add(b); } bufIndex = 0; if (i == dataLen - 1) { buf = null; } else { buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)]; } } } byte[] bytes = new byte[allBytes.size()]; { int i = 0; for (Byte b : allBytes) { bytes[i++] = b.byteValue(); } } return bytes; } /** * 公钥分段解密 * * @param encrypted 待解密数据 * @param publicKey 密钥 */ public static byte[] decryptByPublicKeyForSpilt(byte[] encrypted, byte[] publicKey) throws Exception { int splitLen = DEFAULT_SPLIT.length; if (splitLen <= 0) { return decryptByPublicKey(encrypted, publicKey); } int dataLen = encrypted.length; List<Byte> allBytes = new ArrayList<Byte>(1024); int latestStartIndex = 0; for (int i = 0; i < dataLen; i++) { byte bt = encrypted[i]; boolean isMatchSplit = false; if (i == dataLen - 1) { // 到data的最后了 byte[] part = new byte[dataLen - latestStartIndex]; System.arraycopy(encrypted, latestStartIndex, part, 0, part.length); byte[] decryptPart = decryptByPublicKey(part, publicKey); for (byte b : decryptPart) { allBytes.add(b); } latestStartIndex = i + splitLen; i = latestStartIndex - 1; } else if (bt == DEFAULT_SPLIT[0]) { // 这个是以split[0]开头 if (splitLen > 1) { if (i + splitLen < dataLen) { // 没有超出data的范围 for (int j = 1; j < splitLen; j++) { if (DEFAULT_SPLIT[j] != encrypted[i + j]) { break; } if (j == splitLen - 1) { // 验证到split的最后一位,都没有break,则表明已经确认是split段 isMatchSplit = true; } } } } else { // split只有一位,则已经匹配了 isMatchSplit = true; } } if (isMatchSplit) { byte[] part = new byte[i - latestStartIndex]; System.arraycopy(encrypted, latestStartIndex, part, 0, part.length); byte[] decryptPart = decryptByPublicKey(part, publicKey); for (byte b : decryptPart) { allBytes.add(b); } latestStartIndex = i + splitLen; i = latestStartIndex - 1; } } byte[] bytes = new byte[allBytes.size()]; { int i = 0; for (Byte b : allBytes) { bytes[i++] = b.byteValue(); } } return bytes; }
项目中使用
在实际开发中,不是太重要的数据用一种加密方式感觉就可以了。但是比较重要的数据建议用多种加密方式结合的方式,比如我用的RSA+AES加密。他主要解决了两个问题:1.RSA加解密速度慢,不适合大量数据文件加密 2.AES加密速度很快,但是安全性没有RSA加密方式的安全。其主要思想就是服务端生成公钥私钥,并提供接口将公钥给android端,android端生成AES秘钥,并用AES秘钥对大量数据进行加密(解决RSA加解密速度慢的问题),然后用调用接口拿到的RSA公钥对自己生成AES秘钥进行加密,客户端将得到的秘钥和通过AES加密的数据发送给服务器。(秘钥可以放在请求头中,数据放在请求体中,这个随意了)。服务拿到你的秘钥和数据后,用私钥加密得到AES秘钥,再通过秘钥得到发送的数据就好了。
感谢教我学习的大神
这次的学习也是学习的其他大神的总结,非常感谢他们。在这里记录下来,供自己学习也想让更多的人看到,帮助更多的人。
http://blog.csdn.net/randyjiawenjie/article/details/6587986
http://blog.csdn.net/axi295309066/article/details/52491077
http://blog.csdn.net/qq_26685493/ gongyongfeng的博客
http://www.cnblogs.com/whoislcj/ 李总写代码
- 浅谈android数据存储加密
- 浅谈android数据存储加密
- Android数据存储加密
- 【工具类】Android数据存储sharedpreferences加密
- 【工具类】Android数据存储sharedpreferences加密
- Android数据存储之SQLCipher数据库加密
- Android数据存储之SQLCipher数据库加密
- Android数据存储之SQLCipher数据库加密
- 浅谈IOS数据存储
- 浅谈Android数据存储与IO之一(Sharepreferences)
- Android数据存储之Sqlite采用SQLCipher数据库加密实战
- android实现数据的MD5加密并用SharedPreferences存储
- android存储---sqlite浅谈
- android加密存储文件
- android 加密存储
- hive的数据存储浅谈
- Android 数据加密 MD5加密
- 浅谈贝贝游戏数据加密解密方式
- ArcGIS字段计算器Python变量类型
- Java中HashMap遍历的两种方式
- 数码管实现动静态显示
- cookies序列化与反序列化
- 05_Case语句
- 浅谈android数据存储加密
- php的数组与字符串的转换函数整理汇总
- 第五周--BP算法
- 第三天
- Spring cloud系列二 Spring Cloud 配置中心的基本用法
- TopCoder SRM 147 1000分 DP+矩阵乘法
- window.location.hash属性介绍
- 01_PLSQL块组成部分
- 深入浅出iOS浮点数精度问题 (下)