【动画密码学】Base64编码&解码算法

【动画密码学】Base64编码&解码算法

d642c75f077620b45f49061ae7a877d7.jpeg

本文依然是一篇老的文章,我们还是直接来看动画了。

什么是Base64?

Base64(基底64)是一种基于64个可打印字符来表示二进制数据的表示方法。-- 维基百科

简单来说,小明和小红两个人想要互相传递信息,他们产生的数据只能是二进制的流数据(也就是01),但是小明和小红要想互相传递信息不能直接说,而要写在小纸条上,这个小纸条又无法承载01这时候今天的主角「Base64」就出现了,他可以把二进制的数据流转换成为64个可见的字符。Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据,包括MIME的电子邮件及XML的一些复杂数据。

Base64的工作原理

首先要有一个索引表,标准 Base64 里的 64 个可打印字符是 A-Za-z0-9+/,分别依次对应索引值 0-63,标准的索引表如下:

Base64索引表

4df097de0f745ef87c74a0c0eb32e696.png
Base64索引表

编码方案

编码过程中,每三个字节(8bit * 3 = 24bit)一组进行编码,因为2^6 = 64因此每组有4个索引与之对应,如下图所示:

0c5511663601308d84aebd9b20ad9475.jpeg
编码方案

编码样例

上面的图可能不是很清晰,下面来看一个具体的例子,如下图所示:

2370667ba013822fb47511433370ae05.jpeg
编码样例

可以发现,对于ABC的Base64编码为QUJD,这时候细心的读者可能发现了,如果输入的长度不是3的整数倍或者二进制流的数据不是24的整数倍那么将要如何处理呢。这里Base64采用的是直接Padding0的方案,也就是不足24bit最后一个分组直接Padding0。注意如果是Padding的数据,那么不能按照索引表去查0位置的索引,而是直接用=填充。下面还是来看一个例子吧:

cd3662febe4aa189c6e5ff33e5797894.jpeg
Base64Padding

也就是说,如果剩余两个字节那么将会填充两个=, 如果剩余一个字节那么填充一个=

编码实现

const BASE64_PADDING_CHAR: char = '=';

const BASE64_TABLE: [char; 64] = [
    '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', '+', '/',
];

struct Base64 {}

impl Base64 {
    pub fn encode(data: &[u8]) -> String {
        if data.is_empty() {
            return String::new();
        }
        // 计算padding长度
        let encode_length = match (data.len() / 3, (data.len() % 3) != 0) {
            (data_length, true) => 4 * data_length + 4,
            (data_length, false) => 4 * data_length,
        };
        let mut result = String::with_capacity(encode_length);

        // 编码
        for i in (0..(data.len() / 3 * 3)).step_by(3) {
            let bits = ((data[i] & 0xff) as u32) << 16 | ((data[i + 1] & 0xff) as u32) << 8 | (data[i + 2] & 0xff) as u32;
            result.push(BASE64_TABLE[((bits >> 18) & 0x3f) as usize]);
            result.push(BASE64_TABLE[((bits >> 12) & 0x3f) as usize]);
            result.push(BASE64_TABLE[((bits >> 6) & 0x3f) as usize]);
            result.push(BASE64_TABLE[(bits & 0x3f) as usize]);
        }

        // padding
        match data.len() % 3 {
            1 => {
                let bits: u32 = (((data[data.len() - 1] & 0xff) as u32) << 4) as u32;
                result.push(BASE64_TABLE[((bits >> 6) & 0x3f) as usize]);
                result.push(BASE64_TABLE[(bits & 0x3f) as usize]);
                result.push(BASE64_PADDING_CHAR);
                result.push(BASE64_PADDING_CHAR);
            }
            2 => {
                let bits = ((data[data.len() - 2] & 0xff) as u32) << 10 | ((data[data.len() - 1] & 0xff) as u32) << 2;
                result.push(BASE64_TABLE[((bits >> 12) & 0x3f) as usize]);
                result.push(BASE64_TABLE[((bits >> 6) & 0x3f) as usize]);
                result.push(BASE64_TABLE[(bits & 0x3f) as usize]);
                result.push(BASE64_PADDING_CHAR);
            }
            _ => {}
        }
        return result;
    }
}

#[cfg(test)]
mod test {
    use crate::base64::Base64;

    #[test]
    fn test() {
        println!("Base64(ABC) = {}", Base64::encode("ABC".as_bytes()));
        println!("Base64(A) = {}", Base64::encode("A".as_bytes()));
        println!("Base64(AB) = {}", Base64::encode("AB".as_bytes()));
    }
}

参考资料

  • Base64 - 维基百科,自由的百科全书[1]

  • rfc1421[2]

Reference

[1]

Base64 - 维基百科,自由的百科全书: https://zh.wikipedia.org/wiki/Base64

[2]

rfc1421: https://datatracker.ietf.org/doc/html/rfc1421

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值