Base64加密和解密JDK8

       在 Java8中 Base64编码已经成为Java类库的标准,且内置了Base64编码的编码器和解码器。

一、什么是Base64?  -- 来自百度百科

        Base64是网络上最常见的用于传输 8Bit字节码的编码方式之一,

        Base64就是一种基于64个可打印字符来表示二进制数据的方法。

1、什么是“可打印字符”呢?为什么要用它来传输 8Bit字节码呢?

        Base64一般用于在 HTTP协议下传输二进制数据,由于 HTTP协议是文本协议,所以在 HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的。因为网络传输只能传输可打印字符。

        什么是可打印字符?在ASCII码中规定,0~31、127这33个字符属于控制字符,32~126这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符,不在这个范围内的字符无法传输。那么该怎么才能传输其他字符呢?其中一种方式就是使用Base64。

2、Base64的编码原理

       Base64的原理比较简单,每当我们使用Base64时都会先定义一个类似这样的数组:

['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']

        Base64采用了 "A-Z、a-z、0-9、+、/" 64个可打印字符,这是标准的Base64协议规定。在日常使用中我们还会看到 “=”或“==” 号出现在Base64的编码结果中,“=”在此是作为填充字符出现,后面会讲到。

       Base64就是使用这64个可打印字符来表示二进制数据的方法。Base64的索引与对应字符的关系如下表所示:

索引

对应字符

索引

对应字符

索引

对应字符

索引

对应字符

0

A

17

R

34

i

51

z

1

B

18

S

35

j

52

0

2

C

19

T

36

k

53

1

3

D

20

U

37

l

54

2

4

E

21

V

38

m

55

3

5

F

22

W

39

n

56

4

6

G

23

X

40

o

57

5

7

H

24

Y

41

p

58

6

8

I

25

Z

42

q

59

7

9

J

26

a

43

r

60

8

10

K

27

b

44

s

61

9

11

L

28

c

45

t

62

+

12

M

29

d

46

u

63

/

13

N

30

e

47

v

   

14

O

31

f

48

w

   

15

P

32

g

49

x

   

16

Q

33

h

50

y

 

3、具体转换步骤

        第一步,将待转换的字符串每三个字节分为一组,每个字节占 8bit,那么共有24个二进制位。

        第二步,将上面的24个二进制位每6个一组,共分为4组。

        第三步,在每组前面添加两个0,每组由6个变为8个二进制位,总共32个二进制位,即四个字节。

        第四步,根据Base64的索引与对应字符的关系获得对应的字符值。

从上面的步骤我们发现:

         Base64字符表中的字符原本用6个bit就可以表示,现在前面添加2个0,变为8个bit,会造成一定的浪费。因此,Base64编码之后的文本,要比原文多大约三分之一

         为什么使用3个字节一组呢?因为6和8的最小公倍数为24,三个字节正好24个二进制位,每6个bit位一组,恰好能够分为4组。

4、示例说明

   1)以下图的表格为示例,我们具体分析一下整个过程。

      

        第一步:字符串“Man”为3个字节,“M”、“a”、"n"对应的ASCII码值分别为77,97,110,对应的二进制值是01001101、01100001、01101110。如图第二三行所示,由此组成一个24位的二进制字符串。

        第二步:如图红色框,将24位每6位二进制位一组分成四组。

        第三步:在上面每一组前面补两个0,扩展成32个二进制位,此时变为四个字节:00010011、00010110、00000101、00101110。分别对应的值(Base64编码索引)为:19、22、5、46。

        第四步:用上面的值在Base64编码表中进行查找,分别对应:T、W、F、u。因此“Man”Base64编码之后就变为:TWFu。

   2)位数不足情况

       上面是按照三个字节来举例说明的,如果字节数不足三个,那么该如何处理?

        

        两个字节:两个字节共16个二进制位,依旧按照规则进行分组。此时总共16个二进制位,每6个一组,则第三组缺少2位,用0补齐,得到三个Base64编码,第四组完全没有数据则用“=”补上。因此,上图中“BC”转换之后为“QKM=”;

        一个字节:一个字节共8个二进制位,依旧按照规则进行分组。此时共8个二进制位,每6个一组,则第二组缺少4位,用0补齐,得到两个Base64编码,而后面两组没有对应数据,都用“=”补上。因此,上图中“A”转换之后为“QQ==”;

    3)注意事项

        大多数编码都是由字符串转化成二进制的过程,而Base64的编码则是从二进制转换为字符串。与常规恰恰相反,

        Base64编码主要用在传输、存储、表示二进制领域,不能算得上加密,只是无法直接看到明文。也可以通过打乱Base64编码来进行加密。

        中文有多种编码(比如:utf-8、gb2312、gbk等),不同编码对应Base64编码结果都不一样。

延伸:

        上面我们已经看到了Base64就是用6位(2的6次幂就是64)表示字符,因此成为Base64。同理,Base32就是用5位,Base16就是用4位。大家可以按照上面的步骤进行演化一下。

 

二、Java8中实现Base64

       在 Java8中 Base64编码已经成为Java类库的标准,且内置了Base64编码的编码器和解码器。

       新的Base64API也支持URL和MINE的编码解码。我们直接调用即可。

       java.util.Base64 类仅由用于获得Base64编码方案的编码器和解码器的静态方法组成。

       java.util.Base64.Encoder 类使用RFC 4648和RFC 2045中规定的Base64编码方案来实现用于编码字节数据的编码器。

    • byte[] encode(byte[] src)

      使用Base64编码方案将指定字节数组中的所有字节编码为新分配的字节数组。

      int encode(byte[] src, byte[] dst)

      使用Base64编码方案对来自指定字节数组的所有字节进行编码,将生成的字节写入给定的输出字节数组,从偏移0开始。

      ByteBuffer encode(ByteBuffer buffer)

      使用Base64编码方案将所有剩余字节从指定的字节缓冲区编码到新分配的ByteBuffer中。

      String encodeToString(byte[] src)

      使用Base64编码方案将指定的字节数组编码为字符串。

      Base64.Encoder withoutPadding()

      返回一个编码器实例,编码器等效于此编码器实例,但不会在编码字节数据的末尾添加任何填充字符。

      OutputStream wrap(OutputStream os)

      使用Base64编码方案包装用于编码字节数据的输出流。

       java.util.Base64.Decoder 该类使用RFC 4648和RFC 2045中规定的Base64编码方案来实现用于解码字节数据的解码器。

    • byte[] decode(byte[] src)

      使用Base64编码方案从输入字节数组中解码所有字节,将结果写入新分配的输出字节数组。

      int decode(byte[] src, byte[] dst)

      使用Base64编码方案从输入字节数组中解码所有字节,将结果写入给定的输出字节数组,从偏移0开始。

      ByteBuffer decode(ByteBuffer buffer)

      使用Base64编码方案从输入字节缓冲区中解码所有字节,将结果写入新分配的ByteBuffer。

      byte[] decode(String src)

      使用Base64编码方案将Base64编码的字符串解码为新分配的字节数组。

      InputStream wrap(InputStream is)

      返回一个输入流,用于解码Base64编码字节流。

加密字符串测试 demo

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Demo {

    public static void main(String[] args) throws UnsupportedEncodingException {
        String str = "hello Base64 啊啊\n" +
                "ADCDEFG";
        //编码加密
        String encodeStr = Base64.getEncoder().encodeToString(str.getBytes("UTF-8"));
        System.out.println("加密后的字符串为:" + encodeStr);

        //解码解密
        String decoderStr = new String(Base64.getDecoder().decode(encodeStr), StandardCharsets.UTF_8); //
        // 推荐使用StandardCharsets类指定
        System.out.println("解密后的字符串为" + decoderStr);

    }
}

       

        JDK8 的 Base64类是基于 rfc2045和 rfc4648实现的,根据上文列出的协议内容可以确定,该类的编码结果不会包含换行符,而且在类的注释中明确说明了不会添加换行符。解码会正常还原。

结论:针对比较长的原文进行base64编码:

       jdk7的编码结果包含换行;

       jdk8的编码结果不包含换行;

       jdk8无法解码包含换行的编码结果;

       jdk8的编码结果使用jdk7进行解码,没有任何问题,不再演示

 

三、其他Base64库

    1、Apache Common

        Apache Common中的 org.apache.commons.codec.binary.Base64类是基于 rfc2045实现的,根据类注释可以了解到此实现解码时忽略了所有不在the base64 alphabet范围内的字符,所以该实现可以处理包含换行符的base64编码结果。

        同时该类的编码方法提供了参数,用于指定编码结果长度在超过76个字符的时候是否添加换行,默认不换行。

        

    2、Spring Core

        Spring Core提供了 org.springframework.util.Base64Utils类,该类只是一个工具类,并没有实现任何协议。

优先使用java8中的java.util.Base64类进行编码和解码;
如果java.util.Base64不存在,则会使用org.apache.commons.codec.binary.Base64;
如果都不存在,则会报错

        

    3、协议简述(rfc1521、rfc2045、和rfc4648)看参考文章

 

参考文章:这两篇文章对理解 Base64 很有帮助,感谢前辈

        一篇文章彻底弄懂Base64编码原理

        升级JDK8的坑----base64

 

       站在前辈的肩膀上,每天进步一点点
ends~

发布了248 篇原创文章 · 获赞 59 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_42402854/article/details/99849284
今日推荐