JavaScript base64

加密標準的JavaScript庫https://github.com/brix/crypto-js

簡介

標準的Base64並不適合直接放在URL裏傳輸,因爲URL編碼器會把標準Base64中的“/”和“+”字符變爲形如“%XX”的形式,而這些“%”號在存入數據庫時還需要再進行轉換,因爲ANSI SQL中已將“%”號用作通配符

爲解決此問題,可採用一種用於URL的改進Base64編碼,它在末尾填充'='號,並將標準Base64中的“+”和“/”分別改成了“-”和“_”,這樣就免去了在URL編解碼和數據庫存儲時所要作的轉換,避免了編碼信息長度在此過程中的增加,並統一了數據庫、表單等處對象標識符的格式。

另有一種用於正則表達式的改進Base64變種,它將“+”和“/”改成了“!”和“-”,因爲“+”,“*”以及前面在IRCu中用到的“[”和“]”在正則表達式中都可能具有特殊含義。

此外還有一些變種,它們將“+/”改爲“_-”或“._”(用作編程語言中的標識符名稱)或“.-”(用於XML中的Nmtoken)甚至“_:”(用於XML中的Name)。

Base64要求把每三個8Bit的字節轉換爲四個6Bit的字節(3*8 = 4*6 = 24),然後把6Bit再添兩位高位0,組成四個8Bit的字節,也就是說,轉換後的字符串理論上將要比原來的長1/3。

規則

關於這個編碼的規則:

①.把3個字節變成4個字節。

②每76個字符加一個換行符。

③.最後的結束符也要處理。

例子1

轉換前 11111111, 11111111, 11111111 (二進制)

轉換後 00111111, 00111111, 00111111, 00111111 (二進制)

上面的三個字節是原文,下面的四個字節是轉換後的Base64編碼,其前兩位均爲0。

轉換後,我們用一個碼錶來得到我們想要的字符串(也就是最終的Base64編碼),這個表是這樣的:(摘自RFC2045)

轉換表

Table 1: The Base64 Alphabet

索引

對應字符

索引

對應字符

索引

對應字符

索引

對應字符

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

   

例子2

轉換前 10101101,10111010,01110110

轉換後 00101011, 00011011 ,00101001 ,00110110

十進制 43 27 41 54

對應碼錶中的值 r b p 2

所以上面的24位編碼,編碼後的Base64值爲 rbp2

解碼同理,把 rbq2 的二進制位連接上再重組得到三個8位值,得出原碼。

(解碼只是編碼的逆過程,有關MIME的RFC還有很多,如果需要詳細情況請自行查找。)

第一個字節,根據源字節的第一個字節處理。

規則:源第一字節右移兩位,去掉低2位,高2位補零。

既:00 + 高6位

第二個字節,根據源字節的第一個字節和第二個字節聯合處理。

規則如下,第一個字節高6位去掉然後左移四位,第二個字節右移四位

即:源第一字節低2位 + 源第2字節高4位

第三個字節,根據源字節的第二個字節和第三個字節聯合處理,

規則第二個字節去掉高4位並左移兩位(得高6位),第三個字節右移6位並去掉高6位(得低2位),相加即可

第四個字節,規則,源第三字節去掉高2位即可

//用更接近於編程的思維來說,編碼的過程是這樣的:

//第一個字符通過右移2位獲得第一個目標字符的Base64表位置,根據這個數值取到表上相應的字符,就是第一//個目標字符。

//然後將第一個字符與0x03(00000011)進行與(&)操作並左移4位,接着第二個字符右移4位與前者相或(|),即獲得第二個目標字符。

//再將第二個字符與0x0f(00001111)進行與(&)操作並左移2位,接着第三個字符右移6位與前者相或(|),獲得第三個目標字符。

//最後將第三個字符與0x3f(00111111)進行與(&)操作即獲得第四個目標字符。

//在以上的每一個步驟之後,再把結果與 0x3F 進行 AND 位操作,就可以得到編碼後的字符了。

原文的字節數量應該是3的倍數,如果這個條件不能滿足的話,具體的解決辦法是這樣的:原文剩餘的字節根據編碼規則繼續單獨轉(1變2,2變3;不夠的位數用0補全),再用=號補滿4個字節。這就是爲什麼有些Base64編碼會以一個或兩個等號結束的原因,但等號最多隻有兩個。因爲一個原字節至少會變成兩個目標字節,所以餘數任何情況下都只可能是0,1,2這三個數中的一個。如果餘數是0的話,就表示原文字節數正好是3的倍數(最理想的情況)。如果是1的話,轉成2個Base64編碼字符,爲了讓Base64編碼是4的倍數,就要補2個等號;同理,如果是2的話,就要補1個等號。

原理

轉碼過程例子:

3*8=4*6

內存1個字節佔8位

轉前: s 1 3

先轉成ascii:對應 115 49 51

2進制: 01110011 00110001 00110011

6個一組(4組) 011100110011000100110011

然後纔有後面的 011100 110011 000100 110011

然後計算機一個字節佔8位,不夠就自動補兩個高位0了

所以有了高位補0

科學計算器輸入 00011100 00110011 00000100 00110011

得到 28 51 4 51

查對下照表 c z E z


  先以“迅雷下載”爲例: 很多下載類網站都提供“迅雷下載”的鏈接,其地址通常是加密的迅雷專用下載地址。

其實迅雷的“專用地址”也是用Base64"加密"的,其過程如下:

一、在地址的前後分別添加AA和ZZ

二、對新的字符串進行Base64編碼

應用

Base64編碼可用於在HTTP環境下傳遞較長的標識信息。例如,在Java Persistence系統Hibernate中,就採用了Base64來將一個較長的一個標識符(一般爲128-bit的UUID)編碼爲一個字符串,用作HTTP表單和HTTP GET URL中的參數。在其他應用程序中,也常常需要把二進制數據編碼爲適合放在URL(包括隱藏表單域)中的形式。此時,採用Base64編碼不僅比較簡短,同時也具有不可讀性,即所編碼的數據不會被人用肉眼所直接看到。

然而,標準的Base64並不適合直接放在URL裏傳輸,因爲URL編碼器會把標準Base64中的“/”和“+”字符變爲形如“%XX”的形式,而這些“%”號在存入數據庫時還需要再進行轉換,因爲ANSI SQL中已將“%”號用作通配符。

爲解決此問題,可採用一種用於URL的改進Base64編碼,它不僅在末尾去掉填充的'='號,並將標準Base64中的“+”和“/”分別改成了“-”和“_”,這樣就免去了在URL編解碼和數據庫存儲時所要作的轉換,避免了編碼信息長度在此過程中的增加,並統一了數據庫、表單等處對象標識符的格式。

另有一種用於正則表達式的改進Base64變種,它將“+”和“/”改成了“!”和“-”,因爲“+”,“/”以及前面在IRCu中用到的“[”和“]”在正則表達式中都可能具有特殊含義。

此外還有一些變種,它們將“+/”改爲“_-”或“._”(用作編程語言中的標識符名稱)或“.-”(用於XML中的Nmtoken)甚至“_:”(用於XML中的Name)。

其他應用

Mozilla Thunderbird和Evolution用Base64來保密電子郵件密碼

Base64 也會經常用作一個簡單的“加密”來保護某些數據,而真正的加密通常都比較繁瑣。

垃圾訊息傳播者用Base64來避過反垃圾郵件工具,因爲那些工具通常都不會翻譯Base64的訊息。

在LDIF檔案,Base64用作編碼字串。

代碼實現

JavaScript版

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

if (!Shotgun)

    var Shotgun = {};

if (!Shotgun.Js)

    Shotgun.Js = {};

Shotgun.Js.Base64 = {

    _table: [

        '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''+''/'

    ],

  

    encode: function (bin) {

        var codes = [];

        var un = 0;

        un = bin.length % 3;

        if (un == 1)

            bin.push(0, 0);

        else if (un == 2)

            bin.push(0);

        for (var i = 2; i < bin.length; i += 3) {

            var c = bin[i - 2] << 16;

            c |= bin[i - 1] << 8;

            c |= bin[i];

            codes.push(this._table[c >> 18 & 0x3f]);

            codes.push(this._table[c >> 12 & 0x3f]);

            codes.push(this._table[c >> 6 & 0x3f]);

            codes.push(this._table[c & 0x3f]);

        }

        if (un >= 1) {

            codes[codes.length - 1] = "=";

            bin.pop();

        }

        if (un == 1) {

            codes[codes.length - 2] = "=";

            bin.pop();

        }

        return codes.join("");

    },

    decode: function (base64Str) {

        var i = 0;

        var bin = [];

        var x = 0, code = 0, eq = 0;

        while (i < base64Str.length) {

            var c = base64Str.charAt(i++);

            var idx = this._table.indexOf(c);

            if (idx == -1) {

                switch (c) {

                    case '=': idx = 0; eq++; break;

                    case ' ':

                    case '\n':

                    case "\r":

                    case '\t':

                        continue;

                    default:

                        throw "message""\u0062\u0061\u0073\u0065\u0036\u0034\u002E\u0074\u0068\u0065\u002D\u0078\u002E\u0063\u006E\u0020\u0045\u0072\u0072\u006F\u0072\u003A\u65E0\u6548\u7F16\u7801\uFF1A" + c };

                }

            }

            if (eq > 0 && idx != 0)

                throw "message""\u0062\u0061\u0073\u0065\u0036\u0034\u002E\u0074\u0068\u0065\u002D\u0078\u002E\u0063\u006E\u0020\u0045\u0072\u0072\u006F\u0072\u003A\u7F16\u7801\u683C\u5F0F\u9519\u8BEF\uFF01" };

  

            code = code << 6 | idx;

            if (++x != 4)

                continue;

            bin.push(code >> 16);

            bin.push(code >> 8 & 0xff);

            bin.push(code & 0xff)

            code = x = 0;

        }

        if (code != 0)

            throw "message""\u0062\u0061\u0073\u0065\u0036\u0034\u002E\u0074\u0068\u0065\u002D\u0078\u002E\u0063\u006E\u0020\u0045\u0072\u0072\u006F\u0072\u003A\u7F16\u7801\u6570\u636E\u957F\u5EA6\u9519\u8BEF" };

        if (eq == 1)

            bin.pop();

        else if (eq == 2) {

            bin.pop();

            bin.pop();

        else if (eq > 2)

            throw "message""\u0062\u0061\u0073\u0065\u0036\u0034\u002E\u0074\u0068\u0065\u002D\u0078\u002E\u0063\u006E\u0020\u0045\u0072\u0072\u006F\u0072\u003A\u7F16\u7801\u683C\u5F0F\u9519\u8BEF\uFF01" };

  

        return bin;

    }

};

在MIME格式的電子郵件中,base64可以用來將binary的字節序列數據編碼成ASCII字符序列構成的文本。使用時,在傳輸編碼方式中指定base64。使用的字符包括大小寫字母各26個,加上10個數字,和加號“+”,斜槓“/”,一共64個字符,等號“=”用來作爲後綴用途。

完整的base64定義可見 RFC1421和 RFC2045。編碼後的數據比原始數據略長,爲原來的4/3。在電子郵件中,根據RFC822規定,每76個字符,還需要加上一個回車換行。可以估算編碼後數據長度大約爲原長的135.1%。

轉換的時候,將三個byte的數據,先後放入一個24bit的緩衝區中,先來的byte佔高位。數據不足3byte的話,於緩衝區中剩下的Bit用0補足。然後,每次取出6個bit,按照其值選擇ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符作爲編碼後的輸出。不斷進行,直到全部輸入數據轉換完成。

如果最後剩下兩個輸入數據,在編碼結果後加1個“=”;如果最後剩下一個輸入數據,編碼結果後加2個“=”;如果沒有剩下任何數據,就什麼都不要加,這樣纔可以保證資料還原的正確性。

舉例來說,一段引用自Thomas Hobbes's Leviathan的文句:

Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.

經過base64編碼之後變成:

TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz

IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg

dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu

dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo

ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章