Base64加密的方法是:将字节(bytes)序列转换为字符(character)序列,每3个字节转换为4个字符(3 * 8 = 4 * 6),像这样:|11111122|22223333|33444444| >> |00111111|00222222|00333333|00444444|,不足三个字节的用'='填充,所以最后的字符序列数一定是4的倍数。解密就是加密的逆过程咯!
下面的Java代码有三种实现方式:
Simple:最简单直接的实现方法;
Grace:比较优雅的实现方法;
Official:JDK自带的实现方法;
首先定义接口:
package com.hp.it.mossad.algorithms.base64;
public interface IBase64 {
String encode(byte[] code);
String decode(char[] code);
}
然后是实现类:
package com.hp.it.mossad.algorithms.base64;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidParameterException;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public enum Base64Type {
GRACE(new IBase64() {
public String encode(byte[] data) {
int length = (int)Math.ceil(data.length / 3.0) * 4;
char[] out = new char[length];
for (int i = 0, j = 0; i < data.length ; i += 3, j += 4) {
boolean quad = false;
boolean trip = false;
int val = 0xff & data[i];
val <<= 8;
if (i + 1 < data.length) {
val |= 0xff & data[i + 1];
trip = true;
}
val <<= 8;
if (i + 2 < data.length) {
val |= 0xff & data[i + 2];
quad = true;
}
out[j + 3] = alphabet[quad ? (0x3f & val) : 64];
val >>= 6;
out[j + 2] = alphabet[trip ? (0x3f & val) : 64];
val >>= 6;
out[j + 1] = alphabet[0x3f & val];
val >>= 6;
out[j] = alphabet[0x3f & val];
}
return new String(out);
}
public String decode(char[] data) {
if (data.length % 4 != 0) throw new InvalidParameterException("Invalid length of array : " + data.length);
int length = data.length / 4 * 3;
if (data.length > 0 && data[data.length - 1] == '=') length--;
if (data.length > 1 && data[data.length - 2] == '=') length--;
byte[] out = new byte[length];
int shift = 0;
int valley = 0x00;
int index = 0;
for (int i = 0; i < data.length; i++) {
int value = codes[data[i]];
if (value >= 0) {
valley <<= 6;
valley |= value;
shift += 6;
if (shift >= 8) {
shift -= 8;
out[index++] = (byte)((valley >> shift) & 0xff);
}
}
}
return new String(out);
}
}),
SIMPLE(new IBase64() {
public String encode(byte[] bs) {
int count = bs.length / 3;
int rm = bs.length % 3;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < count; i++) {
int index = i * 3;
baos.write(alphabet[(bs[index] & 0xfc) >> 2]);
baos.write(alphabet[((bs[index] & 0x03) << 4 | (bs[index + 1] & 0xf0) >> 4)]);
baos.write(alphabet[(bs[index + 1] & 0x0f) << 2 | (bs[index + 2] & 0xc0) >> 6]);
baos.write(alphabet[bs[index + 2] & 0x3f]);
}
if (rm == 1) {
baos.write(alphabet[(bs[bs.length - 1] & 0xfc) >> 2]);
baos.write(alphabet[(bs[bs.length - 1] & 0x03) << 4]);
baos.write('=');
baos.write('=');
} else if (rm == 2) {
baos.write(alphabet[(bs[bs.length - 2] & 0xfc) >> 2]);
baos.write(alphabet[(bs[bs.length - 2] & 0x03) << 4 | (bs[bs.length - 1] & 0xf0) >> 4]);
baos.write(alphabet[(bs[bs.length - 1] & 0x0f) << 2]);
baos.write('=');
}
return baos.toString();
}
public String decode(char[] data) {
if (data.length % 4 != 0) throw new InvalidParameterException("The lengh is invalid!");
int count = data.length / 4;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < count - 1; i++) {
int index = i * 4;
baos.write((codes[data[index]] & 0x3f) << 2 | (codes[data[index + 1]] & 0x30) >> 4);
baos.write((codes[data[index + 1]] & 0x0f) << 4 | (codes[data[index + 2]] & 0x3c) >> 2);
baos.write((codes[data[index + 2]] & 0x03) << 6 | codes[data[index + 3]] & 0x3f);
}
if (data[data.length - 2] == '=') {
baos.write((codes[data[data.length - 4]] & 0x3f) << 2 | ((codes[data[data.length - 3]] & 0x30) >> 4));
} else if (data[data.length - 1] == '=') {
baos.write((codes[data[data.length - 4]] & 0x3f) << 2 | ((codes[data[data.length - 3]] & 0x30) >> 4));
baos.write((codes[data[data.length - 3]] & 0x0f) << 4 | ((codes[data[data.length - 2]] & 0x3c)) >> 2);
}
return baos.toString();
}
}),
OFFICIAL(new IBase64() {
@Override
public String encode(byte[] code) {
return encoder.encode(code);
}
@Override
public String decode(char[] code) {
try {
return new String(decoder.decodeBuffer(new String(code)));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private BASE64Encoder encoder = new BASE64Encoder();
private BASE64Decoder decoder = new BASE64Decoder();
});
private Base64Type(IBase64 ib) {
this.ib = ib;
}
private IBase64 ib;
public String encode(byte[] code) {
return this.ib.encode(code);
}
public String decode(char[] code) {
return this.ib.decode(code);
}
private static char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();
private static byte[] codes = new byte[128];
static {
for (int i = 0; i < codes.length; i++) {
codes[i] = -1;
}
for (int i = 'A'; i <= 'Z'; i++) {
codes[i] = (byte)(i - 'A');
}
for (int i = 'a'; i <= 'z'; i++) {
codes[i] = (byte)(26 + i - 'a');
}
for (int i = '0'; i <= '9'; i++) {
codes[i] = (byte)(52 + i - '0');
}
codes['+'] = 62;
codes['/'] = 63;
}
}
最后是测试类:
package com.hp.it.mossad.algorithms.base64;
import org.junit.Test;
public class Base64Test {
@Test
public void grace() {
test(Base64Type.GRACE);
}
@Test
public void simple() {
test(Base64Type.SIMPLE);
}
@Test
public void official() {
test(Base64Type.OFFICIAL);
}
private void test(Base64Type type) {
String s = "Keep calm and carry on.";
String encode = type.encode(s.getBytes());
String decode = type.decode(encode.toCharArray());
System.out.println(type.name() + " : " + s.equals(decode));
}
}