Skip to content

Latest commit

 

History

History
114 lines (83 loc) · 6.98 KB

24.Java加密解密之Base64.md

File metadata and controls

114 lines (83 loc) · 6.98 KB

#24.Java加密解密之Base64

##24.1 Base64 在计算中,字节(byte)是最小的计量单元。ASCII编码使用一个字节对数据进行编码,共可容纳2^8=256个字符。然而在ASCII编码的256个字符当中,有很多字符都是不可打印、对人不可见的。例如空字符、响铃、退格等等字符。如果要让数据的二进制对人可见,可以将二进制转为二进制、八进制或十六进制的字符串表示,不足的是,这些字符串表示都会占用大量计算机存储空间。

当然,还有一种选择那就是使用Base64编码。Base64是一种基于64个可打印字符([A-Za-z0-9+/])来表示二进制数据的表示方法。

Base64基于64个可打印字符,所以每一个Base64编码单元只需要log2(64) = 6个位元。而计算机的计量单元是8个位元,6与8的最小公倍数是24,所以可以使用24个位元(也就是三个字节)作为一个编码处理单元。该编码处理单元规则如下:

  • 第1个字节的前6个位元作为Base64编码的第1个编码单元;
  • 第1个字节的后2个位元和第2个字节的前4位作为Base64编码的第2个编码单元;
  • 第2个字节的后4个位元和第3个字节的后4位作为Base64编码的第3个编码单元;
  • 第3个字节的后6个位元作为Base64编码的第4个编码单元。

引用维基百科的例子:
Base64编码示例

##24.2 补足空缺位元

在Base64编码中,是以24个位元(3个字节)作为一个编码处理单元的,那就可能出现提供的位元不足3个字节(缺1个或2个字节)。处理方法如下:

  • 如果缺1个字节,使用0来填充,在Base64编码结果后面加1个**=来说明填充了1**个字节;
  • 如果缺2个字节,使用0来填充,在Base64编码结果后面加2个**=来指明填充了2**个字节;
  • 后面全部由0填充所产生的字符都需要进行删除。如果填充了1个字节0,则需要删除Base64字符序列(未填充"="前)的最后一个字符(000000对应的字符是A);如果填充了2个字节0,则需要删除Base64字符序列(未填充"="前)的最后两个字符(实际上该字符是A)。

24.3 代码实现

由于计算机最小的处理单元是字节,所以要将数据转为基于6个位元为一个编码单元的Base64编码,则需要将这6个位元转为可以处理的对应的8个位元(1个字节)。所以需要采用位操作:<<>>>|和&

import java.io.UnsupportedEncodingException;
import org.junit.Test;

public class Base64Test {

	@Test
	public void t1_encode() throws UnsupportedEncodingException {
		String text = "HelloWorld!";
		System.out.println(base64Encode(text.getBytes("ASCII"))); // SGVsbG9Xb3JsZCE=
	}

	public static String base64Encode(byte[] data) {
		int len = data.length;
		int paddingLen = 3 - (len % 3 == 0 ? 3 : len % 3); // 填充字节数

		StringBuilder dest = new StringBuilder();
		for (int i = 0; i < len - paddingLen - 1;) {
			handleUnit(data[i++], data[i++], data[i++], dest);
		}
		if (paddingLen != 0) { // 处理填充
			handlePadding(data, len, paddingLen, dest);
		}

		return dest.toString();
	}

	/**
	 * Base64编码处理单元
	 */
	public static void handleUnit(byte b1, byte b2, byte b3, StringBuilder dest) {
		dest.append(BASE64_DIGITS[b1 >>> 2]);
		dest.append(BASE64_DIGITS[(b1 << 4 | b2 >>> 4) & 0x3f]);
		dest.append(BASE64_DIGITS[(b2 << 2 | b3 >>> 6) & 0x3f]);
		dest.append(BASE64_DIGITS[b3 & 0x3f]);
	}

	public static void handlePadding(byte[] data, int len, int paddingLen, StringBuilder dest) {
		if (paddingLen == 1) {
			handleUnit(data[len - len % 3], data[len - len % 3 + 1], (byte) 0x00, dest);
		}

		if (paddingLen == 2) {
			handleUnit(data[len - len % 3], (byte) 0x00, (byte) 0x00, dest);
		}

		dest.setLength(dest.length() - paddingLen); // 删除由0填充产生的字符

		for (int j = 0; j < paddingLen; j++) { // 填充"="
			dest.append("=");
		}
	}

	private static final char[] BASE64_DIGITS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
			'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
			'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
			'4', '5', '6', '7', '8', '9', '+', '/' };
}

以上是Base64编码的一种实现,效率上当然不是最佳的,这也是本人在不参考任何现成代码实现的基础上实现的功能,主要为了加深自己对其的了解 :)。

##24.4 Base64 URL Safe

24.3节的Base64编码结果不能直接用作URL的参数传输,因为Base64的"+","/","="分别对应于URL中的空格、路径分隔符和key=val分割符,如果直接用于URL传参,则会扰乱URL的语义。为了可以让Base64编码在URL使用,人们将Base64中的**"+""/"替换成"-""_",同时不再使用"="**进行末尾填充。

##24.5 Base64应用场景

###24.5.1 将图片内嵌到img标签 HTML5中可以使用伪协议data,将图片以base64的编码内嵌到img标签中。

<img alt="" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAAaABwDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD6X+HXgm4+It/dlrxdP0uxQSXV44BCg5wBkgZIBOTwACT2B1de+Gfh3V9B1LUPBHihNdm0xPMurUujErgncpUDghWI4IbBweK5j4MeOdHhtvE/hPXbl7Gy8QWnkx3wjLJCwVw3mHPClXzkgKNhDH5hXoHwl8FaH4V1DWIdN8b6Nr3iS+sJYrO0trpNoTg73VWYnkL0HADYzzj9Xx1evhK1STm48vLyrlvGS0u27evVWsfjOXZfhsXh6UPZqXPzc8ua0ovXlSV/To73OOvPgdq9zaNJour6XrlzAo+1WlvcKskD4yVPOPxJH0ryI6jk8MCPUHINei+DdOn+Hvwt+KV88CxalbtHoxaAqTGWk2S7WH/XQHP+zXin2/HAIx9K+oy32tWVWM5qcYtJO1r+6m9na2qsfHZvhsPSjRlSpuEpptxbva0nFbq99Hc9M/Zt8a2OjfEdrHU7tbKy1iymsGuzKIjA5AZXEnBU/IVGD1cegqdNM+DPgzUYxefELWtb1K2cMtz4didERhn5lmUNk+6vnmvny6AcFWG5T1B6VUckZ5r1KmTqrXlWjVlBSSTUba2vrdpvZ2srHoUsy9jh4UZUozcG2m76Xt0TXVbs96+Jvxl8OSeCIvBHgKznttCkdZ7++vUKz3MgcOBzyTuVSzHsAoGM1499uHrWIWPHJruPA1lb3OkytNBFKwnI3OgY42r6120MHRy2i40ru7u23dtvq33POxWIqZjVU6tlZWSSskl0SP/Z" />

显示结果:

###24.5.2 密钥存储

###24.5.3 数据证书存储

###24.5.4 简单加密

##24.6 参考资料