base64

特征:09+AZ+a~z + + / =。

本质:它是一种基于 64 个可打印字符来表示二进制数据的表示方法。由于 2^6=64,所以每 6 个比特为一个单元,对应某个可打印字符。3 个字节有 24 个比特,对应于 4 个 Base64 单元,即 3 个字节可由 4 个可打印字符来表示。

目的:用以解决各系统以及传输协议中二进制不兼容的问题

用于:处理文本数据的场合,表示、传输、存储一些二进制数据,包括MIME电子邮件XML的一些复杂数据。

编码规则

假如要编码一个非二进制字符,Base64的编码规则是:先把这个字符处理成二进制,然后每连续6个bit计算其十进制,得出十进制的值之后,再根据Base64对照表找到对应的字符。

编码方式

  1. 将原始数据每三个字节作为一组,每个字节是8个bit,一共是 24 个 bit

  2. 将 24 个 bit 分为四组,每组 6 个 bit

  3. 有不足六位的情况,加补零凑够六位,同时凑出来的位数是 8 的倍数 *,不然就加特殊的六位符号 “ = ” * 。 此时,原始数据的3个字节变成4个字节,增大了将近30%

即 bit 不为 6 或 8 的倍数补 0 *,原始数据不为 3 的倍数补 “ = ” 。

  1. 根据Base64码表得到扩展后每个字节的对应符号

例:原文 man

字节的位总数不为 6 的倍数以及原文不为 3 的倍数:

  • 要凑成8的倍数,是因为base64主要用于加密后的数据传送,而在传送机制中都认为传送的最小单位是按照字节算的,所以不能出现不是位总数不是8的倍数的情况,

  • " = " 的意义:base64 编码过程以 3 个为一组,当原文不足三个时即在 base64 中补充 " = "

  • 只用凑齐满足条件的最小的 6 和 8 的倍数即可,不用补 0 到凑够 24 个 bit 。

base64 码表:

base64 编码: python中 的 base64 模块

  • base64.b64encode()将 bytes 类型数据进行base64编码,返回编码后的bytes类型

  • encode(encoding) 将字符串编码为指定编码(如 UTF-8、ASCII 等)的字节序列。它接受一个编码参数,指定要使用的字符编码方式,然后返回对应编码的字节序列(bytes 对象)

  • decode(encoding) * 将字节序列解码为字符串,根据指定的编码方式将字节序列转换为字符串形式。它接受一个编码参数,指定要使用的字符编码方式,然后返回解码后的字符串。

  • base64.b64deocde()将base64编码的 bytes 类型进行解码,返回解码后的 bytes 类型

  • encodestring 和 decodestring ,专门用来编码和解码字符串

  • urlsafe_b64encode 和 urlsafe_b64decode ,专门对url进行Base64编解码,实际上也是调用的前一组函数

注:

  • 使用库加密时,需要同时用到 encode 和 decode 函数,向使用 encode 将原始数据转换成指定编码的字节,decode 则是将 encode 转换后的字节按照指定编码再转换成加密字符串。

  • 但使用库解密时,只需用到 decode 函数,此时 decode 函数用于解密,将 base64.b64deocde 函数解码后的字节序列按指定编码转换成解密字符。

base64 通过库加密解密代码:


import base64

a="0"
print("选择栏:")
print("1、base64加密")
print("2、base64解密")
print("3、exist")

while a!="3":
    a = input("请选择你的操作:")
    if a=="1":
        yuanshi = input("请输入待加密的字符:")
        yuanshibyte = yuanshi.encode('utf-8')
        jiamibyte = base64.b64encode(yuanshibyte)
        jiami = jiamibyte.decode('utf-8')  
        print("Base64 加密后: ", jiami)
    elif a=="2":
        jiami = input("请输入待解密的字符:")
        jiemibyte = base64.b64decode(jiami)
        jiemi = jiemibyte.decode('utf-8')
        print("Base64 解密后: ", jiemi)

base64 不使用库加解密代码:

zifuji = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

def encode(yuanshi):
    yuanshibyte = yuanshi.encode('utf-8')
    result = ''
    for i in range(0, len(yuanshibyte), 3):
        chunk = yuanshibyte[i:i+3]
        binary_chunk = ''.join(format(byte, '08b') for byte in chunk)
        while len(binary_chunk) % 6 != 0:
            binary_chunk += '00000000' 
        for j in range(0, len(binary_chunk), 6):
            index = int(binary_chunk[j:j+6], 2)
            result += zifuji[index]
    padding = (4 - len(result) % 4) % 4
    result += '=' * padding
    return result
def base64_decode(yuanshi):
    yuanshi = yuanshi.replace('=', '')
    result = bytearray()
    for i in range(0, len(yuanshi), 4):
        chunk = yuanshi[i:i+4]
        binary_chunk = ''
        for char in chunk:
            index = zifuji.index(char)
            binary_chunk += format(index, '06b')
        while len(binary_chunk) % 8 != 0:
            binary_chunk = binary_chunk[:-1] 
        for j in range(0, len(binary_chunk), 8):
            byte = int(binary_chunk[j:j+8], 2)
            result.append(byte)
    return result.decode('utf-8')
a="0"
print("选择栏:")
print("1、base64加密")
print("2、base64解密")
print("3、exist")

while a!="3":
    a = input("请选择你的操作:")

    if a=="1":
        input_str = input("请输入待加密的字符:")
        encoded_str = encode(input_str)
        print("Base64 加密结果:", encoded_str)
    elif a=="2":
        encoded_str = input("请输入待解密的字符:")
        decoded_str = base64_decode(encoded_str)
        print("Base64 解密结果:", decoded_str)

base64 隐写

隐写原理

在对长度非 3 的倍数的字符串进行 Base64 编码过程中,进行转换为二进制字串这一步骤时会在末尾添加 0,而解码过程中之前添加的 0 则会被舍弃,这意味着在这一步骤添加的二进制值可以不全为0,这并不影响解码结果。

例如:

Terra 这一字符串的长度为 5,非 3 的倍数,在转为 6 位二进制字串时添加了两个 0(红色加粗部分)。编码后的结果为 VGVycmE=:

倘若添加的二进制值不全为 0,虽然会改变 " = " 前最后一个字符的值,使编码后的字符串变为 VGVycmH= ,但该字符串进行 Base64 解码的结果依然是 Terra:

末尾有两个 " = " 字符的编码字符串同样如此,Lucy 字符串正常编码应为 THVjeQ==

修改后为 THVjeV==,同上,进行 base64 解码结果依然是 Lucy

解密原理

因为 " = " 用于在原文长度不足 3 的倍数时补全原文成三的倍数,所以可分为: 只有一个 " = " :

原文为 3 的倍数减一 ,二进制转换就相当于 (3^n-1)*8/6 余数总是 4 ,也就是说总要补上两位 " 0 "

同理,有两个 " = " :

二进制转换为 (3^n-2)*8/6 余数总是 2,也就是会补上四位 " 0 "

利用这个特点,可以根据原文的 " = " 长度来决定是取后两位还是后四位的加密二进制数然后组合在一起解码

解密脚本

import base64

def int2Bin(digit):
	return bin(digit)[2:]	#将索引转成二进制,去掉'0b';


def binAsc(string):		#二进制转成ASCII码
	temp = ''
	for i in range(int(len(string) / 8)):
		temp += chr(int(string[i * 8 : i* 8 + 8] , 2))
	return temp

def readBase64FromFile(filename):
	Base64Char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"     #Base64字符集 已按照规范排列
	result = ''
	with open(filename ,'r') as f:
		for data in f.readlines():
			if data.find('==') > 0:
				result += int2Bin(Base64Char.index(data[-4]))[-4:]		#根据隐写原理,‘==’情况取等号前最后一个字符转换后取后4位
			elif data.find('=') > 0:
				result += int2Bin(Base64Char.index(data[-3]))[-2:]		#根据隐写原理,‘=’情况取等号前最后一个字符转换后取后2位
	print(binAsc(result))

readBase64FromFile('flag.txt')