天天看點

一篇文章徹底弄懂Base64編碼原理

Base64的由來

目前Base64已經成為網絡上常見的傳輸8Bit位元組代碼的編碼方式之一。

在做支付系統時,系統之間的封包互動都需要使用Base64對明文進行轉碼,然後再進行簽名或加密,之後再進行(或再次Base64)傳輸。那麼,Base64到底起到什麼作用呢?

在參數傳輸的過程中經常遇到的一種情況:使用全英文的沒問題,但一旦涉及到中文就會出現亂碼情況。

與此類似,網絡上傳輸的字元并不全是可列印的字元,比如二進制檔案、圖檔等。Base64的出現就是為了解決此問題,它是基于64個可列印的字元來表示二進制的資料的一種方法。

電子郵件剛問世的時候,隻能傳輸英文,但後來随着使用者的增加,中文、日文等文字的使用者也有需求,但這些字元并不能被伺服器或網關有效處理,是以Base64就登場了。随之,Base64在URL、Cookie、網頁傳輸少量二進制檔案中也有相應的使用。

Base64的編碼原理

Base64的原理比較簡單,每當我們使用Base64時都會先定義一個類似這樣的數組:

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

上面就是Base64的索引表,字元選用了”A-Z、a-z、0-9、+、/” 64個可列印字元,這是标準的Base64協定規定。

在日常使用中我們還會看到“=”或“==”号出現在Base64的編碼結果中,“=”在此是作為填充字元出現,後面會講到。

具體轉換步驟

第1步,将待轉換的字元串每三個位元組分為一組,每個位元組占8bit,那麼共有24個二進制位。

第2步,将上面的24個二進制位每6個一組,共分為4組。

第3步,在每組前面添加兩個0,每組由6個變為8個二進制位,總共32個二進制位,即四個位元組。

第4步,根據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

從上面的步驟我們發現:

- Base64字元表中的字元原本用6個bit就可以表示,現在前面添加2個0,變為8個bit,會造成一定的浪費。是以,Base64編碼之後的文本,要比原文大約三分之一。

- 為什麼使用3個位元組一組呢?因為6和8的最小公倍數為24,三個位元組正好24個二進制位,每6個bit位一組,恰好能夠分為4組。

示例說明

以下圖的表格為示例,我們具體分析一下整個過程。

第1步:“M”、“a”、”n”對應的ASCII碼值分别為77,97,110,對應的二進制值是01001101、01100001、01101110。如圖第二三行所示,由此組成一個24位的二進制字元串。

第2步:如圖紅色框,将24位每6位二進制位一組分成四組。

第3步:在上面每一組前面補兩個0,擴充成32個二進制位,此時變為四個位元組:00010011、00010110、00000101、00101110。分别對應的值(Base64編碼索引)為:19、22、5、46。

第4步:用上面的值在Base64編碼表中進行查找,分别對應:T、W、F、u。是以“Man”Base64編碼之後就變為:TWFu。

位數不足情況

上面是按照三個位元組來舉例說明的,如果位元組數不足三個,那麼該如何處理?

兩個位元組:兩個位元組共16個二進制位,依舊按照規則進行分組。此時總共16個二進制位,每6個一組,則第三組缺少2位,用0補齊,得到三個Base64編碼,第四組完全沒有資料則用“=”補上。是以,上圖中“BC”轉換之後為“QKM=”;

一個位元組:一個位元組共8個二進制位,依舊按照規則進行分組。此時共8個二進制位,每6個一組,則第二組缺少4位,用0補齊,得到兩個Base64編碼,而後面兩組沒有對應資料,都用“=”補上。是以,上圖中“A”轉換之後為“QQ==”;

注意事項

大多數編碼都是由字元串轉化成二進制的過程,而Base64的編碼則是從二進制轉換為字元串。與正常恰恰相反,

Base64編碼主要用在傳輸、存儲、表示二進制領域,不能算得上加密,隻是無法直接看到明文。也可以通過打亂Base64編碼來進行加密。

中文有多種編碼(比如:utf-8、gb2312、gbk等),不同編碼對應Base64編碼結果都不一樣。

延伸

上面我們已經看到了Base64就是用6位(2的6次幂就是64)表示字元,是以成為Base64。同理,Base32就是用5位,Base16就是用4位。大家可以按照上面的步驟進行演化一下。

Java 驗證

最後,我們用一段Java代碼來驗證一下上面的轉換結果:

import sun.misc.BASE64Encoder;

public class Base64Utils {

   public static void main(String[] args) {

       String man = "Man";

       String a = "A";

       String bc = "BC";

       BASE64Encoder encoder = new BASE64Encoder();

       System.out.println("Man base64結果為:" + encoder.encode(man.getBytes()));

       System.out.println("BC base64結果為:" + encoder.encode(bc.getBytes()));

       System.out.println("A base64結果為:" + encoder.encode(a.getBytes()));

   }

}

列印結果為:

Man base64結果為:TWFu

BC base64結果為:QkM=

A base64結果為:QQ==

以上結果與我們分析所得完全一緻。

原文連結:

https://www.choupangxia.com/topic/detail/61

----------------------------轉載部分end-----------------------------

----------------------------補充内容 start-----------------------------

Base64URL

java.utni.Base64類的源碼的字元提供了兩種,一種是是普通的字元(rfc2045),一種是Url的字元(rf4648)。

https://tools.ietf.org/html/rfc4648 https://tools.ietf.org/html/rfc2045

提供了三個編碼器

    static final Encoder RFC4648 = new Encoder(false, null, -1, true);

    static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);

    static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);

提供了對外函數

/**

    * Returns a {@link Encoder} that encodes using the

    * <a href="#basic">Basic</a> type base64 encoding scheme.

    *

    * @return  A Base64 encoder.

    */

   public static Encoder getEncoder() {

        return Encoder.RFC4648;

   /**

    * <a href="#url">URL and Filename safe</a> type base64

    * encoding scheme.

   public static Encoder getUrlEncoder() {

        return Encoder.RFC4648_URLSAFE;

    * <a href="#mime">MIME</a> type base64 encoding scheme.

   public static Encoder getMimeEncoder() {

       return Encoder.RFC2045;

編碼

        * Encodes all bytes from the specified byte array into a newly-allocated

        * byte array using the {@link Base64} encoding scheme. The returned byte

        * array is of the length of the resulting bytes.

        *

        * @param   src

        *          the byte array to encode

        * @return  A newly-allocated byte array containing the resulting

        *          encoded bytes.

        */

       public byte[] encode(byte[] src) {

           int len = outLength(src.length);          // dst array size

           byte[] dst = new byte[len];

           int ret = encode0(src, 0, src.length, dst);

           if (ret != dst.length)

                return Arrays.copyOf(dst, ret);

           return dst;

       }

        * Encodes the specified byte array into a String using the {@link Base64}

        * encoding scheme.

        * <p> This method first encodes all input bytes into a base64 encoded

        * byte array and then constructs a new String by using the encoded byte

        * array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1

        * ISO-8859-1} charset.

        * <p> In other words, an invocation of this method has exactly the same

        * effect as invoking

        * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.

        * @return  A String containing the resulting Base64 encoded characters

       @SuppressWarnings("deprecation")

       public String encodeToString(byte[] src) {

           byte[] encoded = encode(src);

           return new String(encoded, 0, 0, encoded.length);

解碼

    static final Decoder RFC4648         = new Decoder(false, false);

    static final Decoder RFC4648_URLSAFE = new Decoder(true, false);

    static final Decoder RFC2045         = new Decoder(false, true);

  /**

        * Decodes all bytes from the input byte array using the {@link Base64}

        * encoding scheme, writing the results into a newly-allocated output

        * byte array. The returned byte array is of the length of the resulting

        * bytes.

        *          the byte array to decode

        * @return  A newly-allocated byte array containing the decoded bytes.

        * @throws  IllegalArgumentException

        *          if {@code src} is not in valid Base64 scheme

       public byte[] decode(byte[] src) {

           byte[] dst = new byte[outLength(src, 0, src.length)];

           int ret = decode0(src, 0, src.length, dst);

           if (ret != dst.length) {

               dst = Arrays.copyOf(dst, ret);

           }

Base64Utils工具類介紹

Spring有一個工具類:Base64Utils,提供了base64的封裝,底層用的是java.utni.Base64類。

看下源碼,都是對編碼和解碼對象的封裝

/*

* Copyright 2002-2017 the original author or authors.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*      

https://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package org.springframework.util;

import java.nio.charset.Charset;

import java.nio.charset.StandardCharsets;

import java.util.Base64;

* A simple utility class for Base64 encoding and decoding.

* <p>Adapts to Java 8's {@link java.util.Base64} in a convenience fashion.

* @author Juergen Hoeller

* @author Gary Russell

* @since 4.1

* @see java.util.Base64

public abstract class Base64Utils {

private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

/**

 * Base64-encode the given byte array.

 * @param src the original byte array

 * @return the encoded byte array

 */

public static byte[] encode(byte[] src) {

 if (src.length == 0) {

  return src;

 }

 return Base64.getEncoder().encode(src);

}

 * Base64-decode the given byte array.

 * @param src the encoded byte array

 * @return the original byte array

public static byte[] decode(byte[] src) {

 return Base64.getDecoder().decode(src);

 * Base64-encode the given byte array using the RFC 4648

 * "URL and Filename Safe Alphabet".

 * @since 4.2.4

public static byte[] encodeUrlSafe(byte[] src) {

 return Base64.getUrlEncoder().encode(src);

 * Base64-decode the given byte array using the RFC 4648

public static byte[] decodeUrlSafe(byte[] src) {

 return Base64.getUrlDecoder().decode(src);

 * Base64-encode the given byte array to a String.

 * @param src the original byte array (may be {@code null})

 * @return the encoded byte array as a UTF-8 String

public static String encodeToString(byte[] src) {

  return "";

 return new String(encode(src), DEFAULT_CHARSET);

 * Base64-decode the given byte array from an UTF-8 String.

 * @param src the encoded UTF-8 String

public static byte[] decodeFromString(String src) {

 if (src.isEmpty()) {

  return new byte[0];

 return decode(src.getBytes(DEFAULT_CHARSET));

 * Base64-encode the given byte array to a String using the RFC 4648

public static String encodeToUrlSafeString(byte[] src) {

 return new String(encodeUrlSafe(src), DEFAULT_CHARSET);

 * Base64-decode the given byte array from an UTF-8 String using the RFC 4648

public static byte[] decodeFromUrlSafeString(String src) {

 return decodeUrlSafe(src.getBytes(DEFAULT_CHARSET));

特别需要注意的是 如果是對url進行編碼,要使用

Base64Utils.encodeToUrlSafeString,因為預設的字元集中“+和/”在Url中有特殊含義。

   @Test

   public void testURL() {

       String url = "http://wwww.baidu.com/dfdfdfdf/dfdf=w測試哦rdfdfwe1231j2s+dfadfdf";

       System.out.println(Base64Utils.encodeToUrlSafeString(url.getBytes()));

       System.out.println(Base64Utils.encodeToString(url.getBytes()));

————————————————

版權聲明:本文為CSDN部落客「明明如月學長」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。

https://blog.csdn.net/w605283073/article/details/89112566