Niko's blog

Base64 编码原理简介

2013-08-23

最近想到把在项目中用到的一些ios开发中标准库的类别实现进行收集,进行整理,方面以后使用。这是地址KNAdition,很大部分都是网上找到的,所在我想得自己先了解才好放上来吧。今天就看到了一个关于base64编码的一个方法,之前很早就听说过,但是具体原理却不甚解,趁着今天有空就学习一下。

其实base64就算你不知道,但是你们也经常用到了。它并不算一种加密的算法,只不过是通过一种编码方式把要发送的内容转换成人类不能直接识别的串,方便于传输。该编码的实现需要一些位移的运算,所以需要一些计算机基本知识。

其中说到的是bytes和bit,一个字节8个比特位的,如果你忘了,实现时会有点问题的吧。我开始别人代码时也纠结了。希望没有人遇到我这种情况,都都怪基础不扎实。

Base64就是有一个由64个字符组成的码表,最终生成的字符均从这个码表中可以找到,但是据说这个码表根据不同的实现,选择是不一样的,主要是62和63啦,这里就不深究了,详情看这里的WIKI

以下是码表:来源于wiki

原理

一句话说了就是:把3个字节变成4个字节。

3个字节24位(3 * 8)变成4个字节24位,每个字节取6位,然后高位补0.示例:

10100101 10110110 10111110

把这24bit连在一起,然后从左到右,顺序取6位。取出来的每个一个6位在高位补0,最终变成:

101001 011011 011010 111110

2的6次方就是64了,想必这就是base64的由来了。

以下是一个简单的例子:

以git三个字符为例,g i t三个字符对应的ASCII码为:103 105 116.对应的二进制数值为:

01100111 01101001 01110100

选择后:

00011001 00110110 00100101 00110100

转成整形后为:25 54 37 52从上面的码表中获取到对应的字符为:Z2l0.所以就是git通过base64编码后变成了Z2l0

所以从上面看,只要输入的字节为3的倍数,即可以被3整除,输出的字节数为输入的4/3.如果不是3的倍数,最后剩下的也就是只有1或者2个字节剩余。如果只剩一个字节,这时在转码是会有2个比特位剩下,这两个比特位后面补4个0,最后组成6个比特.这时的输出,每两个00用一个=代替。如果只剩下两个字节,在转换时会有4个比特位剩下,这四个比特位后面补两个0,最后组成6个比特位.

所以如果你输入的字节位n则输出的字节最多为:(n+2)* 4/3. 当n%3的余数为1时,输出的结果会有两个’==’在结尾;当余数位2时,输出的结果有一个’=’在结尾。

            //base64 encode
            - (NSString*) createEncodedString:(NSData*)data
            {
                static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

                const int size = ((data.length + 2)/3)*4;  //max output size
                uint8_t output[size];

                const uint8_t* input = (const uint8_t*)[data bytes];
                for (int i = 0; i < data.length; i += 3)
                {
                    int value = 0;
                    for (int j = i; j < (i + 3); j++)
                    {
                         value <<= 8;  //每一个输入都进行位移8
                        if (j < data.length)
                            value |= (0xFF & input[j]);
                    }

                    const int index = (i / 3) * 4;
                    output[index + 0] = table[(value >> 18) & 0x3F];  //右移十八位,通过&去右边六位
                    output[index + 1] = table[(value >> 12) & 0x3F];
                    output[index + 2] = (i + 1) < data.length ? table[(value >> 6) & 0x3F] : '=';
                    output[index + 3] = (i + 2) < data.length ? table[(value >> 0) & 0x3F] : '=';
            }

            return [[NSString alloc] initWithBytes:output length:size encoding:NSASCIIStringEncoding];
            } 

updated 2013-08-29

base64解码

解码的过程就是一个逆向的过程,首先是要对你的输入做一个判断,确认每一个字符都是在码表的范围内,如果不是可以选择跳过。每一次均可以通过移位和&运算把编码时高位增加的那两位去掉,最后拼成8位。