编程学习资讯网

BASE64编码解码--编程学习网

发布时间:2017-11-29 08:44:29   来源:本站编辑   浏览次数:

1 Base64编码概述

         Base64是一种编码方式,这个术语最初是在“MIME内容传输编码规范”中提出的。Base64不是一种加密算法,它实际上是一种“二进制转换到文本”的编码方式,它能够将任意二进制数据转换为ASCII字符串的形式,以便在只支持文本的环境中也能够顺利地传输二进制数据。

(1)base64编码:把二进制数据转为字符

(2)base64解码:把字符转为二进制数据

2 Base64编码由来

因为有些网络传输渠道并不支持所有字节,例如传统的邮件只支持可见字符的传输,像ASCII码的控制字符(ASCII码包含了 128 个字符。其中前 32 个, 0-31 ,即 0x00-0x1F ,都是不可见字符。这些字符,就叫做控制字符。)就不能通过邮件传输。另外例如图片二进制流的每个字节不可能全部都是可见字符,所以就传送不了。

最好的方法就是在不改变传统协议的情况下,做一种扩展方案来支持二进制文件的传送,把不可能打印的字符用可打印的字符标识,问题就解决了。Base64编码就应运而生,Base64就是一种基于64个可打印字符来表示二进制数据的表示方法。

3 Base64编码原理

如下图Base64编码索引表,字符选用了“A-Z 、 a-z 、 0-9、+、 / ”64个可打印字符。数字代表字符索引,这个是标准Base64标准协议规定的,不能更改。64个字节用6个bit位就可以全部表示(32+16+8+4+2+1)就可以全部表示。这里注意一个Base64字符是8个bit,但有效部分只有右边6个bit,左边两个永远是0。

 

那么怎么用6个有效bit来表示传统字符的8个bit呢?8和6的最小公倍数是24,也就是说3个传统字节可以由4个Base64字符来表示,保证有效位数是一样的,这样就多了1/3的字节数来弥补Base64只有6个有效bit的不足。你也可以说用两个Base64字符也能表示一个传统字符,但是采用最小公倍数的方案其实是最减少浪费的。结合下边的图比较容易理解。Man是三个字符,一共24个有效bit,只好用4个Base64字符来凑齐24个有效位。红框表示的是对应的Base64,6个有效位转化成相应的索引值再对应Base64字符表,查出"Man"对应的Base64字符是"TWFU"。说到这里有个原则不知道你发现了没有,要转换成Base64的最小单位就是三个字节,对一个字符串来说每次都是三个字节三个字节的转换,对应的是Base64的四个字节。这个搞清楚了其实就差不多了。

但是转换到最后你发现不够三个字节了怎么办呢?愿望终于实现了,我们可以用两个Base64来表示一个字符或用三个Base64表示两个字符,像下图的A对应的第二个Base64的二进制位只有两个,把后边的四个补0就是了。所以A对应的Base64字符就是QQ。上边已经说过了,原则是Base64字符的最小单位是四个字符一组,那这才两个字符,后边补两个"="吧。其实不用"="也不耽误解码,之所以用"=",可能是考虑到多段编码后的Base64字符串拼起来也不会引起混淆。由此可见Base64字符串只可能最后出现一个或两个"=",中间是不可能出现"="的。下图中字符"BC"的编码过程也是一样的。

4 java代码实现

复制代码
 1 package xin.dreaming.base64;  2  3 import java.io.UnsupportedEncodingException;  4  5  6 public class Base64 {  7 static private final int BASELENGTH = 255;  8 static private final int LOOKUPLENGTH = 64;  9 static private final int TWENTYFOURBITGROUP = 24;  10 static private final int EIGHTBIT = 8;  11 static private final int SIXTEENBIT = 16;  12 static private final int SIXBIT = 6;  13 static private final int FOURBYTE = 4;  14 static private final int SIGN = -128;  15 static private final byte PAD = (byte) '=';  16 static private byte[] base64Alphabet = new byte[BASELENGTH];  17 static private byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];  18 //static private final Log log = LogSource.getInstance("org.apache.commons.util.Base64");  19  20 static {  21 for (int i = 0; i < BASELENGTH; i++) {  22 base64Alphabet[i] = -1;  23  }  24 for (int i = 'Z'; i >= 'A'; i--) {  25 base64Alphabet[i] = (byte) (i - 'A');  26  }  27 for (int i = 'z'; i >= 'a'; i--) {  28 base64Alphabet[i] = (byte) (i - 'a' + 26);  29  }  30 for (int i = '9'; i >= '0'; i--) {  31 base64Alphabet[i] = (byte) (i - '0' + 52);  32  }  33  34 base64Alphabet['+'] = 62;  35 base64Alphabet['/'] = 63;  36  37 for (int i = 0; i <= 25; i++)  38 lookUpBase64Alphabet[i] = (byte) ('A' + i);  39  40 for (int i = 26, j = 0; i <= 51; i++, j++)  41 lookUpBase64Alphabet[i] = (byte) ('a' + j);  42  43 for (int i = 52, j = 0; i <= 61; i++, j++)  44 lookUpBase64Alphabet[i] = (byte) ('0' + j);  45  46 lookUpBase64Alphabet[62] = (byte) '+';  47 lookUpBase64Alphabet[63] = (byte) '/';  48  }  49  50 public static boolean isBase64(String isValidString) {  51 return isArrayByteBase64(isValidString.getBytes());  52  }  53  54 public static boolean isBase64(byte octect) {  55 //shall we ignore white space? JEFF??  56 return (octect == PAD || base64Alphabet[octect] != -1);  57  }  58  59 public static boolean isArrayByteBase64(byte[] arrayOctect) {  60 int length = arrayOctect.length;  61 if (length == 0) {  62 // shouldn't a 0 length array be valid base64 data?  63 // return false;  64 return true;  65  }  66 for (int i = 0; i < length; i++) {  67 if (!Base64.isBase64(arrayOctect[i]))  68 return false;  69  }  70 return true;  71  }  72  73 public static String encode(String str) {  74 if (str == null)  75 return "";  76 try {  77 byte[] b = str.getBytes("UTF-8");  78 return new String(encode(b), "UTF-8");  79 } catch (UnsupportedEncodingException e) {  80 return "";  81  }  82  }  83 public static byte[] encodeStr2Byte(String str) {  84 if (str == null)  85 return null;  86 try {  87 byte[] b = str.getBytes("UTF-8");  88 return encode(b);  89 } catch (UnsupportedEncodingException e) {  90 return null;  91  }  92  }  93  94 public static String encodeByte2Str(byte[] bytes) {  95 if (bytes == null)  96 return "";  97 try {  98 return new String(encode(bytes), "UTF-8");  99 } catch (UnsupportedEncodingException e) { 100 return null; 101  } 102  } 103 104 105 /** 106  * Encodes hex octects into Base64. 107  * 108  * @param binaryData Array containing binary data to encode. 109  * @return Base64-encoded data. 110 */ 111 public static byte[] encode(byte[] binaryData) { 112 int lengthDataBits = binaryData.length * EIGHTBIT; 113 int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; 114 int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; 115 byte encodedData[] = null; 116 117 if (fewerThan24bits != 0) { 118 //data not divisible by 24 bit 119 encodedData = new byte[(numberTriplets + 1) * 4]; 120 } else { 121 // 16 or 8 bit 122 encodedData = new byte[numberTriplets * 4]; 123  } 124 125 byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; 126 127 int encodedIndex = 0; 128 int dataIndex = 0; 129 int i = 0; 130 //log.debug("number of triplets = " + numberTriplets); 131 for (i = 0; i < numberTriplets; i++) { 132 dataIndex = i * 3; 133 b1 = binaryData[dataIndex]; 134 b2 = binaryData[dataIndex + 1]; 135 b3 = binaryData[dataIndex + 2]; 136 137 //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3); 138 139 l = (byte) (b2 & 0x0f); 140 k = (byte) (b1 & 0x03); 141 142 encodedIndex = i * 4; 143 byte val1 = 144 ((b1 & SIGN) == 0) 145 ? (byte) (b1 >> 2) 146 : (byte) ((b1) >> 2 ^ 0xc0); 147 byte val2 = 148 ((b2 & SIGN) == 0) 149 ? (byte) (b2 >> 4) 150 : (byte) ((b2) >> 4 ^ 0xf0); 151 byte val3 = 152 ((b3 & SIGN) == 0) 153 ? (byte) (b3 >> 6) 154 : (byte) ((b3) >> 6 ^ 0xfc); 155 156 encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; 157 //log.debug( "val2 = " + val2 ); 158 //log.debug( "k4   = " + (k<<4) ); 159 //log.debug(  "vak  = " + (val2 | (k<<4)) ); 160 encodedData[encodedIndex + 1] = 161 lookUpBase64Alphabet[val2 | (k << 4)]; 162 encodedData[encodedIndex + 2] = 163 lookUpBase64Alphabet[(l << 2) | val3]; 164 encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f]; 165  } 166 167 // form integral number of 6-bit groups 168 dataIndex = i * 3; 169 encodedIndex = i * 4; 170 if (fewerThan24bits == EIGHTBIT) { 171 b1 = binaryData[dataIndex]; 172 k = (byte) (b1 & 0x03); 173 //log.debug("b1=" + b1); 174 //log.debug("b1<<2 = " + (b1>>2) ); 175 byte val1 = 176 ((b1 & SIGN) == 0) 177 ? (byte) (b1 >> 2) 178 : (byte) ((b1) >> 2 ^ 0xc0); 179 encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; 180 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4]; 181 encodedData[encodedIndex + 2] = PAD; 182 encodedData[encodedIndex + 3] = PAD; 183 } else if (fewerThan24bits == SIXTEENBIT) { 184 185 b1 = binaryData[dataIndex]; 186 b2 = binaryData[dataIndex + 1]; 187 l = (byte) (b2 & 0x0f); 188 k = (byte) (b1 & 0x03); 189 190 byte val1 = 191 ((b1 & SIGN) == 0) 192 ? (byte) (b1 >> 2) 193 : (byte) ((b1) >> 2 ^ 0xc0); 194 byte val2 = 195 ((b2 & SIGN) == 0) 196 ? (byte) (b2 >> 4) 197 : (byte) ((b2) >> 4 ^ 0xf0); 198 199 encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; 200 encodedData[encodedIndex + 1] = 201 lookUpBase64Alphabet[val2 | (k << 4)]; 202 encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2]; 203 encodedData[encodedIndex + 3] = PAD; 204  } 205 206 return encodedData; 207  } 208 209 public static String decode(String str) { 210 if (str == null) 211 return ""; 212 try { 213 byte[] b = str.getBytes("UTF-8"); 214 return new String(decode(b), "UTF-8"); 215 } catch (UnsupportedEncodingException e) { 216 return ""; 217  } 218  } 219 220 public static byte[] decodeStr2Byte(String str) { 221 if (str == null) 222 return null; 223 try { 224 byte[] b = str.getBytes("UTF-8"); 225 return decode(b); 226 } catch (UnsupportedEncodingException e) { 227 return null; 228  } 229  } 230 231 /** 232  * Decodes Base64 data into octects 233  * 234  * @param binaryData Byte array containing Base64 data 235  * @return Array containing decoded data. 236 */ 237 public static byte[] decode(byte[] base64Data) { 238 // handle the edge case, so we don't have to worry about it later 239 if (base64Data.length == 0) { 240 return new byte[0]; 241  } 242 243 int numberQuadruple = base64Data.length / FOURBYTE; 244 byte decodedData[] = null; 245 byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0; 246 247 // Throw away anything not in base64Data 248 249 int encodedIndex = 0; 250 int dataIndex = 0; 251  { 252 // this sizes the output array properly - rlw 253 int lastData = base64Data.length; 254 // ignore the '=' padding 255 while (base64Data[lastData - 1] == PAD) { 256 if (--lastData == 0) { 257 return new byte[0]; 258  } 259  } 260 decodedData = new byte[lastData - numberQuadruple]; 261  } 262 263 for (int i = 0; i < numberQuadruple; i++) { 264 dataIndex = i * 4; 265 marker0 = base64Data[dataIndex + 2]; 266 marker1 = base64Data[dataIndex + 3]; 267 268 b1 = base64Alphabet[base64Data[dataIndex]]; 269 b2 = base64Alphabet[base64Data[dataIndex + 1]]; 270 271 if (marker0 != PAD && marker1 != PAD) { 272 //No PAD e.g 3cQl 273 b3 = base64Alphabet[marker0]; 274 b4 = base64Alphabet[marker1]; 275 276 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); 277 decodedData[encodedIndex + 1] = 278 (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); 279 decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4); 280 } else if (marker0 == PAD) { 281 //Two PAD e.g. 3c[Pad][Pad] 282 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); 283 } else if (marker1 == PAD) { 284 //One PAD e.g. 3cQ[Pad] 285 b3 = base64Alphabet[marker0]; 286 287 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); 288 decodedData[encodedIndex + 1] = 289 (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); 290  } 291 encodedIndex += 3; 292  } 293 return decodedData; 294  } 295 296 public static void main(String[] args) throws UnsupportedEncodingException{ 297 String s = "application=preSign&merchantId=11110000111100192&custName=测试商户&identityType=01&identityNo=330324198803160199&mobileNo=13738716288&cardType=01&cardNo=6222222222222222&signature=E10ADC3949BA59ABBE56E057F20F883E"; 298 System.out.println("原串: "); 299  System.out.println(s); 300 System.out.println("-------------------------------------------------------------------------------"); 301 String r = encode(s); 302 System.out.println("BASE64编码后: "); 303  System.out.println(r); 304 System.out.println("-------------------------------------------------------------------------------"); 305 String decode = decode(r); 306 System.out.println("BASE64解码后:" ); 307  System.out.println(decode); 308 System.out.println("-------------------------------------------------------------------------------"); 309 310 311  } 312 313 }
复制代码

输出结果:

复制代码
1 原串: 2 application=preSign&merchantId=11110000111100192&custName=测试商户&identityType=01&identityNo=330324198803160199&mobileNo=13738716288&cardType=01&cardNo=6222222222222222&signature=E10ADC3949BA59ABBE56E057F20F883E 3 ------------------------------------------------------------------------------- 4 BASE64编码后: 5 YXBwbGljYXRpb249cHJlU2lnbiZtZXJjaGFudElkPTExMTEwMDAwMTExMTAwMTkyJmN1c3ROYW1lPea1i+ivleWVhuaItyZpZGVudGl0eVR5cGU9MDEmaWRlbnRpdHlObz0zMzAzMjQxOTg4MDMxNjAxOTkmbW9iaWxlTm89MTM3Mzg3MTYyODgmY2FyZFR5cGU9MDEmY2FyZE5vPTYyMjIyMjIyMjIyMjIyMjImc2lnbmF0dXJlPUUxMEFEQzM5NDlCQTU5QUJCRTU2RTA1N0YyMEY4ODNF 6 ------------------------------------------------------------------------------- 7 BASE64解码后: 8 application=preSign&merchantId=11110000111100192&custName=测试商户&identityType=01&identityNo=330324198803160199&mobileNo=13738716288&cardType=01&cardNo=6222222222222222&signature=E10ADC3949BA59ABBE56E057F20F883E 9 ------------------------------------------------------------------------------- 
复制代码

 5 总结

说起Base64编码可能有些奇怪,因为大多数的编码都是由字符转化成二进制的过程,而从二进制转成字符的过程称为解码。而Base64的概念就恰好反了,由二进制转到字符称为编码,由字符到二进制称为解码。

Base64编码主要用在传输、存储、表示二进制等领域,还可以用来加密,但是这种加密比较简单,只是一眼看上去不知道什么内容罢了,当然也可以对Base64的字符序列进行定制来进行加密。

Base64编码是从二进制到字符的过程,像一些中文字符用不同的编码转为二进制时,产生的二进制是不一样的,所以最终产生的Base64字符也不一样。例如"上网"对应utf-8格式的Base64编码是"5LiK572R",对应GB2312格式的Base64编码是"yc/N+A=="。