加密与解密篇一之Base64

      Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个位元为一个单元,对应某个可打印字符。三个字节有24个位元,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示。它可用来作为电子邮件的传输编码。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9 ,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。一些如uuencode的其他编码方法,和之后binhex的版本使用不同的64字符集来代表6个二进制数字,但是它们不叫Base64。【摘自维基百科】

 

     为了保证所输出的编码位可读字符,Base64制定了一个编码表,以便进行统一转换。编码表的大小为2^6=64,这也是Base64名称的由来。

Base64编码表

         
 

 

  Java代码中Base64的使用示例:

package cn.com.base64;
import java.io.IOException;
public class Test {
	/** 
	     * 编码 
	     * @param bstr 
	     * @return String 
	     */  
	    public static String encode(byte[] bstr){  
	        return new sun.misc.BASE64Encoder().encode(bstr);  
	    }  
	  
	    /** 
	     * 解码 
	     * @param str 
	     * @return string 
	     */  
	    public static byte[] decode(String str){  
		    byte[] bt = null;  
		    try {  
		        sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();  
		        bt = decoder.decodeBuffer( str );  
		    } catch (IOException e) {  
		        e.printStackTrace();  
		    }  
	        return bt;  
	    }  
	    /** 
	     * @param args 
	     */  
	    public static void main(String[] args) {  
	  /*      Test te = new Test();  
	        String aa = "hello world";  
	        System.out.println("加密前:"+aa);
	        
	        aa = te.encode(aa.getBytes());  
	        System.out.println("经过BASE64加密后:"+aa);  
	                 
	        String str = aa;  
	        String str2 = new String(te.decode(str));  
	        System.out.println("经过BASE64解密后:"+str2);  */
	    	
	    }  

    说明:第一次在Eclipse中引入BASE64Encoder /BASE64Decoder类,无法引入,解决办法请参 考:http://blog.csdn.net/a0501bqzhxy/article/details/6441526

   运行后的结果:

      加密前:hello world

      经过BASE64加密后:aGVsbG8gd29ybGQ=

      经过BASE64解密后:hello world

 

一步步解读Base64的执行过程:

   Base64的编码都是按字符串长度,以每38bit的字符为一组,然后针对每组,首先获取每个字符的ASCII编码,然后将ASCII编码转换成8bit的二进制,得到一组3*8=24bit的字节,然后再将这24bit划分为46bit的字节,并在每个6bit的字节前面都填两个高位0,得到48bit的字节。然后将这48bit的字节转换成10进制,对照Base64编码表(下表),得到对应编码后的字符。(注:1. 要求被编码字符是8bit的,所以须在ASCII编码范围内,\u0000-\u00ff,中文就不行。  2. 如果被编码字符长度不是3的倍数的时候,则都用0代替,对应的输出字符为=

 1)字符长度为能被3整除时

比如“Tom” :
     T       o        m
ASCII:   84      111      109
8bit字节: 01010100   01101111    01101101
6bit字节: 010101    000110     111101    101101
十进制:  21       6       61       45
对应编码: V       G       9        t

 

  例2)字符串长度不能被3整除时,比如“Lucy”:

 

 L           u           c             y
ASCII:        76          117         99            121
8bit字节:   01001100    01110101    01100011    01111001    00000000   00000000
6bit字节:   010011   000111   010101   100011   011110  010000  000000  000000
十进制:      19          7        21        35          30      16      (异常) (异常)      
对应编码:    T           H         V         j           e       Q        =       =
 
   说明:由于Lucy只有4个字母,所以按3个一组的话,第二组还有两个空位,所以需要用0来补齐。这里就需要注意,因为是需要补齐而出现的0,所以转化成十进制的时候就不能按常规用base64编码表来对应,所以不是a可以理解成为一种特殊的“异常”,编码应该对应“=”。

 

 

接下来解读上面的类Test.java运行的过程:

 

 hello world

 对应的ASCII码序列为:

  [104,101,108,108,111,32,119,111,114,108,100]

 对应的8bit字节序列为:

  [01101000,01100101,01101100,01101100,01101111,00100000,01110111,01101111,01110010,01101100,

  01100100,00000000]

 对应的6bit字节序列为:

   [011010,000110,010101,101100,011011,000110,111100,100000,011101,110110,111101,110010,011011

    ,000110,010000,000000]

  十进制序列为:

     [26,6,21,44,27,6,60,32,29,54,61,50,27,6,16, (异常)]

  对应base64编码序列(对照base64编码表)为:

     [aGVsbG8gd29ybGQ=]

 

   和类Test.java运行的结果是一样的。其中值得注意的是Java中的getBytes()方法,可以获得字符的ASCII码值。

 

 根据上面的推理,我实现了Base64加密与解密的算法: 

 

public class Base64 {
 
    private static final String base64code = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "+/";
    private static final String PADDING_STR = "=";
    private static final String PADDING_STR_BINARRY ="00000000";
    private static final String PADDING_STR_BINARRY2 ="000000";
  
    /**加密
     * @param string 加密的报文
     * @return 返回 加密后的报文
     */
    public static String encode(String string) {
    	Integer[] asciiArray = stringToASCII(string);
    	String binarryStr = asciiToBinarry(asciiArray);
    	
    	//补"0"
    	int zeroPaddingNum = 0;
    	int length = asciiArray.length;
        while(length%3!=0){
        	binarryStr += PADDING_STR_BINARRY;
          	length++;
          	zeroPaddingNum++;
        }
    	
        //查Base64编码表,进行"偷天换日"
        StringBuffer retvalue = new StringBuffer();
        while(binarryStr.length()>0){
        	String str = binarryStr.substring(0,6);
        	int temp = Integer.valueOf(str,2);   //二进制转十进制
        	if(binarryStr.length()>zeroPaddingNum*6){
        	    retvalue.append(base64code.charAt(temp));
        	}else{
        		retvalue.append(PADDING_STR);
        	}
        	binarryStr = binarryStr.substring(6);
        }
    	return retvalue.toString();
    }
    
    /**解密
     * @param string 解密的报文
     * @return 返回 解密后的报文
     */
    public static String decode(String string) {
    	
    	char[] charArray = string.toCharArray();
    	//转二进制字符串
    	int zeroPaddingNum = 0;
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<charArray.length;i++){
        	if(base64code.indexOf(String.valueOf(charArray[i]))>-1){
        	   int tempI = base64code.indexOf(String.valueOf(charArray[i]));
        	   String temp = Integer.toBinaryString(tempI);
        	   while(temp.length()<6){
            		temp = "0"+temp;
               }
        	   sb.append(temp);
        	}else{
        		sb.append(PADDING_STR_BINARRY2);
        		zeroPaddingNum++;
        	}
        }
        String binarryStr = sb.toString();
        StringBuffer retvalue = new StringBuffer();
        while(binarryStr.length()>0){
        	String str = binarryStr.substring(0,8);
        	int temp = Integer.valueOf(str,2);   //二进制转十进制
        	if(binarryStr.length()>zeroPaddingNum*8){
        	    retvalue.append((char)temp);
        	} 
        	binarryStr = binarryStr.substring(8);
        }
    	return retvalue.toString();
    }
    
    /**把String转为ASCII码数组
     * 1.先把String转为char[]数组
     * 2.char数组转为ascii数组
     * @param value
     * @return
     */
    private static Integer[] stringToASCII(String value){
    	Integer[] retInt = new Integer[value.length()];
    	char[] charArray = value.toCharArray();
    	for(int i=0;i<charArray.length;i++){
    		retInt[i] = (int)charArray[i];
    	}
    	return retInt;
    }
   
    /**把 ASCII码数组转为二进制字符串
     * @param value
     * @return
     */
    private static String asciiToBinarry(Integer[] asciiArray){
        StringBuffer sb = new StringBuffer();
        for(Integer i : asciiArray){
        	String temp = Integer.toBinaryString(i);
        	while(temp.length()<8){
        		temp = "0"+temp;
        	}
        	sb.append(temp);
        }
        return sb.toString();
    }
    
    public static void main(String[] args) {
        System.out.println(encode("Tom"));
        System.out.println(decode(encode("Tom")));
        System.out.println(encode("Lucy"));
        System.out.println(decode(encode("Lucy")));
        System.out.println(encode("hello world"));
    }
}

 

 

   下面是摘自高人的Base64源码:

package cn.com.base;
public class Base64 {
 
    private static final String base64code = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "+/";
 
    private static final int splitLinesAt = 76;
 
    public static byte[] zeroPad(int length, byte[] bytes) {
        byte[] padded = new byte[length]; // initialized to zero by JVM
        System.arraycopy(bytes, 0, padded, 0, bytes.length);
        return padded;
    }
 
    public static String encode(String string) {
 
        String encoded = "";
        byte[] stringArray;
        try {
            stringArray = string.getBytes("UTF-8");  // use appropriate encoding string!
        } catch (Exception ignored) {
            stringArray = string.getBytes();  // use locale default rather than croak
        }
        // determine how many padding bytes to add to the output
        int paddingCount = (3 - (stringArray.length % 3)) % 3;
        // add any necessary padding to the input
        stringArray = zeroPad(stringArray.length + paddingCount, stringArray);
        // process 3 bytes at a time, churning out 4 output bytes
        // worry about CRLF insertions later
        for (int i = 0; i < stringArray.length; i += 3) {
            int j = ((stringArray[i] & 0xff) << 16) +
                ((stringArray[i + 1] & 0xff) << 8) + 
                (stringArray[i + 2] & 0xff);
            encoded = encoded + base64code.charAt((j >> 18) & 0x3f) +
                base64code.charAt((j >> 12) & 0x3f) +
                base64code.charAt((j >> 6) & 0x3f) +
                base64code.charAt(j & 0x3f);
        }
        // replace encoded padding nulls with "="
        return splitLines(encoded.substring(0, encoded.length() -
            paddingCount) + "==".substring(0, paddingCount));
 
    }
    public static String splitLines(String string) {
 
        String lines = "";
        for (int i = 0; i < string.length(); i += splitLinesAt) {
 
            lines += string.substring(i, Math.min(string.length(), i + splitLinesAt));
            lines += "\r\n";
 
        }
        return lines;
 
    }
    public static void main(String[] args) {
 
        for (int i = 0; i < args.length; i++) {
 
            System.err.println("encoding \"" + args[i] + "\"");
            System.out.println(encode(args[i]));
 
        }
 
    }
 
}

    其中核心算法一系列的左移与右移运算,实现是理解不了!这个必须得有高人指点。

 

 

 

 

      参考资料:

     http://snowolf.iteye.com/blog/379860

            http://my.eoe.cn/indexer/archive/1055.html

            http://www.iteye.com/topic/605714

            http://java.chinaitlab.com/Special/javajm/Index.html

            http://www.wikihow.com/Encode-a-String-to-Base64-With-Java

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值