编码算法
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开发笔记的完整目录