Base64算法

一. Base64算法概述

Base64算法最早应用于解决电子邮件传输的问题。早期,由于“历史问题”,电子邮件只允许ASCII码字符,如果邮件中包含非ASCII码字符,当它通过有“历史问题”的网关时,这个网关会对该字符的二进制位进行调整,即将其8位二进制码的最高位置0,这样用户收到的邮件就会是一封乱码。为了解决这个问题,产生了Base64算法。

Base64主要用于将不可打印的字符转换成可打印字符,或者简单的说将二进制数据编码成ASCII字符(注:可打印的)。

将二进制数据编码成ASCII字符主要的目的是能在纯文本内容中插入二进制数据,常见的应用场景包括:

1. 电子邮件

这个可参考阮一峰的《MIME笔记》

2. 微软的MHT格式

这是模仿邮件格式将多种资源打包在一个文件中的格式,所有二进制资源都采用 Base64 编码。

3. XML文件

这是一个纯文本文件,如果要基于 XML 格式设计可以保存图片或其它附件的数据格式,那就需要将这些二进制数据转码成 ASCII 字符。

4. DATA URL

最近流行起来的 Data URL,要在URL中使用二进制数据,当然也只能进行 ASCII 编码

当然除了 Base64 之外,还有其它一些编码方式可以将二进制数据编码成 ASCII 字符,比如十六进制编码,除此之外还有 Quoted-printable 等。甚至 URL 中使用 %XX 来对非 ASCII 字符进行编码的方式也可以算在内。

当然一般非特定环境下,选用十六进制编码和 Base64 编码的情况比较多,主要是因为这两种编码易用,而且转换后的数据量相对较小。

十六进制编码是将 1 个字节编码成 2 个十六进制字符,比如 0x10110110 编码成 B6,转换后数据量会增大 1 倍

Base64 编码是将 3 个字节共 24 位数据,以每 6 位一个 Base64 字符 [0-9a-zA-Z+/] 表示,24 位数据共需要 4 个 Base64 字符表示,编码后数据增长约 1/3。为什么是“约”?因为如果原数据字节数不是 3 的倍数,需要补位,这样转换出来的数据量就会比原来的 4/3 略多一点。

从上面的数据增长比来看,Base64编码 比十六进制编码更节省磁盘容量,所以一般较大的数据需要进行 ASCII 编码多采用 Base64;而较小的数据,则使用易于人工识别十六进制(用纸笔就能解码出来)。

具体参考:为什么有的代码要用 base64 进行编码?

二. Base64算法基本原理

Base64算法的转换方式类似于古典加密算法里的单表置换算法 。RFC 2045中给出了Base64的字符映射表,如下图所示。

这张字符映射表中,Value是十进制编码,Encoding是字符,共映射了64个字符,这也是Base64算法命名的由来。映射表的最后一个字符“=”是用来补位的。

Base64算法的编码和解码操作可用作加密解密,但是Base64的字符映射表是公开的,因此并不能叫做加密算法。

Base64算法主要是将给定的字符以字符编码(如ASCII、UTF-8等)对应的十进制数为基准,做编码操作:

1. 将给定的字符串以字符为单位,转换为对应的字符编码。

2. 将获得的字符编码转换为二进制串。

3. 将获得的二进制串做分组转换操作,每3个8位的二进制串为一组,将这样的一组再转换为4个6位二进制串,不足6位时低位补0。

4. 对每组4个6位二进制串补位,即向6位二进制串的高位补两个0,生成4个8位二进制串。

5. 将获得的4-8二进制码转换为十进制码。

6. 将获得的十进制码用Base64字符映射表中对应的字符替换。

经过Base64编码后的数据会比原始数据略长,为原来的4/3倍,编码后的字符数是4的倍数。

编码后的字符串最多有2个补位的“=”,因为原始数据的二进制串的分组是以3个8位为一组的,余数 = 原始数据字节数 mod 3,余数只能为0、1、2。如果余数为0,3个8位转换为4个6位,高位补0之后是4个8位,则不需要补位符;如果余数为1,1个8位只能转换为2个6位,高位补0之后是2个8位,为了让编码之后的字符数是4的倍数,要补两个补位符;同理,如果余数为2,要补一个补位符。

ASCII码进行Base64编码的例子如下图,字符“A”编码之后的字符串为“QQ==”。

非ASCII码如GBK、UTF-8等编码,一个字符包含多个字节,如UTF-8用3个字节表示一个汉字,GBK用2个字节表示一个汉字。以字符串“密”为例,对应的UTF-8编码是-27、-81、-122,用Base64编码如下图,编码后的字符串为“5a+G”。

具体参考:Java加密与解密 - Base64算法 (这个好)     java加密解密研究3、Base64算法

三. Base64算法的实现

Java API中没有Base64的实现,实际上Sun也有Base64算法的实现,但是没有公布。Bouncy Castle提供了一般Base64算法的实现,Commons Codec提供了基于RFC 2045相关定义的Base64算法实现。

Bouncy Castle遵循的是一般Base64算法,就是根据字符映射表做了编码转换。Commons Codec中既支持RFC 2045定义的Base64算法,也支持一般的Base64算法。这两种的差异是RFC 2045定义的算法要求在编码后的字符串中换行和末尾添加回车换行符。

注:RFC 2045中规定,在电子邮件中,每行为76个字符,每行末需添加一个回车换行符("\r\n"),不管每行是否够76个字符,都要添加一个回车换行符。不过在实际应用中,根据实际需要,这一要求往往被忽略。

kSOAP中也提供Base64的实现,具体如下:

package org.kobjects.base64;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class Base64
{
  static final char[] charTab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();

  public static String encode(byte[] paramArrayOfByte)
  {
    return encode(paramArrayOfByte, 0, paramArrayOfByte.length, null).toString();
  }

  public static StringBuffer encode(byte[] paramArrayOfByte, int paramInt1, int paramInt2, StringBuffer paramStringBuffer)
  {
    if (paramStringBuffer == null)
      paramStringBuffer = new StringBuffer(paramArrayOfByte.length * 3 / 2);
    int i = paramInt2 - 3;
    int j = paramInt1;
    int k = 0;
    int l;
    while (j <= i)
    {
      l = (paramArrayOfByte[j] & 0xFF) << 16 | (paramArrayOfByte[(j + 1)] & 0xFF) << 8 | paramArrayOfByte[(j + 2)] & 0xFF;
      paramStringBuffer.append(charTab[(l >> 18 & 0x3F)]);
      paramStringBuffer.append(charTab[(l >> 12 & 0x3F)]);
      paramStringBuffer.append(charTab[(l >> 6 & 0x3F)]);
      paramStringBuffer.append(charTab[(l & 0x3F)]);
      j += 3;
      if (k++ < 14)
        continue;
      k = 0;
      paramStringBuffer.append("\r\n");
    }
    if (j == paramInt1 + paramInt2 - 2)
    {
      l = (paramArrayOfByte[j] & 0xFF) << 16 | (paramArrayOfByte[(j + 1)] & 0xFF) << 8;
      paramStringBuffer.append(charTab[(l >> 18 & 0x3F)]);
      paramStringBuffer.append(charTab[(l >> 12 & 0x3F)]);
      paramStringBuffer.append(charTab[(l >> 6 & 0x3F)]);
      paramStringBuffer.append("=");
    }
    else if (j == paramInt1 + paramInt2 - 1)
    {
      l = (paramArrayOfByte[j] & 0xFF) << 16;
      paramStringBuffer.append(charTab[(l >> 18 & 0x3F)]);
      paramStringBuffer.append(charTab[(l >> 12 & 0x3F)]);
      paramStringBuffer.append("==");
    }
    return paramStringBuffer;
  }

  static int decode(char paramChar)
  {
    if ((paramChar >= ‘A‘) && (paramChar <= ‘Z‘))
      return paramChar - ‘A‘;
    if ((paramChar >= ‘a‘) && (paramChar <= ‘z‘))
      return paramChar - ‘a‘ + 26;
    if ((paramChar >= ‘0‘) && (paramChar <= ‘9‘))
      return paramChar - ‘0‘ + 26 + 26;
    switch (paramChar)
    {
    case ‘+‘:
      return 62;
    case ‘/‘:
      return 63;
    case ‘=‘:
      return 0;
    }
    throw new RuntimeException("unexpected code: " + paramChar);
  }

  public static byte[] decode(String paramString)
  {
    ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
    try
    {
      decode(paramString, localByteArrayOutputStream);
    }
    catch (IOException localIOException)
    {
      throw new RuntimeException();
    }
    return localByteArrayOutputStream.toByteArray();
  }

  public static void decode(String paramString, OutputStream paramOutputStream)
    throws IOException
  {
    int i = 0;
    int j = paramString.length();
    while (true)
    {
      if ((i < j) && (paramString.charAt(i) <= ‘ ‘))
        ++i;
      if (i == j)
        return;
      int k = (decode(paramString.charAt(i)) << 18) + (decode(paramString.charAt(i + 1)) << 12) + (decode(paramString.charAt(i + 2)) << 6) + decode(paramString.charAt(i + 3));
      paramOutputStream.write(k >> 16 & 0xFF);
      if (paramString.charAt(i + 2) == ‘=‘)
        return;
      paramOutputStream.write(k >> 8 & 0xFF);
      if (paramString.charAt(i + 3) == ‘=‘)
        return;
      paramOutputStream.write(k & 0xFF);
      i += 4;
    }
  }
}
时间: 2024-08-05 07:06:53

Base64算法的相关文章

java单向加密算法小结(1)--Base64算法

java单向加密算法小结(1)--Base64算法 从这一篇起整理一下常见的加密算法以及在java中使用的demo,首先从最简单的开始. 简单了解 Base64严格来说并不是一种加密算法,而是一种编码/解码的实现方式. 我们都知道,数据在计算机网络之间是使用字节流的方式进行传递的,所有的信息都要最终转换为0101的二进制,这本身就涉及到编码,解码的应用. Base64,顾名思义,是使用了64个基本的字符来对任意数据进行编码的一种实现方式,那既然有Base64,是不是也有Base32,Base16

java Base64算法的使用

Base64是常见的网络加密算法,Base64编码可用于在HTTP环境下传递较长的标识信息.详见 Base64介绍 1 自定义的base64算法 Base64Encrypt.java public class Base64Encrypt { private static final String CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // base64解密 private

MD5加密算法(信息摘要算法)、Base64算法

1 什么是MD5 信息摘要算法,可以将字符进行加密,每个加密对象在进行加密后都是等长的 应用场景:将用户密码经过MD5加密后再存储到数据库中,这样即使是超级管理员也没有能力知道用户的具体密码是多少:因为MD5加密后的数据时不能够被解密的 步骤: >获取MessageDigest对象 MessageDigest md5 = MessageDigest.getInstance("MD5"); 注意:import java.security.MessageDigest; >通过M

算法系列(十一)BASE64算法实现和使用说明

完整的BASE64定义可见RFC 1421和RFC 2045.编码后的数据比原始数据略长,为原来的4/3.在电子邮件中,根据RFC 822规定,每76个字符,还需要加上一个回车换行.可以估算编码后数据长度大约为原长的135.1%. 转换的时候,将三个byte的数据,先后放入一个24bit的缓冲区中,先来的byte占高位.数据不足3byte的话,于缓冲器中剩下的bit用0补足.然后,每次取出6(因为26=64)个bit,按照其值选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg

标准的Base64算法描述

作者:zhanhailiang 日期:2014-12-19 算法描述: Base64要求把每三个8Bit的字节转换为四个6Bit的字节(38 = 46 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3. 第一个字符通过右移2位获得第一个目标字符的Base64表位置,根据这个数值取到表上相应的字符,就是第一个目标字符. 然后将第一个字符与0x03(00000011)进行与(&)操作并左移4位,接着第二个字符右移4位与前者相或(|)

Base64算法与多版本加密技术

编辑本段VB版 注:其中DigestStrToHexStr为可在程序外部调用加密函数 Option Explicit ' Base64 Encoding/Decoding Algorithm ' By: David Midkiff ([email protected]) ' ' This algorithms encodes and decodes data into Base64 ' format. This format is extremely more efficient than '

BASE64算法及应用

Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一.使用base64具有以下三个优点,一是信息隐藏加密,避免了明码传输带来的安全性问题,二是把二进制byte流转为可见字符传输,使得很适合在URL中传输,三是避免了在不同平台和不同处理器之间调整大小端和拼接数据的麻烦,具有一定的跨平台和跨编程语言的能力.对于一些不能处理二进制byte流的弱语言或者脚本语言来说,也提供了一个应用窗口.当然他的弱点也相当明显,使原始数据变的比以后大了三分之一. Base64的算法原理就是把三个字节24b

python实现base64算法加密

python本身有base64加密的模块,不过是用C写的,封装成了.so文件,无法查看源码,本着学习的心态,自己实现了一遍,算法 原理参考 浅谈Base64编码算法. 代码如下: # coding:utf8 import string import base64 # 编码用64位数组 因为是转换为6个字节的字符 所以64位就够了 letters = list(string.letters) + list(string.digits) + ['+', '/'] def my_base64_enco

传奇的通迅协议与base64算法

传奇的数据根本就没加密. 只要把#(数字)!去掉,中间的那些全部减0x3c,然后,四个字节一组,每个字节取低六位组成6*4=24位,然后分成3个字节,这个是很经典的base64的算法.不过小改了一些,主要是为了传输方便和解包方便,因为把255一个字节缩小到64一个字节,就可以使用其它编码作头和尾的标志. 最新的.net框架应该已经包含了这个算法了吧,直接使用相关函数就行,没必要自己再去编写了.原理你可以到网上查,BASE64编码的好处在于可以把例如汉字等任何文件编码,编码后的文件只包含26个字母