js base64编码原理解析

加密和编码

现在网上搜索base64,有一种说法是base64编码,也有一种说法是base64加密,这使得我对加密和编码这两个概念产生了一些疑问,到底哪种属于加密,哪种属于编码,这两者又有什么区别呢,无意之间,找到了编码与加密这篇文章,才对两种有了一些概念。

加密:通过Intranet、Extranet和Internet进行安全的信息交换的基础。从业务的角度来看,通过加密实现的安全功能包括:身份验证,使收件人确信发件人就是他或她所声明的那个人;机密性,确保只有预期的收件人能够阅读邮件;以及完整性,确保邮件在传输过程中没有被更改。从技术的角度来看,加密是利用数学方法将邮件转换为不可读格式从而达到保护数据的目的的一门科学。

编码:信息从一种形式或格式转换为另一种形式的过程。解码,是编码的逆过程。从概念上可以看出,编码只是表现形式的转换,没有保密的作用,因为编码和解码的算法是公开的,只要知道是什么编码的内容,任何人都可以轻松地解码。

个人理解:加密是一种不可逆的一种算法,不能通过逻辑逆推就能轻易计算得到明文,像md5这种虽然网上有在线解密,但是也是通过周密和完善的碰撞库经过n次试验才能得到正确的明文,像RSA这种就更不用说了,必须要用私钥才能解密得到明文,否则是解不出来的。但是编码就不同,知道了编码的逻辑算法,我们可以通过逻辑逆推就能轻易的写出解码程序。

base64算法原理

首先说下base64编码算法的原理和步骤吧 1.每个字符转ASCII码 2.ASCII码转二进制 3.以6位一组转10进制,最后一个有01代码但是少于6位的后面补0 4.得到的10进制查base64字符对应表得到另一个字符(这个表格在下面有说,大家随便百度下也可以查到) 5.将所有的字符进行拼接得到编码后的字符串,不足4(或者4的倍数)组的,缺几组最后编码补几个=

上面的步骤干看可能有些抽象,结合下面这个表格大家应该能理解的比较顺畅一些:

文本 M a n
ASCII编码 77 97 110
二进制位 0 1 0 0 1 1 0 1 0 1 1 0 0 0 0 1 0 1 1 0 1 1 1 0
索引 19 22 5 46
Base64编码 T W F u
文本(1 Byte) A    
二进制位 0 1 0 0 0 0 0 1                                
二进制位(补0) 0 1 0 0 0 0 0 1 0 0 0 0                        
Base64编码 Q Q = =
文本(2 Byte) B C  
二进制位 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1     x x x x x x
二进制位(补0) 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 x x x x x x
Base64编码 Q k M  =

看到这里如果大家能够自己用自己擅长的语言实现,那么恭喜大家,下面那些篇幅大家已经基本上不用看啦~~~~

本文知识点

  • ASCII编码
  • 进制转换
  • base64序列字符对应表
  • js位运算度

ASCII编码

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统,并等同于国际标准ISO/IEC 646。

通俗来说就是一套字节编码系统,我们目前所用的一些字符都有其对应的ASCII编码,这里有一张对应表,大家自行百度下就能找到并立即了,这里我就贴下这张对应表吧~~~~~~~~

![ASCII表][1]

有人会问,我知道这个表格,但是我代码里要怎么转呢。因为小弟只是前端,下面就只说下js中,ASCII码转换的相关方法,其他语法的写法请大家自查下哈

1.字符转ASCII码:charCodeAt()。例:’A’.charCodeAt(); 2.ASCII码砖字符:fromCharCode()。例:String.fromCharCode(‘65’);

进制转换

这个概念和算法就不给大家灌输了,想必大家都知道是什么了,这里就简单写下js的实现方法:

1.十进制转二进制:(Number).toString(2)。例:(77).toString(2), 2.二进制转十进制:parseInt(‘10101101’,2) ps:上面的77为啥要加括号,那是因为js里.比较特殊,不加括号会报一个错误。

base64序列字符对应表

编号 字符 编号 字符 编号 字符 编号 字符
0 A 16 Q 32 g 48 w
1 B 17 R 33 h 49 x
2 C 18 S 34 i 50 y
3 D 19 T 35 j 51 z
4 E 20 U 36 k 52 0
5 F 21 V 37 l 53 1
6 G 22 W 38 m 54 2
7 H 23 X 39 n 55 3
8 I 24 Y 40 o 56 4
9 J 25 Z 41 p 57 5
10 K 26 a 42 q 58 6
11 L 27 b 43 r 59 7
12 M 28 c 44 s 60 8
13 N 29 d 45 t 61 9
14 O 30 e 46 u 62 +
15 P 31 f 47 v 63 /

js base64编码方法封装

根据最开始的步骤和上述知识点,我进行我初步的js方法封装,具体代码如下:

encodeBase64: function (str) {
	var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
	var sbyteArr = [];
	str = encodeURI(str);
	var len = str.length;
	for (var i = 0, ilen = len; i < ilen; i++) {
		var asc = str[i].charCodeAt();
		var sbyte = parseInt(asc).toString(2);
		var zerolen = 8 - sbyte.length;
		if (zerolen > 0) {
			var zerostr = '';
			for (var j = 0; j < zerolen; j++) {
				zerostr += '0';
			}
		}
		sbyteArr.push(zerostr + sbyte);
	}
	// console.log(str, sbyteArr);
	var sbytestr = sbyteArr.join('');
	// 3个字符一循环,6个一组,判断最后一个是在第几组,方便补0
	var group = len % 3;
	// 计算差多少个0;
	var afterzerod = (sbytestr.length - group * 24) - group * 6;
	// 后补0
	if (group > 0) {
		var afterzerolen = group * 6 - afterzerod;
		for (var k = 0; k < afterzerolen; k++) {
			sbytestr += '0';
		}
	}
	sbytestrArr = sbytestr.replace(/(\w{6})/g, '$1,').split(',');
	var str = '';
	for (var g = 0, glen = sbytestrArr.length - 1; g < glen; g++) {
		str += _keyStr[parseInt(sbytestrArr[g], 2)];
	}
	// 判断还差几个=
	var strlen = 4 * (group + 1) - str.length;
	// 补=
	if (strlen > 0) {
		for (var l = 0; l < strlen; l++) {
			str += '=';
		}
	}
	// console.log(str);
	return str;
}

到这里基本上大家已经对base64有了初步的认识,代码怎么实现心中也有了个大致的雏形,这时候我去网上看了下js的base64编码,发现差别有点大,其最大的差别就是在一下几步:

2.ASCII码转二进制 3.以6位一组转10进制,最后一个有01代码但是少于6位的后面补0

网络的方法用了js的位运算符,至于位运算符大家可以看下我的另一篇文章:js位运算符

该方法的精髓就是通过巧妙的移位,直接从第二部ASCII码得到第三步的十进制数字,省去中间的一系列逻辑,在上述我的实现代码中,涉及到很多的补0和补位(可能有大牛有更好的实现逻辑~~~~~~~~~),使用移位运算符就能省去这些逻辑,能缩减很大一部分代码。

这里贴下我找到的用位运算符实现的base64编码的算法:

encode: function (input) {
	var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
	var output = "";
	var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
	var i = 0;
	input = encodeURI(input);
	while (i < input.length) {
			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);
			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;
			if (isNaN(chr2)) {
					enc3 = enc4 = 64;
			} else if (isNaN(chr3)) {
					enc4 = 64;
			}
			output = output +
			_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
			_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
	}
	return output;
}

本文参考链接: 1.http://www.cnblogs.com/chengmo/archive/2014/05/18/3735917.html 2.http://blog.csdn.net/leslietuang/article/details/45128799