一、前言
关于apache的commons-net包中实现的org.apache.commons.net.util.Base64实现base/base64加解密encode/decode算法,具体如下所示
二、源码说明
package org.apache.commons.net.util;@b@@b@import java.io.UnsupportedEncodingException;@b@import java.math.BigInteger;@b@@b@public class Base64@b@{@b@ private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2;@b@ private static final int DEFAULT_BUFFER_SIZE = 8192;@b@ static final int CHUNK_SIZE = 76;@b@ static final byte[] CHUNK_SEPARATOR = { 13, 10 };@b@ private static final byte[] STANDARD_ENCODE_TABLE = { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47 };@b@ private static final byte[] URL_SAFE_ENCODE_TABLE = { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 95 };@b@ private static final byte PAD = 61;@b@ private static final byte[] DECODE_TABLE = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };@b@ private static final int MASK_6BITS = 63;@b@ private static final int MASK_8BITS = 255;@b@ private final byte[] encodeTable;@b@ private final int lineLength;@b@ private final byte[] lineSeparator;@b@ private final int decodeSize;@b@ private final int encodeSize;@b@ private byte[] buffer;@b@ private int pos;@b@ private int readPos;@b@ private int currentLinePos;@b@ private int modulus;@b@ private boolean eof;@b@ private int x;@b@@b@ public Base64()@b@ {@b@ this(false);@b@ }@b@@b@ public Base64(boolean urlSafe)@b@ {@b@ this(76, CHUNK_SEPARATOR, urlSafe);@b@ }@b@@b@ public Base64(int lineLength)@b@ {@b@ this(lineLength, CHUNK_SEPARATOR);@b@ }@b@@b@ public Base64(int lineLength, byte[] lineSeparator)@b@ {@b@ this(lineLength, lineSeparator, false);@b@ }@b@@b@ public Base64(int lineLength, byte[] lineSeparator, boolean urlSafe)@b@ {@b@ if (lineSeparator == null) {@b@ lineLength = 0;@b@ lineSeparator = CHUNK_SEPARATOR;@b@ }@b@ this.lineLength = ((lineLength > 0) ? lineLength / 4 * 4 : 0);@b@ this.lineSeparator = new byte[lineSeparator.length];@b@ System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);@b@ if (lineLength > 0)@b@ this.encodeSize = (4 + lineSeparator.length);@b@ else@b@ this.encodeSize = 4;@b@@b@ this.decodeSize = (this.encodeSize - 1);@b@ if (containsBase64Byte(lineSeparator)) {@b@ String sep = newStringUtf8(lineSeparator);@b@ throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]");@b@ }@b@ this.encodeTable = ((urlSafe) ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE);@b@ }@b@@b@ public boolean isUrlSafe()@b@ {@b@ return (this.encodeTable == URL_SAFE_ENCODE_TABLE);@b@ }@b@@b@ boolean hasData()@b@ {@b@ return (this.buffer != null);@b@ }@b@@b@ int avail()@b@ {@b@ return ((this.buffer != null) ? this.pos - this.readPos : 0);@b@ }@b@@b@ private void resizeBuffer()@b@ {@b@ if (this.buffer == null) {@b@ this.buffer = new byte[8192];@b@ this.pos = 0;@b@ this.readPos = 0;@b@ } else {@b@ byte[] b = new byte[this.buffer.length * 2];@b@ System.arraycopy(this.buffer, 0, b, 0, this.buffer.length);@b@ this.buffer = b;@b@ }@b@ }@b@@b@ int readResults(byte[] b, int bPos, int bAvail)@b@ {@b@ if (this.buffer != null) {@b@ int len = Math.min(avail(), bAvail);@b@ if (this.buffer != b) {@b@ System.arraycopy(this.buffer, this.readPos, b, bPos, len);@b@ this.readPos += len;@b@ if (this.readPos >= this.pos)@b@ this.buffer = null;@b@@b@ }@b@ else@b@ {@b@ this.buffer = null;@b@ }@b@ return len;@b@ }@b@ return ((this.eof) ? -1 : 0);@b@ }@b@@b@ void setInitialBuffer(byte[] out, int outPos, int outAvail)@b@ {@b@ if ((out != null) && (out.length == outAvail)) {@b@ this.buffer = out;@b@ this.pos = outPos;@b@ this.readPos = outPos;@b@ }@b@ }@b@@b@ void encode(byte[] in, int inPos, int inAvail)@b@ {@b@ int i;@b@ if (this.eof) {@b@ return;@b@ }@b@@b@ if (inAvail < 0) {@b@ this.eof = true;@b@ if ((this.buffer == null) || (this.buffer.length - this.pos < this.encodeSize))@b@ resizeBuffer();@b@@b@ switch (this.modulus)@b@ {@b@ case 1:@b@ this.buffer[(this.pos++)] = this.encodeTable[(this.x >> 2 & 0x3F)];@b@ this.buffer[(this.pos++)] = this.encodeTable[(this.x << 4 & 0x3F)];@b@@b@ if (this.encodeTable == STANDARD_ENCODE_TABLE) {@b@ this.buffer[(this.pos++)] = 61;@b@ this.buffer[(this.pos++)] = 61; } break;@b@ case 2:@b@ this.buffer[(this.pos++)] = this.encodeTable[(this.x >> 10 & 0x3F)];@b@ this.buffer[(this.pos++)] = this.encodeTable[(this.x >> 4 & 0x3F)];@b@ this.buffer[(this.pos++)] = this.encodeTable[(this.x << 2 & 0x3F)];@b@@b@ if (this.encodeTable == STANDARD_ENCODE_TABLE)@b@ this.buffer[(this.pos++)] = 61;@b@@b@ }@b@@b@ if ((this.lineLength > 0) && (this.pos > 0)) {@b@ System.arraycopy(this.lineSeparator, 0, this.buffer, this.pos, this.lineSeparator.length);@b@ this.pos += this.lineSeparator.length;@b@ }@b@ } else {@b@ for (i = 0; i < inAvail; ++i) {@b@ if ((this.buffer == null) || (this.buffer.length - this.pos < this.encodeSize))@b@ resizeBuffer();@b@@b@ this.modulus = (++this.modulus % 3);@b@ int b = in[(inPos++)];@b@ if (b < 0)@b@ b += 256;@b@@b@ this.x = ((this.x << 8) + b);@b@ if (0 == this.modulus) {@b@ this.buffer[(this.pos++)] = this.encodeTable[(this.x >> 18 & 0x3F)];@b@ this.buffer[(this.pos++)] = this.encodeTable[(this.x >> 12 & 0x3F)];@b@ this.buffer[(this.pos++)] = this.encodeTable[(this.x >> 6 & 0x3F)];@b@ this.buffer[(this.pos++)] = this.encodeTable[(this.x & 0x3F)];@b@ this.currentLinePos += 4;@b@ if ((this.lineLength > 0) && (this.lineLength <= this.currentLinePos)) {@b@ System.arraycopy(this.lineSeparator, 0, this.buffer, this.pos, this.lineSeparator.length);@b@ this.pos += this.lineSeparator.length;@b@ this.currentLinePos = 0;@b@ }@b@ }@b@ }@b@ }@b@ }@b@@b@ void decode(byte[] in, int inPos, int inAvail)@b@ {@b@ if (this.eof)@b@ return;@b@@b@ if (inAvail < 0)@b@ this.eof = true;@b@@b@ for (int i = 0; i < inAvail; ++i) {@b@ if ((this.buffer == null) || (this.buffer.length - this.pos < this.decodeSize))@b@ resizeBuffer();@b@@b@ byte b = in[(inPos++)];@b@ if (b == 61)@b@ {@b@ this.eof = true;@b@ break;@b@ }@b@ if ((b >= 0) && (b < DECODE_TABLE.length)) {@b@ int result = DECODE_TABLE[b];@b@ if (result >= 0) {@b@ this.modulus = (++this.modulus % 4);@b@ this.x = ((this.x << 6) + result);@b@ if (this.modulus == 0) {@b@ this.buffer[(this.pos++)] = (byte)(this.x >> 16 & 0xFF);@b@ this.buffer[(this.pos++)] = (byte)(this.x >> 8 & 0xFF);@b@ this.buffer[(this.pos++)] = (byte)(this.x & 0xFF);@b@ }@b@@b@ }@b@@b@ }@b@@b@ }@b@@b@ if ((this.eof) && (this.modulus != 0)) {@b@ this.x <<= 6;@b@ switch (this.modulus)@b@ {@b@ case 2:@b@ this.x <<= 6;@b@ this.buffer[(this.pos++)] = (byte)(this.x >> 16 & 0xFF);@b@ break;@b@ case 3:@b@ this.buffer[(this.pos++)] = (byte)(this.x >> 16 & 0xFF);@b@ this.buffer[(this.pos++)] = (byte)(this.x >> 8 & 0xFF);@b@ }@b@ }@b@ }@b@@b@ public static boolean isBase64(byte octet)@b@ {@b@ return ((octet == 61) || ((octet >= 0) && (octet < DECODE_TABLE.length) && (DECODE_TABLE[octet] != -1)));@b@ }@b@@b@ public static boolean isArrayByteBase64(byte[] arrayOctet)@b@ {@b@ for (int i = 0; i < arrayOctet.length; ++i)@b@ if ((!(isBase64(arrayOctet[i]))) && (!(isWhiteSpace(arrayOctet[i]))))@b@ return false;@b@@b@@b@ return true;@b@ }@b@@b@ private static boolean containsBase64Byte(byte[] arrayOctet)@b@ {@b@ for (int i = 0; i < arrayOctet.length; ++i)@b@ if (isBase64(arrayOctet[i]))@b@ return true;@b@@b@@b@ return false;@b@ }@b@@b@ public static byte[] encodeBase64(byte[] binaryData)@b@ {@b@ return encodeBase64(binaryData, false);@b@ }@b@@b@ public static String encodeBase64String(byte[] binaryData)@b@ {@b@ return newStringUtf8(encodeBase64(binaryData, true));@b@ }@b@@b@ public static byte[] encodeBase64URLSafe(byte[] binaryData)@b@ {@b@ return encodeBase64(binaryData, false, true);@b@ }@b@@b@ public static String encodeBase64URLSafeString(byte[] binaryData)@b@ {@b@ return newStringUtf8(encodeBase64(binaryData, false, true));@b@ }@b@@b@ public static byte[] encodeBase64Chunked(byte[] binaryData)@b@ {@b@ return encodeBase64(binaryData, true);@b@ }@b@@b@ public Object decode(Object pObject)@b@ {@b@ if (pObject instanceof byte[])@b@ return decode((byte[])(byte[])pObject);@b@ if (pObject instanceof String)@b@ return decode((String)pObject);@b@@b@ throw new RuntimeException("Parameter supplied to Base64 decode is not a byte[] or a String");@b@ }@b@@b@ public byte[] decode(String pArray)@b@ {@b@ return decode(getBytesUtf8(pArray));@b@ }@b@@b@ private byte[] getBytesUtf8(String pArray) {@b@ try {@b@ return pArray.getBytes("UTF8");@b@ } catch (UnsupportedEncodingException e) {@b@ throw new RuntimeException(e);@b@ }@b@ }@b@@b@ public byte[] decode(byte[] pArray)@b@ {@b@ reset();@b@ if ((pArray == null) || (pArray.length == 0))@b@ return pArray;@b@@b@ long len = pArray.length * 3 / 4;@b@ byte[] buf = new byte[(int)len];@b@ setInitialBuffer(buf, 0, buf.length);@b@ decode(pArray, 0, pArray.length);@b@ decode(pArray, 0, -1);@b@@b@ byte[] result = new byte[this.pos];@b@ readResults(result, 0, result.length);@b@ return result;@b@ }@b@@b@ public static byte[] encodeBase64(byte[] binaryData, boolean isChunked)@b@ {@b@ return encodeBase64(binaryData, isChunked, false);@b@ }@b@@b@ public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe)@b@ {@b@ return encodeBase64(binaryData, isChunked, urlSafe, 2147483647);@b@ }@b@@b@ public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe, int maxResultSize)@b@ {@b@ if ((binaryData == null) || (binaryData.length == 0)) {@b@ return binaryData;@b@ }@b@@b@ long len = getEncodeLength(binaryData, 76, CHUNK_SEPARATOR);@b@ if (len > maxResultSize) {@b@ throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + len + ") than the specified maxium size of " + maxResultSize);@b@ }@b@@b@ Base64 b64 = new Base64(0, CHUNK_SEPARATOR, urlSafe);@b@ return b64.encode(binaryData);@b@ }@b@@b@ public static byte[] decodeBase64(String base64String)@b@ {@b@ return new Base64().decode(base64String);@b@ }@b@@b@ public static byte[] decodeBase64(byte[] base64Data)@b@ {@b@ return new Base64().decode(base64Data);@b@ }@b@@b@ private static boolean isWhiteSpace(byte byteToCheck)@b@ {@b@ switch (byteToCheck)@b@ {@b@ case 9:@b@ case 10:@b@ case 13:@b@ case 32:@b@ return true;@b@ }@b@ return false;@b@ }@b@@b@ public Object encode(Object pObject)@b@ {@b@ if (!(pObject instanceof byte[]))@b@ throw new RuntimeException("Parameter supplied to Base64 encode is not a byte[]");@b@@b@ return encode((byte[])(byte[])pObject);@b@ }@b@@b@ public String encodeToString(byte[] pArray)@b@ {@b@ return newStringUtf8(encode(pArray));@b@ }@b@@b@ private static String newStringUtf8(byte[] encode) {@b@ String str = null;@b@ try {@b@ str = new String(encode, "UTF8");@b@ } catch (UnsupportedEncodingException ue) {@b@ throw new RuntimeException(ue);@b@ }@b@ return str;@b@ }@b@@b@ public byte[] encode(byte[] pArray)@b@ {@b@ reset();@b@ if ((pArray == null) || (pArray.length == 0))@b@ return pArray;@b@@b@ long len = getEncodeLength(pArray, this.lineLength, this.lineSeparator);@b@ byte[] buf = new byte[(int)len];@b@ setInitialBuffer(buf, 0, buf.length);@b@ encode(pArray, 0, pArray.length);@b@ encode(pArray, 0, -1);@b@@b@ if (this.buffer != buf) {@b@ readResults(buf, 0, buf.length);@b@ }@b@@b@ if ((isUrlSafe()) && (this.pos < buf.length)) {@b@ byte[] smallerBuf = new byte[this.pos];@b@ System.arraycopy(buf, 0, smallerBuf, 0, this.pos);@b@ buf = smallerBuf;@b@ }@b@ return buf;@b@ }@b@@b@ private static long getEncodeLength(byte[] pArray, int chunkSize, byte[] chunkSeparator)@b@ {@b@ chunkSize = chunkSize / 4 * 4;@b@@b@ long len = pArray.length * 4 / 3;@b@ long mod = len % 4L;@b@ if (mod != 0L)@b@ len += 4L - mod;@b@@b@ if (chunkSize > 0) {@b@ boolean lenChunksPerfectly = len % chunkSize == 0L;@b@ len += len / chunkSize * chunkSeparator.length;@b@ if (!(lenChunksPerfectly))@b@ len += chunkSeparator.length;@b@ }@b@@b@ return len;@b@ }@b@@b@ public static BigInteger decodeInteger(byte[] pArray)@b@ {@b@ return new BigInteger(1, decodeBase64(pArray));@b@ }@b@@b@ public static byte[] encodeInteger(BigInteger bigInt)@b@ {@b@ if (bigInt == null)@b@ throw new NullPointerException("encodeInteger called with null parameter");@b@@b@ return encodeBase64(toIntegerBytes(bigInt), false);@b@ }@b@@b@ static byte[] toIntegerBytes(BigInteger bigInt)@b@ {@b@ int bitlen = bigInt.bitLength();@b@@b@ bitlen = bitlen + 7 >> 3 << 3;@b@ byte[] bigBytes = bigInt.toByteArray();@b@@b@ if ((bigInt.bitLength() % 8 != 0) && (bigInt.bitLength() / 8 + 1 == bitlen / 8)) {@b@ return bigBytes;@b@ }@b@@b@ int startSrc = 0;@b@ int len = bigBytes.length;@b@@b@ if (bigInt.bitLength() % 8 == 0) {@b@ startSrc = 1;@b@ --len;@b@ }@b@ int startDst = bitlen / 8 - len;@b@ byte[] resizedBytes = new byte[bitlen / 8];@b@ System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);@b@ return resizedBytes;@b@ }@b@@b@ private void reset()@b@ {@b@ this.buffer = null;@b@ this.pos = 0;@b@ this.readPos = 0;@b@ this.currentLinePos = 0;@b@ this.modulus = 0;@b@ this.eof = false;@b@ }@b@}