com.cloudhopper.commons.util.codec.Base64Codec Maven / Gradle / Ivy
package com.cloudhopper.commons.util.codec;
/*
* #%L
* ch-commons-util
* %%
* Copyright (C) 2012 Cloudhopper by Twitter
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.nio.ByteBuffer;
import java.text.ParseException;
/**
* Code originally copied from the OpenDS Java project.
*
* http://www.opends.org/
*
* This class removes the dependency on the Jakarta commons-codec library.
* Since this utility package is used in every Cloudhopper Java project,
* providing our own implementation (that's also much faster) allows the removal
* of a transitive dependency for every Cloudhopper Java project :-)
*
* This class provides methods for performing base64 encoding and decoding.
* Base64 is a mechanism for encoding binary data in ASCII form by converting
* sets of three bytes with eight significant bits each to sets of four bytes
* with six significant bits each.
*
* @author joelauer (twitter: @jjlauer or http://twitter.com/jjlauer)
*/
public class Base64Codec {
/**
* The set of characters that may be used in base64-encoded values.
*/
private static final char[] BASE64_ALPHABET =
("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
"0123456789+/").toCharArray();
/**
* Prevent instance creation.
*/
private Base64Codec() {
// No implementation required.
}
/**
* Encodes the provided raw data using base64.
* @param rawData The raw data to encode. It must not be null
.
* @return The base64-encoded representation of the provided raw data.
*/
public static String encode(byte[] rawData) {
StringBuilder buffer = new StringBuilder(4 * rawData.length / 3);
int pos = 0;
int iterations = rawData.length / 3;
for (int i = 0; i < iterations; i++) {
int value = ((rawData[pos++] & 0xFF) << 16) |
((rawData[pos++] & 0xFF) << 8) | (rawData[pos++] & 0xFF);
buffer.append(BASE64_ALPHABET[(value >>> 18) & 0x3F]);
buffer.append(BASE64_ALPHABET[(value >>> 12) & 0x3F]);
buffer.append(BASE64_ALPHABET[(value >>> 6) & 0x3F]);
buffer.append(BASE64_ALPHABET[value & 0x3F]);
}
switch (rawData.length % 3) {
case 1:
buffer.append(BASE64_ALPHABET[(rawData[pos] >>> 2) & 0x3F]);
buffer.append(BASE64_ALPHABET[(rawData[pos] << 4) & 0x3F]);
buffer.append("==");
break;
case 2:
int value = ((rawData[pos++] & 0xFF) << 8) | (rawData[pos] & 0xFF);
buffer.append(BASE64_ALPHABET[(value >>> 10) & 0x3F]);
buffer.append(BASE64_ALPHABET[(value >>> 4) & 0x3F]);
buffer.append(BASE64_ALPHABET[(value << 2) & 0x3F]);
buffer.append("=");
break;
}
return buffer.toString();
}
/**
* Decodes the provided set of base64-encoded data.
* @param encodedData The base64-encoded data to decode. It must not be
* null
.
* @return The decoded raw data.
* @throws ParseException If a problem occurs while attempting to decode the
* provided data.
*/
public static byte[] decode(String encodedData) throws ParseException {
// The encoded value must have length that is a multiple of four bytes.
int length = encodedData.length();
if ((length % 4) != 0) {
//Message message = ERR_BASE64_DECODE_INVALID_LENGTH.get(encodedData);
throw new ParseException("Base64 data was not 4-byte aligned", 0);
}
ByteBuffer buffer = ByteBuffer.allocate(length);
for (int i = 0; i < length; i += 4) {
boolean append = true;
int value = 0;
for (int j = 0; j < 4; j++) {
switch (encodedData.charAt(i + j)) {
case 'A':
value <<= 6;
break;
case 'B':
value = (value << 6) | 0x01;
break;
case 'C':
value = (value << 6) | 0x02;
break;
case 'D':
value = (value << 6) | 0x03;
break;
case 'E':
value = (value << 6) | 0x04;
break;
case 'F':
value = (value << 6) | 0x05;
break;
case 'G':
value = (value << 6) | 0x06;
break;
case 'H':
value = (value << 6) | 0x07;
break;
case 'I':
value = (value << 6) | 0x08;
break;
case 'J':
value = (value << 6) | 0x09;
break;
case 'K':
value = (value << 6) | 0x0A;
break;
case 'L':
value = (value << 6) | 0x0B;
break;
case 'M':
value = (value << 6) | 0x0C;
break;
case 'N':
value = (value << 6) | 0x0D;
break;
case 'O':
value = (value << 6) | 0x0E;
break;
case 'P':
value = (value << 6) | 0x0F;
break;
case 'Q':
value = (value << 6) | 0x10;
break;
case 'R':
value = (value << 6) | 0x11;
break;
case 'S':
value = (value << 6) | 0x12;
break;
case 'T':
value = (value << 6) | 0x13;
break;
case 'U':
value = (value << 6) | 0x14;
break;
case 'V':
value = (value << 6) | 0x15;
break;
case 'W':
value = (value << 6) | 0x16;
break;
case 'X':
value = (value << 6) | 0x17;
break;
case 'Y':
value = (value << 6) | 0x18;
break;
case 'Z':
value = (value << 6) | 0x19;
break;
case 'a':
value = (value << 6) | 0x1A;
break;
case 'b':
value = (value << 6) | 0x1B;
break;
case 'c':
value = (value << 6) | 0x1C;
break;
case 'd':
value = (value << 6) | 0x1D;
break;
case 'e':
value = (value << 6) | 0x1E;
break;
case 'f':
value = (value << 6) | 0x1F;
break;
case 'g':
value = (value << 6) | 0x20;
break;
case 'h':
value = (value << 6) | 0x21;
break;
case 'i':
value = (value << 6) | 0x22;
break;
case 'j':
value = (value << 6) | 0x23;
break;
case 'k':
value = (value << 6) | 0x24;
break;
case 'l':
value = (value << 6) | 0x25;
break;
case 'm':
value = (value << 6) | 0x26;
break;
case 'n':
value = (value << 6) | 0x27;
break;
case 'o':
value = (value << 6) | 0x28;
break;
case 'p':
value = (value << 6) | 0x29;
break;
case 'q':
value = (value << 6) | 0x2A;
break;
case 'r':
value = (value << 6) | 0x2B;
break;
case 's':
value = (value << 6) | 0x2C;
break;
case 't':
value = (value << 6) | 0x2D;
break;
case 'u':
value = (value << 6) | 0x2E;
break;
case 'v':
value = (value << 6) | 0x2F;
break;
case 'w':
value = (value << 6) | 0x30;
break;
case 'x':
value = (value << 6) | 0x31;
break;
case 'y':
value = (value << 6) | 0x32;
break;
case 'z':
value = (value << 6) | 0x33;
break;
case '0':
value = (value << 6) | 0x34;
break;
case '1':
value = (value << 6) | 0x35;
break;
case '2':
value = (value << 6) | 0x36;
break;
case '3':
value = (value << 6) | 0x37;
break;
case '4':
value = (value << 6) | 0x38;
break;
case '5':
value = (value << 6) | 0x39;
break;
case '6':
value = (value << 6) | 0x3A;
break;
case '7':
value = (value << 6) | 0x3B;
break;
case '8':
value = (value << 6) | 0x3C;
break;
case '9':
value = (value << 6) | 0x3D;
break;
case '+':
value = (value << 6) | 0x3E;
break;
case '/':
value = (value << 6) | 0x3F;
break;
case '=':
append = false;
switch (j) {
case 2:
buffer.put((byte) ((value >>> 4) & 0xFF));
break;
case 3:
buffer.put((byte) ((value >>> 10) & 0xFF));
buffer.put((byte) ((value >>> 2) & 0xFF));
break;
}
break;
default:
//Message message = ERR_BASE64_DECODE_INVALID_CHARACTER.get(encodedData, encodedData.charAt(i + j));
throw new ParseException("Invalid Base64 character '" + encodedData.charAt(i + j) + "'", i+j);
}
if (!append) {
break;
}
}
if (append) {
buffer.put((byte) ((value >>> 16) & 0xFF));
buffer.put((byte) ((value >>> 8) & 0xFF));
buffer.put((byte) (value & 0xFF));
} else {
break;
}
}
buffer.flip();
byte[] returnArray = new byte[buffer.limit()];
buffer.get(returnArray);
return returnArray;
}
}