!文章内容如有错误或排版问题,请提交反馈,非常感谢!
在程序开发中,我们会经常使用Base64对字符串或二进制数据进行编码解码,虽然字符串在经过Base64编码后看上去很像被加密过,但是并不能成为加密解密算法,原因是Base64的编码解码过程非常的简单,且编码解码的流程的算法是公开的,起不到任何加密的效果。Base64的设计目的也并不是为了加密。
所谓Base64,就是说选出64个字符—-小写字母a-z、大写字母A-Z、数字0-9、符号”+”、”/”(再加上作为垫字的”=”,实际上是65个字符)—-作为一个基本字符集。然后,其他所有符号都转换成这个字符集中的字符。具体来说,转换方式可以分为四步。
- 将每三个字节作为一组,一共是24个二进制位。
- 将这24个二进制位分为四组,每个组有6个二进制位。
- 在每组前面加两个00,扩展成32个二进制位,即四个字节。
- 根据下表,得到扩展后的每个字节的对应符号,这就是Base64的编码值。
base64编码表
0x00 0x10 0x20 0x30 0x40 0x50 0x60 0x70 0x80 0x90 0xa0 0xb0 0xc0 0xd0 0xe0 0xf0 0x100 0x110 0x120 0x130 0x140 0x150 0x160 0x170 0x180 0x190 0x1a0 0x1b0 0x1c0 0x1d0 0x1e0 0x1f0 0x200 0x210 0x220 0x230 0x240 0x250 0x260 0x270 0x280 0x290 0x2a0 0x2b0 0x2c0 0x2d0 0x2e0 0x2f0 0x300 0x310 0x320 0x330 0x340 0x350 0x360 0x370 0x380 0x390 0x3a0 0x3b0 0x3c0 0x3d0 0x3e0 0x3f 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 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 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
因为,Base64将三个字节转化成四个字节,因此Base64编码后的文本,会比原文本大出三分之一左右。
从上面的编码表中,我们可以看到采用公共的Base64并不能其实简单的加密和解密,但可以通过适当的修改Base64来实现简单加密与解密。尽管不及专门的对称加密和非对称加密的安全性,但性能远胜于专门的加密解密过程,且可以实现可见字符的传输。适用于安全要求不高,对密文要求可见,且密文长度受限的场景。
Base64x的原理非常的简单,就是将编码表的顺序进行打乱,再进行编码,具体代码如下:
public class Base64x { private static final String DEFAULT_ENCODING_TABLES = "ABCDEFGHIUVWXYZ+abcdefJKLMNOPQRSTghijk016789/lmnopqrs2345tuvwxyz"; private String encodingTables; public Base64x(int offset) { this.encodingTables = ""; final int move = offset % DEFAULT_ENCODING_TABLES.length(); this.encodingTables = DEFAULT_ENCODING_TABLES.substring(move) + DEFAULT_ENCODING_TABLES.substring(0, move); } public Base64x(String s, int offset) { this.encodingTables = ""; final int move = offset % s.length(); this.encodingTables = s.substring(move) + s.substring(0, move); } /** * encode * * coverts a byte array to a string populated with base64 digits. It steps * through the byte array calling a helper method for each block of three * input bytes * * @param raw * The byte array to encode * @return A string in base64 encoding */ public String encode(byte[] raw) { StringBuffer encoded = new StringBuffer(); for (int i = 0; i < raw.length; i += 3) { encoded.append(encodeBlock(raw, i)); } return encoded.toString(); } /** * encodeBlock * * creates 4 base64 digits from three bytes of input data. we use an * integer, block, to hold the 24 bits of input data. * * @return An array of 4 characters */ protected char[] encodeBlock(byte[] raw, int offset) { int block = 0; // how much space left in input byte array int slack = raw.length - offset - 1; // if there are fewer than 3 bytes in this block, calculate end int end = (slack >= 2) ? 2 : slack; // convert signed quantities into unsigned for (int i = 0; i <= end; i++) { byte b = raw[offset + i]; int neuter = (b < 0) ? b + 256 : b; block += neuter << (8 * (2 - i)); } // extract the base64 digets, which are six bit quantities. char[] base64 = new char[4]; for (int i = 0; i < 4; i++) { int sixbit = (block >>> (6 * (3 - i))) & 0x3f; base64[i] = getChar(sixbit); } // pad return block if needed if (slack < 1) base64[2] = '='; if (slack < 2) base64[3] = '='; // always returns an array of 4 characters return base64; } /** * decode * * convert a base64 string into an array of bytes. * * @param base64 * A String of base64 digits to decode. * @return A byte array containing the decoded value of the base64 input * string */ public byte[] decode(String base64) { // how many padding digits? int pad = 0; for (int i = base64.length() - 1; base64.charAt(i) == '='; i--) { pad++; } // we know know the lenght of the target byte array. int length = base64.length() * 6 / 8 - pad; byte[] raw = new byte[length]; int rawIndex = 0; // loop through the base64 value. A correctly formed // base64 string always has a multiple of 4 characters. for (int i = 0; i < base64.length(); i += 4) { int block = (getValue(base64.charAt(i)) << 18) + (getValue(base64.charAt(i + 1)) << 12) + (getValue(base64.charAt(i + 2)) << 6) + (getValue(base64.charAt(i + 3))); // based on the block, the byte array is filled with the // appropriate 8 bit values for (int j = 0; j < 3 && rawIndex + j < raw.length; j++) raw[rawIndex + j] = (byte)((block >> (8 * (2 - j))) & 0xff); rawIndex += 3; } return raw; } /** * getChar * * encapsulates the translation from six bit quantity to base64 digit */ protected char getChar(int sixBit) { return encodingTables.charAt(sixBit); } /** * getValue * * translates from base64 digits to their 6 bit value */ protected int getValue(char c) { if (c == '=') return 0; return encodingTables.indexOf(c); } }