在程序开发中,我们会经常使用Base64对字符串或二进制数据进行编码解码,虽然字符串在经过Base64编码后看上去很像被加密过,但是并不能成为加密解密算法,原因是Base64的编码解码过程非常的简单,且编码解码的流程的算法是公开的,起不到任何加密的效果。Base64的设计目的也并不是为了加密。
所谓Base64,就是说选出64个字符—-小写字母a-z、大写字母A-Z、数字0-9、符号”+”、”/”(再加上作为垫字的”=”,实际上是65个字符)—-作为一个基本字符集。然后,其他所有符号都转换成这个字符集中的字符。具体来说,转换方式可以分为四步。
- 将每三个字节作为一组,一共是24个二进制位。
- 将这24个二进制位分为四组,每个组有6个二进制位。
- 在每组前面加两个00,扩展成32个二进制位,即四个字节。
- 根据下表,得到扩展后的每个字节的对应符号,这就是Base64的编码值。
base64编码表
0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2a 0x2b 0x2c 0x2d 0x2e 0x2f 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3a 0x3b 0x3c 0x3d 0x3e 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 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 + /
因为,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 methode 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); } }