Base64 编解码
0.Base64的由来
目前Base64已经成为网络上常见的传输8Bit字节代码的编码方式之一。在做支付系统时,系统之间的报文交互都需要使用Base64对明文进行转码,然后再进行签名或加密,之后再进行(或再次Base64)传输。那么,Base64到底起到什么作用呢?
在参数传输的过程中经常遇到的一种情况:使用全英文的没问题,但一旦涉及到中文就会出现乱码情况。与此类似,网络上传输的字符并不全是可打印的字符,比如二进制文件、图片等。Base64的出现就是为了解决此问题,它是基于64个可打印的字符来表示二进制的数据的一种方法。
电子邮件刚问世的时候,只能传输英文,但后来随着用户的增加,中文、日文等文字的用户也有需求,但这些字符并不能被服务器或网关有效处理,因此Base64就登场了。随之,Base64在URL、Cookie、网页传输少量二进制文件中也有相应的使用。
1. Base64的编码原理
Base64的原理比较简单,每当我们使用Base64时都会先定义一个类似这样的数组:
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
上面就是Base64的索引表,字符选用了"A-Z、a-z、0-9、+、/" 64个(所以叫base64)可打印字符,这是标准的Base64协议规定。在日常使用中我们还会看到“=”或“==”号出现在Base64的编码结果中,“=”在此是作为填充字符出现,后面会讲到。
Base64就是用6位(2的6次幂就是64)表示字符,因此成为Base64。同理,Base32就是用5位,Base16就是用4位。
1.1 索引表
1.2 编码步骤
- 第一步,将待转换的字符串每三个字节分为一组,每个字节占8bit,那么共有24个二进制位。
- 第二步,将上面的24个二进制位每6个一组,共分为4组。
- 第三步,在每组前面添加两个0,每组由6个变为8个二进制位,总共32个二进制位,即四个字节。
- 第四步,根据Base64编码对照表(见上图)获得对应的值。
注意:
- Base64字符表中的字符原本用6个bit就可以表示,现在前面添加2个0,变为8个bit,会造成一定的浪费。因此,Base64编码之后的文本,要比原文大约三分之一。
- 为什么使用3个字节一组呢?因为6和8的最小公倍数为24,三个字节正好24个二进制位,每6个bit位一组,恰好能够分为4组。
1.3 一般示例
- 第一步:“M”、“a”、"n"对应的ASCII码值分别为77,97,110,对应的二进制值是01001101、01100001、01101110。如图第二三行所示,由此组成一个24位的二进制字符串。
- 第二步:如图红色框,将24位每6位二进制位一组分成四组。
- 第三步:在上面每一组前面补两个0,扩展成32个二进制位,此时变为四个字节:00010011、00010110、00000101、00101110。分别对应的值(Base64编码索引)为:19、22、5、46。
- 第四步:用上面的值在Base64编码表中进行查找,分别对应:T、W、F、u。因此“Man”Base64编码之后就变为:TWFu。
1.4 位数不足的情况
如果文本的字节数不是3的倍数,那如何处理呢?
- 两个字节:两个字节共16个二进制位,依旧按照规则进行分组。此时总共16个二进制位,每6个一组,则第三组缺少2位,用0补齐,得到三个Base64编码,第四组完全没有数据则用“=”补上。因此,上图中“BC”转换之后为“QKM=”;
- 一个字节:一个字节共8个二进制位,依旧按照规则进行分组。此时共8个二进制位,每6个一组,则第二组缺少4位,用0补齐,得到两个Base64编码,而后面两组没有对应数据,都用“=”补上。因此,上图中“A”转换之后为“QQ==”;
2. java代码验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import sun.misc.BASE64Encoder; /** * */ public class Base64Utils { public static void main(String[] args) { String man = "Man" ; String a = "A" ; String bc = "BC" ; BASE64Encoder encoder = new BASE64Encoder(); System. out .println( "Man base64结果为:" + encoder.encode(man.getBytes())); System. out .println( "BC base64结果为:" + encoder.encode(bc.getBytes())); System. out .println( "A base64结果为:" + encoder.encode(a.getBytes())); } } |
结果:和上面的示例一样。
1 2 3 | Man base64结果为:TWFu BC base64结果为:QkM= A base64结果为:QQ== |
3. C语言版本源码
3.1 解码原理
逆向推导,由每4个字节(每个字节包含6位有效比特位)合并成3个8位二进制数。
3.1.2 编制解码表
为了获取字符在编码索引表中的位置,每次都要在表中查找字符的位置;为了提高效率,可以编制一个128字节的解码索引表,例如上面“TWFu”的’T’,对应10进制为84,在编码索引表的位置为19,那么我们可以在解码索引表的下标84的位置放19;同理,‘W’对应的10进制为87,在编码索引表的位置为22,那么我们可以在解码索引表的下标87的位置放 22。64个参与编码的字符对应的下标位置,分别放进编码索引表的索引值。 我们给解码索引表起名base64DecodeChars,那么在这个表中,用C语言表示,就有下面的对应关系:
1 2 3 4 5 6 | Base64编码字符作为下标 编码字符对应的十进制数 C数组的值 -------------------------------------------------------------------- base64DecodeChars[ 'T' ] --- base64DecodeChars[84] --- 19 base64DecodeChars[ 'W' ] --- base64DecodeChars[87] --- 22 base64DecodeChars[ 'F' ] --- base64DecodeChars[70] --- 5 base64DecodeChars[ 'u' ] --- base64DecodeChars[117] --- 46 |
3.2 自己编写
主要参考多篇博客
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | #include <stdio.h> #include <stdlib.h> #include <string.h> static const char base64_table[] = { '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' , '+' , '/' }; static const unsigned char base64_suffix_map[256] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, 255, 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, 255, 255, 255, 255, 255, 255, 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, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; unsigned char move_bits(unsigned char src, int mv_left, int mv_right) { src <<= mv_left; src >>= mv_right; return src; } int base64_encode( char *indata, int inlen, char *outdata, int *outlen) { int ret=0; int i=0; if (indata==NULL || inlen<0){ printf( "indata is null,please input a correct data" ); return -1; } int in_new_len=0; int pad_len=0; if (inlen%3 != 0){ pad_len = 3 - inlen%3; } in_new_len=inlen+pad_len; char *p=outdata; /* int j=0; for(j=0;j<64;j++){ printf("base64_table[%d] is:%c\n",j,base64_table[j]); } */ for (i=0;i<in_new_len;i+=3){ *p = base64_table[move_bits(*indata,0,2)]; if (i==in_new_len-3 && pad_len != 0){ if (pad_len==1){ *(p+1) = base64_table[move_bits(*indata,6,2) + move_bits(*(indata+1),0,4)]; *(p+2) = base64_table[move_bits(*(indata+1),4,2)]; *(p+3) = '=' ; } else if (pad_len==2){ *(p+1) = base64_table[move_bits(*indata,6,2)]; *(p+2) = '=' ; *(p+3) = '=' ; } } else { *(p+1) = base64_table[move_bits(*indata,6,2) + move_bits(*(indata+1),0,4)]; *(p+2) = base64_table[move_bits(*(indata+1),4,2)+move_bits(*(indata+2),0,6)]; *(p+3) = base64_table[move_bits(*(indata+2),2,2)]; } p=p+4; indata=indata+3; ret=i; } *outlen=(in_new_len/3)*4; return ret; } int base64_decode( const char * indata, int inlen, char *outdata, int *outlen){ int ret = 0; if (indata == NULL || inlen <=0){ return ret=-1; } if (inlen%4 != 0){ return ret=-2; } int t=0,x=0,y=0,i=0; unsigned char c=0; int g=3; while (x<inlen){ c = base64_suffix_map[indata[x++]]; switch (c) { case 255: return -1; break ; case 253: continue ; break ; case 254: c=0; g--; break ; default : break ; } t = (t<<6)|c; if (++y==4){ outdata[i++] =(unsigned char )((t>>16)&0xff); if (g>1) outdata[i++] = (unsigned char )((t>>8)&0xff); if (g>2) outdata[i++] = (unsigned char )(t&0xff); y=t=0; } } if (outlen != NULL){ *outlen = i; } return ret; } int main( int argc, char *argv[]) { if (argc < 2){ printf( "use: %s xxx(a str)\n" ,argv[0]); return 0; } int ret; char *buf=NULL; buf=argv[1]; int len_base64=0; printf( "len of buf is: %ld\n" ,strlen(buf)); int len=(strlen(buf)+2)/3*4; printf( "len is: %d\n" ,len); char *buf_base64 = ( char *)malloc(len); memset(buf_base64, '\0' ,len); ret=base64_encode(buf,strlen(buf),buf_base64,&len_base64); printf( "buf is:\n" ); printf( "%s\n" ,buf); printf( "\nbase64_encode is:\n" ); printf( "%s\n" ,buf_base64); printf( "ret is: %d,len_base64 is: %d\n\n" ,ret,len_base64); char *buf_base64_de = ( char *)malloc(strlen(buf)); memset(buf_base64_de, '\0' ,strlen(buf)); int len_base64_de=0; ret=base64_decode(buf_base64,len,buf_base64_de,&len_base64_de); printf( "\nbase64_decode is:\n" ); printf( "%s\n" ,buf_base64_de); printf( "ret is: %d,len_base64_de is: %d\n\n" ,ret,len_base64_de); return 0; } |
3.3 其他版本
https://blog.csdn.net/wooin/article/details/127429256
参考链接:
https://blog.csdn.net/wo541075754/article/details/81734770
https://blog.csdn.net/hwd00001/article/details/125803710
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?