BASE64编码解码

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=="。

参考:

  1、https://www.zhihu.com/question/36306744/answer/71626823

  2、https://baike.baidu.com/item/base64/8545775?fr=aladdin&fromid=1142965&fromtitle=base64%E7%BC%96%E8%A7%A3%E7%A0%81#4_2

 

posted @ 2017-11-28 14:18  DREAM.XIN  阅读(14981)  评论(2编辑  收藏  举报