Android开发笔记(七十二)数据加密算法
2016-02-29 10:18
302 查看
编码算法
URL编码
URL编码其实并非加解密算法,只是对特殊字符进行字符转义,从而方便在URL中传输参数。URL编码有两种方式,一种是狭义的URL编码,另一种是广义的URL编码。狭义的URL编码指的是只对汉字进行编码,相关代码参见《Android开发笔记(六十三)HTTP访问的通信方式》。
广义的URL编码指的是除了汉字之外,还对其他特殊字符进行编码,如空格转换为“%20”,其他的“?”、“&”“/”也分别转换为“%3F”、“%26”、“%2F”。广义的URL编码可直接使用URLEncoder的encode方法,URL解码使用URLDecoder的decode方法,代码示例如下:
//URL编码 public static String encodeURL(String str) { String encode_str = str; try { encode_str = URLEncoder.encode(str, "utf-8"); } catch (Exception e) { e.printStackTrace(); } return encode_str; } //URL解码 public static String decodeURL(String str) { String decode_str = str; try { decode_str = URLDecoder.decode(str, "utf-8"); } catch (Exception e) { e.printStackTrace(); } return decode_str; }
BASE64编码
BASE64是一种针对字节流的编码工具,用于把不可见的字节流转换为可见的字符串。如果把待加密的数据先转为字节流,然后再把字节流通过BASE64编码成字符串,就好像是完成了加密操作。同时,这个字符串也可以通过BASE64解码为原始数据,因此,我们也可以把BASE64编码看作是一种简单的可逆加密算法。BASE64有两种编码方式,一种是SUN的,另一种是Apache的。
SUN的BASE64编码,编码算法在sun.misc.BASE64Encoder的encode函数,解码算法在sun.misc.BASE64Decoder的decodeBuffer函数。但是SUN的这个包不在Java的核心库内,所以Android上会报方法找不到的错误。要想在Android上也能使用SUN的BASE64,有两种方式,一种是导入rt.jar包,另一种是在工程中直接加入SUN的源码。
Apache的BASE64编码,编码算法在Base64的encodeBase64String函数,解码算法在Base64的decodeBase64函数。Apache方式对应的jar包名称是commons-codec-***.jar,可是Android内部有相同包名的方法,编译的时候不会报错,运行时便会报方法找不到。要想正常运行,得下载源码后修改包名,避免与系统自带的包冲突。
加密算法
MD5加密
MD5是不可逆的加密算法,也就是无法解密。MD5的加密实现在commons-codec-***.jar中,但是该包的MD5加密函数md5Hex在java环境可以正常运行,但在Android上运行会报错:java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString。这个报错与上面Apache的BASE64编码的问题是一样的,解决该问题有三个办法:1、使用MessageDigest方式进行MD5加密;
2、下载org.apache.commons.codec的源码,改个包名,在Android环境重新编译打成jar;
3、使用下面代码实现曲线加密:
String md5Str = new String(Hex.encodeHex(DigestUtils.md5(raw)));
具体的MD5加密示例代码如下:
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; public class MD5Util { /* * 1.一个运用基本类的实例 MessageDigest 对象开始被初始化。该对象通过使用 update 方法处理数据。 任何时候都可以调用 * reset 方法重置摘要。 一旦所有需要更新的数据都已经被更新了,应该调用 digest 方法之一完成哈希计算。 * 对于给定数量的更新数据,digest 方法只能被调用一次。 在调用 digest 之后,MessageDigest 对象被重新设置成其初始状态。 */ public static String encrypByMd5(String raw) { String md5Str = raw; try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(raw.getBytes()); byte[] encryContext = md.digest(); int i; StringBuffer buf = new StringBuffer(""); for (int offset = 0; offset < encryContext.length; offset++) { i = encryContext[offset]; if (i < 0) { i += 256; } if (i < 16) { buf.append("0"); } buf.append(Integer.toHexString(i)); } md5Str = buf.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return md5Str; } /* * 2.使用开发的jar直接应用 使用外部的jar包中的类:import * org.apache.commons.codec.digest.DigestUtils; 对上面内容的一个封装使用方便 */ public static String encrypByMd5Jar(String raw) { //String md5Str = DigestUtils.md5Hex(raw); String md5Str = new String(Hex.encodeHex(DigestUtils.md5(raw))); return md5Str; } }
RSA加密
RSA使用公钥加密,在另一端使用私钥加密,这样即使加密的公钥被泄露,对方没有私钥仍然无法正确解密。下面是RSA加密的几个注意事项:
1、需要导入bcprov-jdk16-1.46.jar;
2、RSA加密的结果是byte字节流,得经过BASE64编码,形成文本字符串后方可正常传输;
3、有时候要对加密前的字符串做reverse倒序处理;
AES加密
AES是设计用来替换DES的高级加密算法。下面是AES算法的代码示例:import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; public class AesCipher { private static final String Algorithm = "AES"; 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)); } public static String encrypt(String key, String src) throws Exception { byte[] rawKey = getRawKey(key.getBytes()); byte[] result = encrypt(rawKey, src.getBytes()); return toHex(result); } public static String decrypt(String key, String encrypted) throws Exception { byte[] rawKey = getRawKey(key.getBytes()); byte[] enc = toByte(encrypted); byte[] result = decrypt(rawKey, enc); return new String(result); } private static byte[] getRawKey(byte[] seed) throws Exception { KeyGenerator kgen = KeyGenerator.getInstance(Algorithm); // SHA1PRNG 强随机种子算法, 要区别4.2以上版本的调用方法 SecureRandom sr = null; if (android.os.Build.VERSION.SDK_INT >= 17) { sr = SecureRandom.getInstance("SHA1PRNG", "Crypto"); }else { sr = SecureRandom.getInstance("SHA1PRNG"); } sr.setSeed(seed); kgen.init(256, sr);//256 bits or 128 bits,192bits SecretKey skey = kgen.generateKey(); byte[] raw = skey.getEncoded(); return raw; } private static byte[] encrypt(byte[] key, byte[] src) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(key, Algorithm); Cipher cipher = Cipher.getInstance(Algorithm); cipher.init(Cipher.ENCRYPT_MODE, skeySpec); byte[] encrypted = cipher.doFinal(src); return encrypted; } private static byte[] decrypt(byte[] key, byte[] encrypted) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(key, Algorithm); Cipher cipher = Cipher.getInstance(Algorithm); cipher.init(Cipher.DECRYPT_MODE, skeySpec); byte[] decrypted = cipher.doFinal(encrypted); return decrypted; } 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(); } }
3DES加密
3DES(或称为Triple DES)是三重数据加密算法,它相当于对每个数据块应用三次DES加密算法。因为原先DES算法的密钥长度过短,使得容易遭到破解,所以3DES通过增加密钥的长度,从而防范被暴力破解,因此3DES不是设计全新的密码算法。实际开发中,3DES的密钥必须是24位的字节数组,过短或过长在运行时都会报错“java.security.InvalidKeyException”。另外,3DES加密生成的是字节数组,也得通过BASE64编码为文本字符串。具体的3DES加密过程,除了密钥不同之外,还存在两种加密方式:
1、使用加密算法“DESede”,此时初始化Cipher对象只需传入密钥;
2、使用加密算法“desede/CBC/PKCS5Padding”,此时初始化Cipher对象除了传入密钥,还需传入一个字节数组的参数对象即IvParameterSpec;
下面是DESede方式的代码示例:
import android.annotation.SuppressLint; import java.io.IOException; import java.io.UnsupportedEncodingException; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import com.example.exmencrypt.base64.BASE64Decoder; import com.example.exmencrypt.base64.BASE64Encoder; public class Des3Util { // 定义加密算法,DESede即3DES private static final String Algorithm = "DESede"; @SuppressLint("TrulyRandom") private static byte[] encryptMode(String key, byte[] src) { try { SecretKey deskey = new SecretKeySpec(build3DesKey(key), Algorithm); Cipher cipher = Cipher.getInstance(Algorithm); cipher.init(Cipher.ENCRYPT_MODE, deskey); return cipher.doFinal(src); }catch (Exception e) { e.printStackTrace(); return null; } } private static byte[] decryptMode(String key, byte[] src) { try { SecretKey deskey = new SecretKeySpec(build3DesKey(key), Algorithm); Cipher cipher = Cipher.getInstance(Algorithm); cipher.init(Cipher.DECRYPT_MODE, deskey); return cipher.doFinal(src); }catch (Exception e) { e.printStackTrace(); return null; } } //根据字符串生成密钥24位的字节数组 private static byte[] build3DesKey(String keyStr) throws UnsupportedEncodingException { byte[] key = new byte[24]; byte[] temp = keyStr.getBytes("UTF-8"); if (key.length > temp.length) { System.arraycopy(temp, 0, key, 0, temp.length); }else { System.arraycopy(temp, 0, key, 0, key.length); } return key; } public static String encrypt(String key, String raw) { byte[] enBytes = encryptMode(key, raw.getBytes()); BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(enBytes); } public static String decrypt(String key, String enc) { try { BASE64Decoder decoder = new BASE64Decoder(); byte[] enBytes = decoder.decodeBuffer(enc); byte[] deBytes = decryptMode(key, enBytes); return new String(deBytes); } catch (IOException e) { e.printStackTrace(); return enc; } } }
下面是desede/CBC/PKCS5Padding方式的代码示例:
import android.annotation.SuppressLint; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESedeKeySpec; import javax.crypto.spec.IvParameterSpec; public class Des3Cipher { private static final String Algorithm = "desede"; // 向量 private final static String iv = "01234567"; // 加解密统一使用的编码方式 private final static String encoding = "utf-8"; @SuppressLint("TrulyRandom") public static String encrypt(String secretKey, String plainText) throws Exception { SecretKey deskey = null; DESedeKeySpec spec = new DESedeKeySpec(secretKey.getBytes()); SecretKeyFactory keyfactory = SecretKeyFactory.getInstance(Algorithm); deskey = keyfactory.generateSecret(spec); Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding"); IvParameterSpec ips = new IvParameterSpec(iv.getBytes()); cipher.init(Cipher.ENCRYPT_MODE, deskey, ips); byte[] encryptData = cipher.doFinal(plainText.getBytes(encoding)); return Base64Util.encode(encryptData); } public static String decrypt(String secretKey, String encryptText) throws Exception { SecretKey deskey = null; DESedeKeySpec spec = new DESedeKeySpec(secretKey.getBytes()); SecretKeyFactory keyfactory = SecretKeyFactory.getInstance(Algorithm); deskey = keyfactory.generateSecret(spec); Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding"); IvParameterSpec ips = new IvParameterSpec(iv.getBytes()); cipher.init(Cipher.DECRYPT_MODE, deskey, ips); byte[] decryptData = cipher.doFinal(Base64Util.decode(encryptText)); return new String(decryptData, encoding); } }
点此查看Android开发笔记的完整目录
相关文章推荐
- Android的Style的使用
- android sqlite查询方式
- android在进行创建项目gen下没有自动生成R.java
- Android Service和Thread的区别
- Android Media相关标准接口
- android studio gradle介绍
- android学习之RadioButton
- 深入理解LayoutInflater.inflate()
- Android布局— — —表格布局
- 使用Android Studio与ArcGIS Android SDK的开发环境部署和HelloWorld
- Android ViewPager 小圆点指示器
- Android实现Material Design风格的设置页面(滑动开关控件)
- Android 实现蘑菇街购物车动画效果
- 五步搞定Android开发环境部署——非常详细的Android开发环境搭建教程
- Android开发把项目打包成apk
- Android - LearnActivity
- Android 获取验证码后的倒计时功能
- Android Volley完全解析
- android okvolley框架搭建
- 为Eclipse添加Java和Android SDK源代码