[VB.net][VB6][VBA]客製化 Base64 編碼/解碼 的技巧(一)數底的轉換
預計這篇文章會比較長,所以貼文先命名為(一)
先認識 Base64(維基百科)
Base64 不算是「加密」和「解密」的技術,它其實是「數底轉換」的一種情境,把數據用 64 進制來表達就是 Base64 編碼的內涵。
在數字系統中我們是用「視覺符號」來圖象化數學上抽象的「量」,所用的邏輯基礎是「進位原則」,而顯現的結果則是連續排列的符號,並且用這組「連續排列的符號」做為傳達「量」的方式。
寫一個多用途的數底轉換函式,用實例說明一下。
若有一個計物量是「三萬二千七百六十七」用各種不同條件做數底轉換看看會是什麼情形:
Imports System.Text Public Class Form1 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Demo(32767, "0,1,2,3,4,5,6,7,8,9") Demo(32767, "◎,○,●,←,→,?,┼,※,€,《") Demo(32767, "0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F") Demo(32767, "0,1") Demo(32767, "0,1,2,3,4,5,6,7") Demo(32767, "0,1,2,3,4") Demo(32767, "○,●,↑,↓") End Sub Sub Demo(ByVal v As Long, ByVal s As String) Dim 進位底數 = Split(s, ",").Length Dim 圖像 As String = 數值_to_視覺圖像(v, s) Dim 數值 As Long = 視覺圖像_to_數值(圖像, s) Debug.WriteLine(String.Format("十進制的 {0} 用[{1}]符號以 '{2} 進制' 呈現的圖像是:", v, s, 進位底數) & 圖像) Debug.WriteLine(String.Format("用[{0}]符號組成的 {1} 進數 '{2}' 代表了十進制的 ", s, 進位底數, 圖像) & v) Debug.WriteLine("") End Sub '---將數值符號串轉換為 10 進數--- Function 視覺圖像_to_數值(ByVal 符號 As String, ByVal 使用的符號 As String) As Long Dim 符號集合 As List(Of String) = Split(使用的符號, ",").ToList Dim 數字陣列 = Split(符號, " ") Dim 進位底數 = 符號集合.Count Dim 位元數 As Integer = UBound(數字陣列) Dim tmp As Long = 0 For i = 0 To 位元數 tmp += 符號集合.IndexOf(數字陣列(位元數 - i)) * (進位底數 ^ i) Next Return tmp End Function '---將 10 進數轉為用指定進位方式的符號串表示--- Function 數值_to_視覺圖像(ByVal 數值 As Long, ByVal 使用的符號 As String) As String Dim 符號陣列 As List(Of String) = Split(使用的符號, ",").ToList Dim 進位底數 = 符號陣列.Count Dim tmp As String = "" Do tmp = 符號陣列(數值 Mod 進位底數) & " " & tmp 數值 = 數值 \ 進位底數 Loop While 數值 > 0 Return Trim(tmp) End Function End Class
輸出結果:
十進制的 32767 用[0,1,2,3,4,5,6,7,8,9]符號以 '10 進制' 呈現的圖像是:3 2 7 6 7
用[0,1,2,3,4,5,6,7,8,9]符號組成的 10 進數 '3 2 7 6 7' 代表了十進制的 32767十進制的 32767 用[◎,○,●,←,→,?,┼,※,€,《]符號以 '10 進制' 呈現的圖像是:← ● ※ ┼ ※
用[◎,○,●,←,→,?,┼,※,€,《]符號組成的 10 進數 '← ● ※ ┼ ※' 代表了十進制的 32767十進制的 32767 用[0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F]符號以 '16 進制' 呈現的圖像是:7 F F F
用[0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F]符號組成的 16 進數 '7 F F F' 代表了十進制的 32767十進制的 32767 用[0,1]符號以 '2 進制' 呈現的圖像是:1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
用[0,1]符號組成的 2 進數 '1 1 1 1 1 1 1 1 1 1 1 1 1 1 1' 代表了十進制的 32767十進制的 32767 用[0,1,2,3,4,5,6,7]符號以 '8 進制' 呈現的圖像是:7 7 7 7 7
用[0,1,2,3,4,5,6,7]符號組成的 8 進數 '7 7 7 7 7' 代表了十進制的 32767十進制的 32767 用[0,1,2,3,4]符號以 '5 進制' 呈現的圖像是:2 0 2 2 0 3 2
用[0,1,2,3,4]符號組成的 5 進數 '2 0 2 2 0 3 2' 代表了十進制的 32767十進制的 32767 用[○,●,↑,↓]符號以 '4 進制' 呈現的圖像是:● ↓ ↓ ↓ ↓ ↓ ↓ ↓
用[○,●,↑,↓]符號組成的 4 進數 '● ↓ ↓ ↓ ↓ ↓ ↓ ↓' 代表了十進制的 32767
從上面例子可看出:
- 同一個數據 32767 用不同的進位制來表示,會有不同的外觀。
- 採用的進位底數越大,輸出的位元長度越小。
- 符號不一定要用印度阿拉伯數字,用 ◎§←→※ 也是可以,因為它們也是符號。
- 數底轉換是可逆的,因此用來做「編碼/解碼」是可行的。
那麼 Base64 是什麼呢?這回我們以字串為例加以說明。
- 把字串編碼為數字,放到 Byte() 陣列。
- Byte 陣列可以視為 256 進位表示法,每個 Byte 都是一個符號。(它用了 00、01、.........、FF 共 256 個不同的符號)
- 用 A-Z、a-z、0-9 及 +/ 共64個符號建立 64 進位系統。
- 依序取出陣列的3個字節轉換為 64 進位制的4個字節。
- 來源陣列長度若不為3的倍數就補 0。
- 目的字串長度若不為4的倍數就補 =。
實作看一下:
把「大家好」三個字用 Base64 編碼和解碼。
仍用剛才的函式,只是叫用前先處理一下:'---建立所使用的符號集--- Dim B1 = "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," Dim B2 = "0,1,2,3,4,5,6,7,8,9,+,/" Dim strBase64 = B1 & B1.ToLower & B2 '---把 '大家好' 編碼為 Uncode 數據--- Dim code As New UnicodeEncoding Dim src() = code.GetBytes("大家好") '---用 256 進位制(16進制的二個符號一組)來表示--- Dim b = New StringBuilder For i = 0 To src.Length - 1 b.Append(String.Format("{0:x2}", src(i))) Next Demo("&h" & b.ToString, strBase64)
輸出結果:
十進制的 43266265021785 用[A,B,…(略)…7,8,9,+,/]符號以 '64 進制' 呈現的圖像是:J 1 m 2 W 3 1 Z
用[A,B,…(略)…7,8,9,+,/]符號組成的 64 進數 'J 1 m 2 W 3 1 Z' 代表了十進制的 43266265021785用工具驗證一下:
也可以用 System.Convert 類別的ToBase64String() 同樣可以得到 ‘J1m2W31Z’的結果。
心得結語:
- 經過 Base64 編碼後的字串看起來的確有「加密」效果,但實則不然,因為那 64 個符號排列的順序是公開的(見 RFC1421 http://tools.ietf.org/html/rfc1421)。
- Base64 編碼後的長度是可以明確估算的 = ((Bytes.Lenth-1)\3+1)*4
- 如果改變了符號集的字元順序再用同樣手法進行 base64 編碼,那就有加密效果了。
- 如要真正的加密編碼,光是改變進位制是不夠的,還要:
- 利用亂數 Key 和原始 Byte() 陣列的每個 Byte 做位元運算產生新值;解碼時再反向操作得到原始數據。
- 要有可以一對多的性質,即便是完全相同的原始資料,也要能每次都編碼出不同的結果,增加破解的難度。
- 要有混淆功能,即便只是更動了一篇文章中的一個字元,再編碼的結果也要有全新的風貌。
- 本文暫打住,後續再把這些功能加上去。