Android开发笔记(七十二)数据加密算法

编码算法

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开发笔记的完整目录
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值