zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Netty优雅退出
    使用FFmpeg切片HLS流
    Golang协程和线程区别
    00 PyTorch 开发环境搭建
    SpringBoot + LibreOffice + Hutool 实现附件预览简单示例
    css 注意事项 BFC
    css 伪类 属性选择器 优先级
    html img 标签图片格区别
    redis集群
    Redis RDB与AOF
  • 原文地址:https://www.cnblogs.com/xq1314/p/7909521.html
Copyright © 2011-2022 走看看