base64原理与运用

什么是base64

Base64 编码,首先我们应该搞清楚,为什么里面有个 64 的字样呢?其实是因为该编码使用 64 个明文来编码任意 的二进制文件,它里面只使用了 A-Z,a-z,0-9,+,/这 64 个字符,有“略懂”的同学就会说了,里面还有“=”号啊,不错,不过等号不属于编码字符,而是填充字符。

Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。

用记事本打开exejpgpdf这些文件时,我们都会看到一大堆乱码,因为二进制文件包含很多无法显示和打印的字符,所以,如果要让记事本这样的文本处理软件能处理二进制数据,就需要一个二进制到字符串的转换方法。Base64是一种最常见的二进制编码方法。

Base64编码是从二进制到字符的过程,可用于在http环境下传递较长的标识信息。

如下:

诞生原因:

为什么发明这么个编码呢,其实这个编码的原理是很简单的,“破解”也很容易,电子邮件刚出来的 时候,只传递英文字符,这没有问题,但是后来,中国人,日本人都要发 email,这样问题就来了,因为这些字符有可能会被邮件服务器或者网关当成命令处 理,故必须得有一种编码来对邮件进行加密,但是加密的目的是为了能够使得一些原始的服务器不出问题(新得牛叉服务器已经能处理这些乱七八糟得情况了,不过 因为已经形成了一套规范,所以邮件还是得经过 Base64 编码才能传递),这样加密必须得简单(那搞个取反,异或加密吧,还是没解决根本问题 咯),加密简单,这样客户端程序加密解密也快,又要是明文 Ascii 编码,这样 Base64 就诞生了。 

编码原理:

64是2的6次方,所以用6bit来表示一个base64字符,然而ASCII码需要8个Bit来表示,所以正常来转换是以3个字节为单位,这样3*8=4*6三个字符就转换 成了4个base64字符,长度增加33%,好处是编码后的文本数据可以在邮件正文、网页等直接显示。

以字符串"Son"为例,通过base64编码后就是:U29u,如下图

刚刚好的情况,3个ASCII字符刚好转换成对应的4个Base64字符。但是,当需要转换的字符数不是3的倍数的情况下该怎么办呢?Base64规定,当字符数不是 3 的整数倍时,字符数/3 的余数自然就是 2 或者 1。转换的时候,结果不够 6 位的用 0 来补上相应的位置,之后再在 6 位的前面补两个 0。转换完空出的结果就用 就用“=”来补位,总之要保证最后编码出来得字节数是 4 的倍数。

扫描二维码关注公众号,回复: 4475028 查看本文章

每6个Bit为一组,第一组转换后为字符“U”,第二组末尾补4个0转换后为字符“w”。剩下的使用“=”替代。即字符“S”通过Base64编码后为“Uw==”。这就是Base64的编码过程。

在线Base64转换器

在线Base64转换器

Java中实现Base64

实现方式有3种(3中方式编码出来的结果都是一样的),任选一种即可,正常项目中用得比较多的是用Apache Commons Codec提供的实现.

1.jdk自带的方式

2.(第三方)Apache Commons Codec提供的实现,需要引入依赖:

<!-- Apache Commons Codec -->
<dependency>
	<groupId>commons-codec</groupId>
	<artifactId>commons-codec</artifactId>
	<version>1.10</version>
</dependency>

3.(第三方)bouncy Castle提供的实现,需要引入依赖:

<!-- bouncy castle -->
<dependency>
	<groupId>org.bouncycastle</groupId>
	<artifactId>bcprov-jdk15on</artifactId>
	<version>1.60</version>
</dependency>

代码实现,我这里三种方式都有提供,各位按需取用:

import java.io.IOException;
import java.util.Arrays;

import org.apache.commons.codec.binary.Base64;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Base64Demo {

		private static String src = "Son";

		public static void main(String[] args) {
			System.out.println(Arrays.toString("0".getBytes()));
			jdkBase64();
			System.out.println("------------------");
			commonsCodesBase64();
			System.out.println("------------------");
			bouncyCastleBase64();
		}
		
		/**
		 * jdk自带的实现
		 */
		@SuppressWarnings("restriction")
		public static void jdkBase64() {
			try {
				BASE64Encoder encoder = new BASE64Encoder();
				String encode = encoder.encode(src.getBytes());
				System.out.println("encode : " + encode);
				
				BASE64Decoder decoder = new BASE64Decoder();
				System.out.println("decode : " + new String(decoder.decodeBuffer(encode)));
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		/**
		 * (第三方)Apache Commons Codec提供的实现
		 */
		public static void commonsCodesBase64() {
			byte[] encodeBytes = Base64.encodeBase64(src.getBytes());
			System.out.println("encode : " + new String(encodeBytes));
			
			byte[] decodeBytes = Base64.decodeBase64(encodeBytes);
			System.out.println("decode : " + new String(decodeBytes));
		}
		
		/**
		 * (第三方)bouncy Castle提供的实现
		 */
		public static void bouncyCastleBase64() {
			byte[] encodeBytes = org.bouncycastle.util.encoders.Base64.encode(src.getBytes());
			System.out.println("encode : " + new String(encodeBytes));
			
			byte[] decodeBytes = org.bouncycastle.util.encoders.Base64.decode(encodeBytes); 
			System.out.println("decode : " + new String(decodeBytes));
		}

}

带来的一些问题:

1.由于标准的Base64编码后可能出现字符+/,在URL中就不能直接作为参数,所以又有一种"url safe"的base64编码,其实就是把字符+/分别变成-_

/**
 * URL安全的base64编码
*/
public static void commonsCodesBase64URLSafe() {
	// encoder will emit - and _ instead of the usual + and / characters
	String encode = Base64.encodeBase64URLSafeString("1245".getBytes());
	System.out.println(encode);
	byte[] decode = Base64.decodeBase64(encode);
	System.out.println(new String(decode));
}

2.由于=字符也可能出现在Base64编码中,但=用在URL、Cookie里面会造成歧义,所以,很多Base64编码后要把=去掉,这里可以用URLEncoderURLDecoder来解决.

public static void commonsCodesBase64CookieSafe() throws UnsupportedEncodingException {
	byte[] encodeBytes = Base64.encodeBase64("12".getBytes());
	System.out.println("base64Encode : " + new String(encodeBytes));
	String encode = URLEncoder.encode(new String(encodeBytes), "UTF-8");
	System.out.println("URLEncoder : " +encode);
	String decode = URLDecoder.decode(encode, "UTF-8");
	System.out.println("URLDecoder : " +decode);
	byte[] decodeBytes = Base64.decodeBase64(decode);
	System.out.println("base64Decode : " + new String(decodeBytes));
}

输出结果:

base64Encode : MTI=
URLEncoder : MTI%3D
URLDecoder : MTI=
base64Decode : 12

一些使用案例

先以“迅雷下载”为例: 很多下载类网站都提供“迅雷下载”的链接,其地址通常是加密的迅雷专用下载地址。

其实迅雷的“专用地址”也是用Base64"加密"的,其过程如下:

一、在地址的前后分别添加AA和ZZ

二、对新的字符串进行Base64编码

另: Flashget的与迅雷类似,只不过在第一步时加的“料”不同罢了,Flashget在地址前后加的“料”是[FLASHGET]

QQ旋风的干脆不加料,直接就对地址进行Base64编码了

参考资料:

https://baike.baidu.com/item/base64

https://blog.csdn.net/qq_20545367/article/details/79538530

https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001399413803339f4bbda5c01fc479cbea98b1387390748000

https://cloud.tencent.com/developer/article/1134508

猜你喜欢

转载自blog.csdn.net/u010825931/article/details/84952478