Base64编码原理解析与Java实现

原文 http://blog.csdn.net/zdqdj1/article/details/51760412

作者: SoyaDokio

一、前言

        目前还在找工作,工作日时投投简历面面试,这周末难免就闲来无事了,那就只好看看慕课逛逛CSDN了,正巧看到一个关于Base64的课程《Java实现Base64加密》,点进去看看,完了发觉完全不是我想的那回事儿,人给的实现方式还不唯一,给了3个方法,但都是API,没有实现原理。而鉴于在下喜欢钻牛角尖,于是抱着试试的心态,查了官网RFC2045下载完整PDF),了解了下相关情况,这才有了本文。


二、Base64是什么

        以下摘自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常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据。包括MIME的email、在XML中存储复杂数据。

----------------------------------------------------------- ↑ 华丽分割线 ↑ -----------------------------------------------------------


三、规则与原理


      原理

        以下摘自Base64的维基百科

----------------------------------------------------------- ↓ 华丽分割线 ↓ -----------------------------------------------------------

        在MIME格式的电子邮件中,base64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本。使用时,在传输编码方式中指定base64。使用的字符包括大小写字母各26个,加上10个数字,和加号“+”,斜杠“/”,一共64个字符,等号“=”用来作为后缀用途。

        完整的base64定义可见RFC1421和RFC2045。编码后的数据比原始数据略长,为原来的4/3。在电子邮件中,根据RFC822规定,每76个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的135.1%。

        转换的时候,将三个byte的数据,先后放入一个24bit的缓冲区中,先来的byte占高位。数据不足3byte的话,于缓冲器中剩下的bit用0补足。然后,每次取出6(因为2^6=64)个bit,按照其值选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符作为编码后的输出。不断进行,直到全部输入数据转换完成。

        当原数据长度不是3的整数倍时,如果最后剩下一个输入数据,在编码结果后加2个“=”;如果最后剩下两个输入数据,编码结果后加1个“=”;如果没有剩下任何数据,就什么都不要加,这样才可以保证数据还原的正确性。

----------------------------------------------------------- ↑ 华丽分割线 ↑ -----------------------------------------------------------

       上面提到编码后的数据长度约为原长的135.1%,算式为:1*(4/3)*((76+1)/76)≈1.351

        鉴于很多朋友不爱看大片枯燥文字,在下以个人理解将之总结为以下3点:

        将原始数据以先后顺序每3个字节分成一组*。在每一组中,把3个8bit的字节(3*8=24)按高低位顺序分为每段6bit的4段(4*6=24),将每段转为十进制得到0~63之间的数,以之为索引在Base64编码表中对照得到4个密文。重复以上操作。
       *若最后一组不足3个字节,则在其二进制位的低位上用尽量少的“0”补位,使二进制位的个数为6的倍数。

        ②密文每76个字符数据后加一个换行符*
       
*注意这里的换行符指CRLF(\r\n),而不是LF(\n),为什么?请看RFC2045下载完整PDF)。

        若原始数据字节长度除以3余1,则在输出数据末尾加2个“=”,若原始数据长度除以3余2,则在输出数据末尾加1个“=”


      规则

        1.ASCII编码表:

        2.Base64编码表


四、实例

        我们还是来看下面这3个例子比较直观(例子下载)。

        例子1、假设我们的明文为“Base64”(数据长度为6,正好是3的倍数),则其编码计算方式如下:


        例子2、假设我们的明文为“test”(数据长度为44%3=1),则其编码计算方式如下:



        例子3、假设我们的明文为“JiaMi”(数据长度为55%3=2),则其编码计算方式如下:



五、编程思路

        思路什么的,我觉得就不必要写了,代码里注释得很充分了。


六、代码

package com.dokio.base64;

public class Base64 {
	
	//Constructor
	public Base64() {
		
	}
	
	private static String base64Code= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	
	public static String encode(String srcStr) {
		//有效值检查
		if(srcStr == null || srcStr.length() == 0) {
			return srcStr;
		}
		//将明文的ASCII码转为二进制位字串
		char[] srcStrCh= srcStr.toCharArray();
		StringBuilder asciiBinStrB= new StringBuilder();
		String asciiBin= null;
		for(int i= 0; i< srcStrCh.length; i++) {
			asciiBin= Integer.toBinaryString((int)srcStrCh[i]);
			while(asciiBin.length()< 8) {
				asciiBin= "0"+ asciiBin;
			}
			asciiBinStrB.append(asciiBin);
		}
		//跟据明文长度在二进制位字串尾部补“0”
		while(asciiBinStrB.length()% 6!= 0) {
			asciiBinStrB.append("0");
		}
		String asciiBinStr= String.valueOf(asciiBinStrB);
		//将上面得到的二进制位字串转为Value,再跟据Base64编码表将之转为Encoding
		char[] codeCh= new char[asciiBinStr.length()/ 6];
		int index= 0;
		for(int i= 0; i< codeCh.length; i++) {
			index= Integer.parseInt(asciiBinStr.substring(0, 6), 2);
			asciiBinStr= asciiBinStr.substring(6);
			codeCh[i]= base64Code.charAt(index);
		}
		StringBuilder code= new StringBuilder(String.valueOf(codeCh));
		//跟据需要在尾部添加“=”
		if(srcStr.length()% 3 == 1) {
			code.append("==");
		} else if(srcStr.length()% 3 == 2) {
			code.append("=");
		}
		//每76个字符加一个回车换行符(CRLF)
		int i= 76;
		while(i< code.length()) {
			code.insert(i, "\r\n");
			i+= 76;
		}
		code.append("\r\n");
		return String.valueOf(code);
	}
	
	public static String decode(String srcStr) {
		//有效值检查
		if(srcStr == null || srcStr.length() == 0) {
			return srcStr;
		}
		//检测密文中“=”的个数后将之删除,同时删除换行符
		int eqCounter= 0;
		if(srcStr.endsWith("==")) {
			eqCounter= 2;
		} else if(srcStr.endsWith("=")) {
			eqCounter= 1;
		}
		srcStr= srcStr.replaceAll("=", "");
		srcStr= srcStr.replaceAll("\r\n", "");
		//跟据Base64编码表将密文(Encoding)转为对应Value,然后转为二进制位字串
		char[] srcStrCh= srcStr.toCharArray();
		StringBuilder indexBinStr= new StringBuilder();
		String indexBin= null;
		for(int i= 0; i< srcStrCh.length; i++) {
			indexBin= Integer.toBinaryString(base64Code.indexOf((int)srcStrCh[i]));
			while(indexBin.length()< 6) {
				indexBin= "0"+ indexBin;
			}
			indexBinStr.append(indexBin);
		}
		//删除因编码而在尾部补位的“0”后得到明文的ASCII码的二进制位字串
		if(eqCounter == 1) {
			indexBinStr.delete(indexBinStr.length()- 2, indexBinStr.length());
		} else if(eqCounter == 2) {
			indexBinStr.delete(indexBinStr.length()- 4, indexBinStr.length());
		}
		String asciiBinStr= String.valueOf(indexBinStr);
		//将上面得到的二进制位字串分隔成字节后还原成明文
		String asciiBin= null;
		char[] ascii= new char[asciiBinStr.length()/ 8];
		for(int i= 0; i< ascii.length; i++) {
			asciiBin= asciiBinStr.substring(0, 8);
			asciiBinStr= asciiBinStr.substring(8);
			ascii[i]= (char)Integer.parseInt(asciiBin, 2);
		}
		return String.valueOf(ascii);
	}
	
	public static void main(String[] args) {
		System.out.print(encode("I like your long long shadow.It just seems you are unhappy to say goodbye to me."));
		System.out.print("\n------\n");
		System.out.print(decode("SSBsaWtlIHlvdXIgbG9uZyBsb25nIHNoYWRvdy5JdCBqdXN0IHNlZW1zIHlvdSBhcmUgdW5oYXBweSB0byBzYXkgZ29vZGJ5ZSB0byBtZS4="));
		System.out.print("\n------\n");
		System.out.print(decode(encode("I like your long long shadow.It just seems you are unhappy to say goodbye to me.")));
	}
	
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值