Base64变种实现,如何实现Base64自定义编码
某些情況下,標準的Base64編碼可能無法滿足特殊的業(yè)務需求,此時我們往往希望通過最簡單的方式實現(xiàn)Base64的變種以滿足需求。所謂變種是指借鑒了Base64的思想,但是自定義了字符表。下文基于java作描述,所用到的數(shù)據(jù)類型或語法均基于java。
Base64實現(xiàn)思想:
Base64的編碼思想十分簡單。首先我們知道每字節(jié)的長度為8位,Base64將3個字節(jié)進行組合并拆分,分解為4個字節(jié)(高兩位補0),每個字節(jié)可表示的最大整數(shù)是63,因此可以對應一個擁有63個字符的編碼表,通過編碼表映射,最終得到Base64字符串。
Base64的組合拆分邏輯如下:
有字節(jié)(ByteA)、字節(jié)(ByteB)、字節(jié)(ByteC)
1.取ByteA的前6位,并在高位補上兩個0得到第一個編碼字節(jié)(EnByteA)
2.取ByteA的后2位、ByteB的前4位,并在高位補上兩個0得到第二個編碼字節(jié)(EnByteB)
3.取ByteB的后4位、ByteC的前2位,并在高位補上兩個0得到第三個編碼字節(jié)(EnByteC)
4.取ByteC的后6位,并在高位補上兩個0得到第四個編碼字節(jié)(EnByteD)
如:aaaaaaaa bbbbbbbb cccccccc
最終會被拆解為:00aaaaaa 00aabbbb 00bbbbcc 00cccccc
在JAVA中表示如下:
EnByteA = ByteA >> 2 &0x3f
EnByteB = (((ByteA & 0x3) << 4) | (ByteB >> 4))) & 0x3f
EnByteC = (((ByteB & 0xf) << 2) | (ByteC >> 6)) & 0x3f
EnByteD = ByteC & 0x3f
其中:
0x3f:00111111
0x3:00000011
0xf:00001111
通過上文描述,可知,Base64將3個字節(jié)作為一組進行編碼,當字節(jié)數(shù)不是3的倍數(shù)時,將會出現(xiàn)剩余字節(jié)不夠組成一組的情況,Base64仍然會用以上編碼規(guī)則對剩余的字節(jié)進行編碼,但會采用填充字符的方式表達缺少的字節(jié),如缺少一個字節(jié)才夠一組,則在末尾補上一個“=”,缺少兩個字節(jié)則補上“==”。
對剩余字節(jié)的處理:
剩余一個字節(jié)時:
EnByteA = ByteA >> 2 &0x3f
EnByteB = (((ByteA & 0x3) << 4)
EnByteC = 填充字符在編碼表中的下標
EnByteD = 填充字符在編碼表中的下標
剩余兩個字節(jié)時:
EnByteA = ByteA >> 2 &0x3f
EnByteB = (((ByteA & 0x3) << 4) | (ByteB >> 4))) & 0x3f
EnByteC = (((ByteB & 0xf) << 2)
EnByteD = 填充字符在編碼表中的下標
Base64對剩余字節(jié)的處理完整地保留了字節(jié)信息,但是會導致編碼可變。如何理解可變?假如剩余一個字節(jié),則EnByteB實際上是來記錄ByteA 的低2位的,因此EnByteB的低6位在解碼中會被忽略,由此EnByteB的低6位是任意值都能解碼到ByteA ,因此通過這種思想實現(xiàn)的編碼容易被攻擊者通過篡改最后一個字符的方式猜測到編碼規(guī)則。
自定義編碼表
在標準的Base64編碼中,編碼表由[a-zA-Z0-9]組成。為滿足我們特殊的需求,我們可以自定義自己的編碼表,只要有63個符號表示即可,并且對于填充“=”我們也可用自己的方式去實現(xiàn)。
舉個例子:
定義編碼表如下
定義解碼表如下
private static final byte[] Base64Reversal = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, 63, 0, 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, -1, -1, -1, -1, -1, -1, 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}解碼表可以通過程序自動生成
其中的解碼表數(shù)組(reversal)定義了長度為123,是因為在編碼表中,ascii值最大是122,則數(shù)組長度最小為123
這樣定義的解碼表,在解碼邏輯中可以直接通過Base64Reversal[char]的方式得到byte。
最終實現(xiàn):
package com.kingsoft.passport.util;import java.nio.charset.Charset;/*** base64* @author yuzhanchao**/ public class Base64 {private static final Charset DEAFAULT_CHARSET = Charset.forName("utf-8");private static final char[] Base64Alphabet = { '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', '#', '@' };private static final byte[] Base64Reversal = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, 62, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, 63, 0, 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, -1, -1, -1, -1, -1, -1, 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 };private static final char Bit4Symbol = '-';private static final char Bit8Symbol = '_';public static String encode(String str) {byte[] bytes = str.getBytes(DEAFAULT_CHARSET);int sourceLength = bytes.length;int groupLength = sourceLength / 3;int remainder = sourceLength % 3;int resultLength = groupLength * 4;if (remainder == 1) {resultLength += 3;} else if (remainder == 2) {resultLength += 4;}char[] alphabet = Base64Alphabet;char[] result = new char[resultLength];int sourceCursor = 0;int targetCursor = 0;for (int i = 0; i < groupLength; i++) {int byte1 = bytes[sourceCursor++] & 0xff;int byte2 = bytes[sourceCursor++] & 0xff;int byte3 = bytes[sourceCursor++] & 0xff;result[targetCursor++] = alphabet[(byte1 >> 2 & 0x3f)];result[targetCursor++] = alphabet[((((byte1 & 0x3) << 4) | (byte2 >> 4))) & 0x3f];result[targetCursor++] = alphabet[((((byte2 & 0xf) << 2) | (byte3 >> 6))) & 0x3f];result[targetCursor++] = alphabet[byte3 & 0x3f];}if (remainder > 0) {byte byte1 = bytes[sourceCursor++];result[targetCursor++] = alphabet[byte1 >> 2 & 0x3f];if (remainder == 1) {result[targetCursor++] = alphabet[((byte1 & 0x3) << 4) & 0x3f];result[targetCursor++] = Bit8Symbol;} else {byte byte2 = bytes[sourceCursor++];result[targetCursor++] = alphabet[(((byte1 & 0x3) << 4) | (byte2 >> 4)) & 0x3f];result[targetCursor++] = alphabet[((byte2 & 0xf) << 2)];result[targetCursor++] = Bit4Symbol;}}return new String(result);}public static String decode(String str) {char[] source = str.toCharArray();int strLength = source.length;int groupLength = 0;int remainder = 0;char last = source[strLength - 1];if (last == Bit8Symbol) {groupLength = (strLength - 3) / 4;remainder = 1;} else if (last == Bit4Symbol) {groupLength = strLength / 4 - 1;remainder = 2;} else {groupLength = strLength / 4;}byte[] reversal = Base64Reversal;byte[] result = new byte[groupLength * 3 + remainder];int sourceCursor = 0;int targetCursor = 0;for (int i = 0; i < groupLength; i++) {byte sbyte1 = reversal[source[sourceCursor++]];byte sbyte2 = reversal[source[sourceCursor++]];byte sbyte3 = reversal[source[sourceCursor++]];byte sbyte4 = reversal[source[sourceCursor++]];result[targetCursor++] = (byte) ((sbyte1 << 2) | (sbyte2 >> 4));result[targetCursor++] = (byte) ((sbyte2 << 4) | (sbyte3 >> 2));result[targetCursor++] = (byte) (((sbyte3 & 0x3) << 6) | sbyte4);}if (remainder > 0) {byte sbyte1 = reversal[source[sourceCursor++]];byte sbyte2 = reversal[source[sourceCursor++]];result[targetCursor++] = (byte) ((sbyte1 << 2) | (sbyte2 >> 4));if (remainder == 2) {byte sbyte3 = reversal[source[sourceCursor++]];result[targetCursor++] = (byte) ((sbyte2 << 4) | (sbyte3 >> 2));}}return new String(result, DEAFAULT_CHARSET);}}總結
以上是生活随笔為你收集整理的Base64变种实现,如何实现Base64自定义编码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【正点原子Linux连载】第三十三章 U
- 下一篇: Unity2020使用Steam VR开