Base64(32)
0x01概念理解
百度百科中解释
Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。可查看RFC2045~RFC2049,上面有MIME的详细规范。
Base64编码是从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息。例如,在Java Persistence系统Hibernate中,就采用了Base64来将一个较长的唯一标识符(一般为128-bit的UUID)编码为一个字符串,用作HTTP表单和HTTP GET URL中的参数。在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式。此时,采用Base64编码具有不可读性,需要解码后才能阅读。
Base64算法使用了单表置换算法的思想。Base64使用一个由64个字符组成的映射表,见Base64字符映射表,然后其他的所有符号都根据这个映射表转换成对应的字符。
注意:Base64是在给定字符编码(如:gbk,utf-8)的基础上进行的,因为经过这些字符编码的编码之后,已经转换成该编码的二进制字节码了
这样Base64才能使用它的64字符进行转换
0x02简介
标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换,因为ANSI SQL中已将“%”号用作通配符。
为解决此问题,可采用一种用于URL的改进Base64编码,它在末尾填充’='号,并将标准Base64中的“+”和“/”分别改成了“-”和“_”,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。
另有一种用于正则表达式的改进Base64变种,它将“+”和“/”改成了“!”和“-”,因为“+”,“*”以及前面在IRCu中用到的“[”和“]”在正则表达式中都可能具有特殊含义。
此外还有一些变种,它们将“+/”改为“-”或“.”(用作编程语言中的标识符名称)或“.-”(用于XML中的Nmtoken)甚至“_:”(用于XML中的Name)。
Base64要求把每三个8Bit的字节转换为四个6Bit的字节(3 * 8 = 4*6 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。
0x03编码原理
1>将给定的字符串转化成对应的字符编码(如:GBK、UTF-8)
2>将获得该字符编码转换成二进制码
3>对获得的二进制码进行分组操作
第一步:每3个字节(8位二进制)为一组,一共24个二进制位
第二步:将这个24个二进制位分成4组,每个组有6个二进制位,不足6位的,后面补0。
第三步:在每个组前面加两个0,这样每个组就又变成了8位,即每个组一个字节,4个组就4个字节了。
第四步:根据Base64的转码表找到每个字节对应的符号,这个符号就是Base64的编码值
转码过程例子:
3 * 8=4*6
内存1个字节占8位
转前: s 1 3
先转成ascii:对应 115 49 51
2进制: 01110011 00110001 00110011
6个一组(4组) 011100110011000100110011
然后才有后面的 011100 110011 000100 110011
然后计算机是8位8位的存数 6不够,自动就补两个高位0了
所有有了 高位补0
科学计算器输入 00011100 00110011 00000100 00110011
得到 28 51 4 51
查对下照表 c z E z

0x04编码规则
经过Base64编码后的字符串的字符数一定是4的整数倍。在使用Base64编码时,如果得到的字符数不为4的整数倍,则后面使用等号 ‘=’补足
①把3个字符变成4个字符。
②每76个字符加一个换行符。
③最后的结束符也要处理。
例1:
转换前 11111111, 11111111, 11111111 (二进制)
转换后 00111111, 00111111, 00111111, 00111111 (二进制)
上面的三个字节是原文,下面的四个字节是转换后的Base64编码,其前两位均为0。
转换后,我们用一个码表来得到我们想要的字符串(也就是最终的Base64编码)
例2:
转换前 10101101,10111010,01110110
转换后 00101011, 00011011 ,00101001 ,00110110
十进制 43 27 41 54
对应码表中的值 r b p 2
所以上面的24位编码,编码后的Base64值为 rbp2
解码同理,把 rbq2 的二进制位连接上再重组得到三个8位值,得出原码。
(解码只是编码的逆过程,有关MIME的RFC还有很多,如果需要详细情况请自行查找。)
第一个字节,根据源字节的第一个字节处理。
规则:源第一字节右移两位,去掉低2位,高2位补零。
即:00 + 高6位
第二个字节,根据源字节的第一个字节和第二个字节联合处理。
规则如下,第一个字节高6位去掉然后左移四位,第二个字节右移四位
即:源第一字节低2位 + 源第2字节高4位
第三个字节,根据源字节的第二个字节和第三个字节联合处理,
规则第二个字节去掉高4位并左移两位(得高6位),第三个字节右移6位并去掉高6位(得低2位),相加即可
第四个字节,规则,源第三字节去掉高2位即可
//用更接近于编程的思维来说,编码的过程是这样的:
//第一个字符通过右移2位获得第一个目标字符的Base64表位置,根据这个数值取到表上相应的字符,就是第一//个目标字符。
//然后将第一个字符与0x03(00000011)进行与(&)操作并左移4位,接着第二个字符右移4位与前者相或(|),即获得第二个目标字符。
//再将第二个字符与0x0f(00001111)进行与(&)操作并左移2位,接着第三个字符右移6位与前者相或(|),获得第三个目标字符。
//最后将第三个字符与0x3f(00111111)进行与(&)操作即获得第四个目标字符。
//在以上的每一个步骤之后,再把结果与 0x3F 进行 AND 位操作,就可以得到编码后的字符了。
可是等等……聪明的你可能会问到,原文的字节数量应该是3的倍数啊,如果这个条件不能满足的话,那该怎么办呢?
我们的解决办法是这样的:原文剩余的字节根据编码规则继续单独转(1变2,2变3;不够的位数用0补全),再用=号补满4个字节。这就是为什么有些Base64编码会以一个或两个等号结束的原因,但等号最多只有两个。因为:
一个原字节至少会变成两个目标字节
所以余数任何情况下都只可能是0,1,2这三个数中的一个。如果余数是0的话,就表示原文字节数正好是3的倍数(最理想的情况)。如果是1的话,转成2个Base64编码字符,为了让Base64编码是4的倍数,就要补2个等号;同理,如果是2的话,就要补1个等号。
0x05代码实现
JAVA
package com.dokio.base64;
public class Base64 {
//Constructor
public Base64() {
}
private static final String base64Code= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
public static String encode(String srcStr) {
//有效值检查
if(srcStr == null || srcStr.length() == 0) {
return srcStr;
}
//将明文的ASCII码转为二进制位字串
char[] srcStrCh= srcStr.toCharArray();
StringBuilder asciiBinStrB= new StringBuilder();
String asciiBin= null;
for(int i= 0; i< srcStrCh.length; i++) {
asciiBin= Integer.toBinaryString((int)srcStrCh[i]);
while(asciiBin.length()< 8) {
asciiBin= "0"+ asciiBin;
}
asciiBinStrB.append(asciiBin);
}
//跟据明文长度在二进制位字串尾部补“0”
while(asciiBinStrB.length()% 6!= 0) {
asciiBinStrB.append("0");
}
String asciiBinStr= String.valueOf(asciiBinStrB);
//将上面得到的二进制位字串转为Value,再跟据Base64编码表将之转为Encoding
char[] codeCh= new char[asciiBinStr.length()/ 6];
int index= 0;
for(int i= 0; i< codeCh.length; i++) {
index= Integer.parseInt(asciiBinStr.substring(0, 6), 2);
asciiBinStr= asciiBinStr.substring(6);
codeCh[i]= base64Code.charAt(index);
}
StringBuilder code= new StringBuilder(String.valueOf(codeCh));
//跟据需要在尾部添加“=”
if(srcStr.length()% 3 == 1) {
code.append("==");
} else if(srcStr.length()% 3 == 2) {
code.append("=");
}
//每76个字符加一个回车换行符(CRLF)
int i= 76;
while(i< code.length()) {
code.insert(i, "\r\n");
i+= 76;
}
code.append("\r\n");
return String.valueOf(code);
}
public static String decode(String srcStr) {
//有效值检查
if(srcStr == null || srcStr.length() == 0) {
return srcStr;
}
//检测密文中“=”的个数后将之删除,同时删除换行符
int eqCounter= 0;
if(srcStr.endsWith("==")) {
eqCounter= 2;
} else if(srcStr.endsWith("=")) {
eqCounter= 1;
}
srcStr= srcStr.replaceAll("=", "");
srcStr= srcStr.replaceAll("\r\n", "");
//跟据Base64编码表将密文(Encoding)转为对应Value,然后转为二进制位字串
char[] srcStrCh= srcStr.toCharArray();
StringBuilder indexBinStr= new StringBuilder();
String indexBin= null;
for(int i= 0; i< srcStrCh.length; i++) {
indexBin= Integer.toBinaryString(base64Code.indexOf((int)srcStrCh[i]));
while(indexBin.length()< 6) {
indexBin= "0"+ indexBin;
}
indexBinStr.append(indexBin);
}
//删除因编码而在尾部补位的“0”后得到明文的ASCII码的二进制位字串
if(eqCounter == 1) {
indexBinStr.delete(indexBinStr.length()- 2, indexBinStr.length());
} else if(eqCounter == 2) {
indexBinStr.delete(indexBinStr.length()- 4, indexBinStr.length());
}
String asciiBinStr= String.valueOf(indexBinStr);
//将上面得到的二进制位字串分隔成字节后还原成明文
String asciiBin= null;
char[] ascii= new char[asciiBinStr.length()/ 8];
for(int i= 0; i< ascii.length; i++) {
asciiBin= asciiBinStr.substring(0, 8);
asciiBinStr= asciiBinStr.substring(8);
ascii[i]= (char)Integer.parseInt(asciiBin, 2);
}
return String.valueOf(ascii);
}
public static void main(String[] args) {
System.out.print(encode("I like your long long shadow.It just seems you are unhappy to say goodbye to me."));
System.out.print("\n---dokio---\n");
System.out.print(decode("SSBsaWtlIHlvdXIgbG9uZyBsb25nIHNoYWRvdy5JdCBqdXN0IHNlZW1zIHlvdSBhcmUgdW5oYXBweSB0byBzYXkgZ29vZGJ5ZSB0byBtZS4="));
System.out.print("\n---dokio---\n");
System.out.print(decode(encode("I like your long long shadow.It just seems you are unhappy to say goodbye to me.")));
}
C
编码
#include <stdio.h>
#include <string.h>
// 全局常量定义
const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const char padding_char = '=';
/*编码代码
* const unsigned char * sourcedata, 源数组
* char * base64 ,码字保存
*/
int base64_encode(const unsigned char * sourcedata, char * base64)
{
int i=0, j=0;
unsigned char trans_index=0; // 索引是8位,但是高两位都为0
const int datalength = strlen((const char*)sourcedata);
for (; i < datalength; i += 3){
// 每三个一组,进行编码
// 要编码的数字的第一个
trans_index = ((sourcedata[i] >> 2) & 0x3f);
base64[j++] = base64char[(int)trans_index];
// 第二个
trans_index = ((sourcedata[i] << 4) & 0x30);
if (i + 1 < datalength){
trans_index |= ((sourcedata[i + 1] >> 4) & 0x0f);
base64[j++] = base64char[(int)trans_index];
}else{
base64[j++] = base64char[(int)trans_index];
base64[j++] = padding_char;
base64[j++] = padding_char;
break; // 超出总长度,可以直接break
}
// 第三个
trans_index = ((sourcedata[i + 1] << 2) & 0x3c);
if (i + 2 < datalength){ // 有的话需要编码2个
trans_index |= ((sourcedata[i + 2] >> 6) & 0x03);
base64[j++] = base64char[(int)trans_index];
trans_index = sourcedata[i + 2] & 0x3f;
base64[j++] = base64char[(int)trans_index];
}
else{
base64[j++] = base64char[(int)trans_index];
base64[j++] = padding_char;
break;
}
}
base64[j] = '\0';
return 0;
}
解码
/** 在字符串中查询特定字符位置索引
* const char *str ,字符串
* char c,要查找的字符
*/
inline int num_strchr(const char *str, char c) //
{
const char *pindex = strchr(str, c);
if (NULL == pindex){
return -1;
}
return pindex - str;
}
/* 解码
* const char * base64 码字
* unsigned char * dedata, 解码恢复的数据
*/
int base64_decode(const char * base64, unsigned char * dedata)
{
int i = 0, j=0;
int trans[4] = {0,0,0,0};
for (;base64[i]!='\0';i+=4){
// 每四个一组,译码成三个字符
trans[0] = num_strchr(base64char, base64[i]);
trans[1] = num_strchr(base64char, base64[i+1]);
// 1/3
dedata[j++] = ((trans[0] << 2) & 0xfc) | ((trans[1]>>4) & 0x03);
if (base64[i+2] == '='){
continue;
}
else{
trans[2] = num_strchr(base64char, base64[i + 2]);
}
// 2/3
dedata[j++] = ((trans[1] << 4) & 0xf0) | ((trans[2] >> 2) & 0x0f);
if (base64[i + 3] == '='){
continue;
}
else{
trans[3] = num_strchr(base64char, base64[i + 3]);
}
// 3/3
dedata[j++] = ((trans[2] << 6) & 0xc0) | (trans[3] & 0x3f);
}
dedata[j] = '\0';
return 0;
}
主函数
// 测试
int main()
{
const unsigned char str[] = "a45rbcd";
const unsigned char *sourcedata = str ;
char base64[128];
base64_encode(sourcedata, base64);
printf("编码:%s\n",base64);
char dedata[128];
base64_decode(base64, (unsigned char*)dedata);
printf("译码:%s", dedata);
getchar();
getchar();
return 0;
}
---------------------
作者:icesongqiang
原文:https://blog.csdn.net/u011491972/article/details/52800177
Pyton
"""
base64实现
"""
import base64
import string
# base 字符集
base64_charset = string.ascii_uppercase + string.ascii_lowercase + string.digits + '+/'
def encode(origin_bytes):
"""
将bytes类型编码为base64
:param origin_bytes:需要编码的bytes
:return:base64字符串
"""
# 将每一位bytes转换为二进制字符串
base64_bytes = ['{:0>8}'.format(str(bin(b)).replace('0b', '')) for b in origin_bytes]
resp = ''
nums = len(base64_bytes) // 3
remain = len(base64_bytes) % 3
integral_part = base64_bytes[0:3 * nums]
while integral_part:
# 取三个字节,以每6比特,转换为4个整数
tmp_unit = ''.join(integral_part[0:3])
tmp_unit = [int(tmp_unit[x: x + 6], 2) for x in [0, 6, 12, 18]]
# 取对应base64字符
resp += ''.join([base64_charset[i] for i in tmp_unit])
integral_part = integral_part[3:]
if remain:
# 补齐三个字节,每个字节补充 0000 0000
remain_part = ''.join(base64_bytes[3 * nums:]) + (3 - remain) * '0' * 8
# 取三个字节,以每6比特,转换为4个整数
# 剩余1字节可构造2个base64字符,补充==;剩余2字节可构造3个base64字符,补充=
tmp_unit = [int(remain_part[x: x + 6], 2) for x in [0, 6, 12, 18]][:remain + 1]
resp += ''.join([base64_charset[i] for i in tmp_unit]) + (3 - remain) * '='
return resp
def decode(base64_str):
"""
解码base64字符串
:param base64_str:base64字符串
:return:解码后的bytearray;若入参不是合法base64字符串,返回空bytearray
"""
if not valid_base64_str(base64_str):
return bytearray()
# 对每一个base64字符取下标索引,并转换为6为二进制字符串
base64_bytes = ['{:0>6}'.format(str(bin(base64_charset.index(s))).replace('0b', '')) for s in base64_str if
s != '=']
resp = bytearray()
nums = len(base64_bytes) // 4
remain = len(base64_bytes) % 4
integral_part = base64_bytes[0:4 * nums]
while integral_part:
# 取4个6位base64字符,作为3个字节
tmp_unit = ''.join(integral_part[0:4])
tmp_unit = [int(tmp_unit[x: x + 8], 2) for x in [0, 8, 16]]
for i in tmp_unit:
resp.append(i)
integral_part = integral_part[4:]
if remain:
remain_part = ''.join(base64_bytes[nums * 4:])
tmp_unit = [int(remain_part[i * 8:(i + 1) * 8], 2) for i in range(remain - 1)]
for i in tmp_unit:
resp.append(i)
return resp
def valid_base64_str(b_str):
"""
验证是否为合法base64字符串
:param b_str: 待验证的base64字符串
:return:是否合法
"""
if len(b_str) % 4:
return False
for m in b_str:
if m not in base64_charset:
return False
return True
if __name__ == '__main__':
s = '我的目标是星辰大海. One piece, all Blue'.encode()
local_base64 = encode(s)
print('使用本地base64加密:', local_base64)
b_base64 = base64.b64encode(s)
print('使用base64加密:', b_base64.decode())
print('使用本地base64解密:', decode(local_base64).decode())
print('使用base64解密:', base64.b64decode(b_base64).decode())
0x06 Base32
Base32和Base64的一点区分注意
看到编码内容,只有大写和数字,根据Base64和Base32 区别:
base64中包含大写字母(A-Z)、小写字母(a-z)、数字0——9以及+/;
base32中只有大写字母(A-Z)和数字234567
package com.ft.otp.util.alg;
import java.io.UnsupportedEncodingException;
public class Base32 {
private static final String base32Chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
private static final int[] base32Lookup = {
0xFF, 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // '0', '1', '2', '3', '4', '5', '6', '7'
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // '8', '9', ':', ';', '<', '=', '>', '?'
0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G'
0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W'
0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 'X', 'Y', 'Z', '[', '\', ']', '^', '_'
0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g'
0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 'p', 'q', 'r', 's', 't', 'u', 'v', 'w'
0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 'x', 'y', 'z', '{', '|', '}', '~', 'DEL'
};
public static String encode(
final byte[] bytes) {
int i = 0, index = 0, digit = 0;
int currByte, nextByte;
StringBuffer base32 = new StringBuffer((bytes.length + 7) * 8 / 5);
while (i < bytes.length) {
currByte = (bytes[i] >= 0) ? bytes[i] : (bytes[i] + 256); // unsign
/* Is the current digit going to span a byte boundary? */
if (index > 3) {
if ((i + 1) < bytes.length) {
nextByte = (bytes[i + 1] >= 0) ? bytes[i + 1] : (bytes[i + 1] + 256);
} else {
nextByte = 0;
}
digit = currByte & (0xFF >> index);
index = (index + 5) % 8;
digit <<= index;
digit |= nextByte >> (8 - index);
i++;
} else {
digit = (currByte >> (8 - (index + 5))) & 0x1F;
index = (index + 5) % 8;
if (index == 0) {
i++;
}
}
base32.append(base32Chars.charAt(digit));
}
return base32.toString();
}
public static byte[] decode(
final String base32) {
int i, index, lookup, offset, digit;
byte[] bytes = new byte[base32.length() * 5 / 8];
for (i = 0, index = 0, offset = 0; i < base32.length(); i++) {
lookup = base32.charAt(i) - '0';
/* Skip chars outside the lookup table */
if (lookup < 0 || lookup >= base32Lookup.length) {
continue;
}
digit = base32Lookup[lookup];
/* If this digit is not in the table, ignore it */
if (digit == 0xFF) {
continue;
}
if (index <= 3) {
index = (index + 5) % 8;
if (index == 0) {
bytes[offset] |= digit;
offset++;
if (offset >= bytes.length) {
break;
}
} else {
bytes[offset] |= digit << (8 - index);
}
} else {
index = (index + 5) % 8;
bytes[offset] |= (digit >>> index);
offset++;
if (offset >= bytes.length) {
break;
}
bytes[offset] |= digit << (8 - index);
}
}
return bytes;
}
public static class Logger {
public Logger() {
}
public static void v(String string) {
System.out.println(string);
return;
}
}
public static void main(String[] args) {
String input = "1wozaina9";
byte[] inputBuf = input.getBytes();
// try {
// String s = new String(inputBuf, "utf-8");
// Logger.v("s : " + s);
// } catch (UnsupportedEncodingException e1) {}
//
String encoded = Base32.encode(inputBuf);
System.out.println("==========="+encoded);
try {
byte[] outputBuf = Base32.decode(encoded);
String s = new String(outputBuf, "utf-8");
Logger.v("s : " + s);
} catch (Exception e) {
Logger.v("Illegal character");
}
}
}
---------------------
作者:Accompany_l
原文:https://blog.csdn.net/java001122/article/details/80228983
base32和base64原理是一样的,32和64分别是2^5 和 2^6。
拿base32举例来说,每一个字符是有5Bit,但是ASCII字符有8Bit,所以base32是用8个base32字符来代替5个ASCII字符。
同样,base64是用4个base64字符代替3个ASCII字符。这样的话,进行对应的转码就只要取出对应位数的字符进行简单的位运算,不足位补零就可以了。
本文详细介绍了Base64和Base32编码的概念、原理、编码规则以及编码实现。Base64是基于64个可打印字符来表示二进制数据的编码方式,常用于HTTP环境下的数据传输。Base32则只使用大写字母和数字2-7进行编码。编码过程涉及到字符编码、二进制分组和位运算。文章还提供了不同编程语言的代码实现示例。
1674

被折叠的 条评论
为什么被折叠?



