package com.malcom.library.android.utils.encoding.base64; /* * Copyright (C) 2001-2007 Stephen Ostermiller * http://ostermiller.org/contact.pl?regarding=Java+Utilities * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * See COPYING.TXT for details. */ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.text.MessageFormat; /** * Implements Base64 encoding and decoding as defined by RFC 2045: * "Multi-purpose Internet Mail Extensions (MIME) Part One: Format of Internet * Message Bodies" page 23. More information about this class is available from * <a target="_top" href= * "http://ostermiller.org/utils/Base64.html">ostermiller.org</a>. * * <blockquote> * <p> * The Base64 Content-Transfer-Encoding is designed to represent arbitrary * sequences of octets in a form that need not be humanly readable. The encoding * and decoding algorithms are simple, but the encoded data are consistently * only about 33 percent larger than the unencoded data. This encoding is * virtually identical to the one used in Privacy Enhanced Mail (PEM) * applications, as defined in RFC 1421. * </p> * * <p> * A 65-character subset of US-ASCII is used, enabling 6 bits to be represented * per printable character. (The extra 65th character, "=", is used to signify a * special processing function.) * </p> * * <p> * NOTE: This subset has the important property that it is represented * identically in all versions of ISO 646, including US-ASCII, and all * characters in the subset are also represented identically in all versions of * EBCDIC. Other popular encodings, such as the encoding used by the uuencode * utility, MacIntosh binhex 4.0 [RFC-1741], and the base85 encoding specified * as part of Level 2 PostScript, do no share these properties, and thus do not * fulfill the portability requirements a binary transport encoding for mail * must meet. * </p> * * <p> * The encoding process represents 24-bit groups of input bits as output strings * of 4 encoded characters. Proceeding from left to right, a 24-bit input group * is formed by concatenating 3 8bit input groups. These 24 bits are then * treated as 4 concatenated 6-bit groups, each of which is translated into a * single digit in the base64 alphabet. When encoding a bit stream via the * base64 encoding, the bit stream must be presumed to be ordered with the * most-significant-bit first. That is, the first bit in the stream will be the * high-order bit in the first 8bit byte, and the eighth bit will be the * low-order bit in the first 8bit byte, and so on. * </p> * * <p> * Each 6-bit group is used as an index into an array of 64 printable * characters. The character referenced by the index is placed in the output * string. These characters, identified in Table 1, below, are selected so as to * be universally representable, and the set excludes characters with particular * significance to SMTP (e.g., ".", CR, LF) and to the multi-part boundary * delimiters defined in RFC 2046 (e.g., "-"). * </p> * * <pre> * Table 1: The Base64 Alphabet * * Value Encoding Value Encoding Value Encoding Value Encoding * 0 A 17 R 34 i 51 z * 1 B 18 S 35 j 52 0 * 2 C 19 T 36 k 53 1 * 3 D 20 U 37 l 54 2 * 4 E 21 V 38 m 55 3 * 5 F 22 W 39 n 56 4 * 6 G 23 X 40 o 57 5 * 7 H 24 Y 41 p 58 6 * 8 I 25 Z 42 q 59 7 * 9 J 26 a 43 r 60 8 * 10 K 27 b 44 s 61 9 * 11 L 28 c 45 t 62 + * 12 M 29 d 46 u 63 / * 13 N 30 e 47 v * 14 O 31 f 48 w (pad) = * 15 P 32 g 49 x * 16 Q 33 h 50 y * </pre> * <p> * The encoded output stream must be represented in lines of no more than 76 * characters each. All line breaks or other characters no found in Table 1 must * be ignored by decoding software. In base64 data, characters other than those * in Table 1, line breaks, and other white space probably indicate a * transmission error, about which a warning message or even a message rejection * might be appropriate under some circumstances. * </p> * * <p> * Special processing is performed if fewer than 24 bits are available at the * end of the data being encoded. A full encoding quantum is always completed at * the end of a body. When fewer than 24 input bits are available in an input * group, zero bits are added (on the right) to form an integral number of 6-bit * groups. Padding at the end of the data is performed using the "=" character. * Since all base64 input is an integral number of octets, only the following * cases can arise: (1) the final quantum of encoding input is an integral * multiple of 24 bits; here, the final unit of encoded output will be an * integral multiple of 4 characters with no "=" padding, (2) the final quantum * of encoding input is exactly 8 bits; here, the final unit of encoded output * will be two characters followed by two "=" padding characters, or (3) the * final quantum of encoding input is exactly 16 bits; here, the final unit of * encoded output will be three characters followed by one "=" padding * character. * </p> * * <p> * Because it is used only for padding at the end of the data, the occurrence of * any "=" characters may be taken as evidence that the end of the data has been * reached (without truncation in transit). No such assurance is possible, * however, when the number of octets transmitted was a multiple of three and no * "=" characters are present. * </p> * * <p> * Any characters outside of the base64 alphabet are to be ignored in * base64-encoded data. * </p> * * <p> * Care must be taken to use the proper octets for line breaks if base64 * encoding is applied directly to text material that has not been converted to * canonical form. In particular, text line breaks must be converted into CRLF * sequences prior to base64 encoding. The important thing to note is that this * may be done directly by the encoder rather than in a prior canonicalization * step in some implementations. * </p> * * <p> * NOTE: There is no need to worry about quoting potential boundary delimiters * within base64-encoded bodies within multi-part entities because no hyphen * characters are used in the base64 encoding. * </p> * </blockquote> * * @author Stephen Ostermiller * http://ostermiller.org/contact.pl?regarding=Java+Utilities * @since ostermillerutils 1.00.00 */ public class Base64 { /** * Symbol that represents the end of an input stream * * @since ostermillerutils 1.00.00 */ private static final int END_OF_INPUT = -1; /** * A character that is not a valid base 64 character. * * @since ostermillerutils 1.00.00 */ private static final int NON_BASE_64 = -1; /** * A character that is not a valid base 64 character. * * @since ostermillerutils 1.00.00 */ private static final int NON_BASE_64_WHITESPACE = -2; /** * A character that is not a valid base 64 character. * * @since ostermillerutils 1.00.00 */ private static final int NON_BASE_64_PADDING = -3; /** * This class need not be instantiated, all methods are static. * * @since ostermillerutils 1.00.00 */ private Base64() { // should not be called } /** * Table of the sixty-four characters that are used as the Base64 alphabet: * [a-z0-9A-Z+/] * * @since ostermillerutils 1.00.00 */ protected static final byte[] base64Chars = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', }; /** * Reverse lookup table for the Base64 alphabet. reversebase64Chars[byte] * gives n for the nth Base64 character or negative if a character is not a * Base64 character. * * @since ostermillerutils 1.00.00 */ protected static final byte[] reverseBase64Chars = new byte[0x100]; static { // Fill in NON_BASE_64 for all characters to start with for (int i = 0; i < reverseBase64Chars.length; i++) { reverseBase64Chars[i] = NON_BASE_64; } // For characters that are base64Chars, adjust // the reverse lookup table. for (byte i = 0; i < base64Chars.length; i++) { reverseBase64Chars[base64Chars[i]] = i; } reverseBase64Chars[' '] = NON_BASE_64_WHITESPACE; reverseBase64Chars['\n'] = NON_BASE_64_WHITESPACE; reverseBase64Chars['\r'] = NON_BASE_64_WHITESPACE; reverseBase64Chars['\t'] = NON_BASE_64_WHITESPACE; reverseBase64Chars['\f'] = NON_BASE_64_WHITESPACE; reverseBase64Chars['='] = NON_BASE_64_PADDING; } /** * Version number of this program * * @since ostermillerutils 1.00.00 */ public static final String version = "1.2"; /** * Encode a String in Base64. The String is converted to and from bytes * according to the platform's default character encoding. No line breaks or * other white space are inserted into the encoded data. * * @param string * The data to encode. * @return An encoded String. * * @since ostermillerutils 1.00.00 */ public static String encode(String string) { return new String(encode(string.getBytes())); } /** * Encode a String in Base64. No line breaks or other white space are * inserted into the encoded data. * * @param string * The data to encode. * @param enc * Character encoding to use when converting to and from bytes. * @throws UnsupportedEncodingException * if the character encoding specified is not supported. * @return An encoded String. * * @since ostermillerutils 1.00.00 */ public static String encode(String string, String enc) throws UnsupportedEncodingException { return new String(encode(string.getBytes(enc)), enc); } /** * Encode bytes in Base64. No line breaks or other white space are inserted * into the encoded data. * * @param bytes * The data to encode. * @return String with Base64 encoded data. * * @since ostermillerutils 1.04.00 */ public static String encodeToString(byte[] bytes) { return encodeToString(bytes, false); } /** * Encode bytes in Base64. * * @param bytes * The data to encode. * @param lineBreaks * Whether to insert line breaks every 76 characters in the * output. * @return String with Base64 encoded data. * * @since ostermillerutils 1.04.00 */ public static String encodeToString(byte[] bytes, boolean lineBreaks) { try { return new String(encode(bytes, lineBreaks), "ASCII"); } catch (UnsupportedEncodingException iex) { // ASCII should be supported throw new RuntimeException(iex); } } /** * Encode bytes in Base64. No line breaks or other white space are inserted * into the encoded data. * * @param bytes * The data to encode. * @return Encoded bytes. * * @since ostermillerutils 1.00.00 */ public static byte[] encode(byte[] bytes) { return encode(bytes, false); } /** * Encode bytes in Base64. * * @param bytes * The data to encode. * @param lineBreaks * Whether to insert line breaks every 76 characters in the * output. * @return Encoded bytes. * * @since ostermillerutils 1.04.00 */ public static byte[] encode(byte[] bytes, boolean lineBreaks) { ByteArrayInputStream in = new ByteArrayInputStream(bytes); // calculate the length of the resulting output. // in general it will be 4/3 the size of the input // but the input length must be divisible by three. // If it isn't the next largest size that is divisible // by three is used. int mod; int length = bytes.length; if ((mod = length % 3) != 0) { length += 3 - mod; } length = length * 4 / 3; ByteArrayOutputStream out = new ByteArrayOutputStream(length); try { encode(in, out, lineBreaks); } catch (IOException x) { // This can't happen. // The input and output streams were constructed // on memory structures that don't actually use IO. throw new RuntimeException(x); } return out.toByteArray(); } /** * Encode this file in Base64. Line breaks will be inserted every 76 * characters. * * @param fIn * File to be encoded (will be overwritten). * @throws IOException * if an input or output error occurs. * * @since ostermillerutils 1.00.00 */ public static void encode(File fIn) throws IOException { encode(fIn, fIn, true); } /** * Encode this file in Base64. * * @param fIn * File to be encoded (will be overwritten). * @param lineBreaks * Whether to insert line breaks every 76 characters in the * output. * @throws IOException * if an input or output error occurs. * * @since ostermillerutils 1.00.00 */ public static void encode(File fIn, boolean lineBreaks) throws IOException { encode(fIn, fIn, lineBreaks); } /** * Encode this file in Base64. Line breaks will be inserted every 76 * characters. * * @param fIn * File to be encoded. * @param fOut * File to which the results should be written (may be the same * as fIn). * @throws IOException * if an input or output error occurs. * * @since ostermillerutils 1.00.00 */ public static void encode(File fIn, File fOut) throws IOException { encode(fIn, fOut, true); } /** * Encode this file in Base64. * * @param fIn * File to be encoded. * @param fOut * File to which the results should be written (may be the same * as fIn). * @param lineBreaks * Whether to insert line breaks every 76 characters in the * output. * @throws IOException * if an input or output error occurs. * * @since ostermillerutils 1.00.00 */ public static void encode(File fIn, File fOut, boolean lineBreaks) throws IOException { File temp = null; InputStream in = null; OutputStream out = null; try { in = new BufferedInputStream(new FileInputStream(fIn)); temp = File.createTempFile("Base64", null, null); out = new BufferedOutputStream(new FileOutputStream(temp)); encode(in, out, lineBreaks); in.close(); in = null; out.flush(); out.close(); out = null; FileHelper.move(temp, fOut, true); } finally { if (in != null) { in.close(); in = null; } if (out != null) { out.flush(); out.close(); out = null; } } } /** * Encode data from the InputStream to the OutputStream in Base64. Line * breaks are inserted every 76 characters in the output. * * @param in * Stream from which to read data that needs to be encoded. * @param out * Stream to which to write encoded data. * @throws IOException * if there is a problem reading or writing. * * @since ostermillerutils 1.00.00 */ public static void encode(InputStream in, OutputStream out) throws IOException { encode(in, out, true); } /** * Encode data from the InputStream to the OutputStream in Base64. * * @param in * Stream from which to read data that needs to be encoded. * @param out * Stream to which to write encoded data. * @param lineBreaks * Whether to insert line breaks every 76 characters in the * output. * @throws IOException * if there is a problem reading or writing. * * @since ostermillerutils 1.00.00 */ public static void encode(InputStream in, OutputStream out, boolean lineBreaks) throws IOException { // Base64 encoding converts three bytes of input to // four bytes of output int[] inBuffer = new int[3]; int lineCount = 0; boolean done = false; while (!done && (inBuffer[0] = in.read()) != END_OF_INPUT) { // Fill the buffer inBuffer[1] = in.read(); inBuffer[2] = in.read(); // Calculate the out Buffer // The first byte of our in buffer will always be valid // but we must check to make sure the other two bytes // are not END_OF_INPUT before using them. // The basic idea is that the three bytes get split into // four bytes along these lines: // [AAAAAABB] [BBBBCCCC] [CCDDDDDD] // [xxAAAAAA] [xxBBBBBB] [xxCCCCCC] [xxDDDDDD] // bytes are considered to be zero when absent. // the four bytes are then mapped to common ASCII symbols // A's: first six bits of first byte out.write(base64Chars[inBuffer[0] >> 2]); if (inBuffer[1] != END_OF_INPUT) { // B's: last two bits of first byte, first four bits of second // byte out.write(base64Chars[((inBuffer[0] << 4) & 0x30) | (inBuffer[1] >> 4)]); if (inBuffer[2] != END_OF_INPUT) { // C's: last four bits of second byte, first two bits of // third byte out.write(base64Chars[((inBuffer[1] << 2) & 0x3c) | (inBuffer[2] >> 6)]); // D's: last six bits of third byte out.write(base64Chars[inBuffer[2] & 0x3F]); } else { // C's: last four bits of second byte out.write(base64Chars[((inBuffer[1] << 2) & 0x3c)]); // an equals sign for a character that is not a Base64 // character out.write('='); done = true; } } else { // B's: last two bits of first byte out.write(base64Chars[((inBuffer[0] << 4) & 0x30)]); // an equal signs for characters that is not a Base64 characters out.write('='); out.write('='); done = true; } lineCount += 4; if (lineBreaks && lineCount >= 76) { out.write('\n'); lineCount = 0; } } if (lineBreaks && lineCount >= 1) { out.write('\n'); lineCount = 0; } out.flush(); } /** * Decode a Base64 encoded String. Characters that are not part of the * Base64 alphabet are ignored in the input. The String is converted to and * from bytes according to the platform's default character encoding. * * @param string * The data to decode. * @return A decoded String. * * @since ostermillerutils 1.00.00 */ public static String decode(String string) { return new String(decode(string.getBytes())); } /** * Decode a Base64 encoded String. Characters that are not part of the * Base64 alphabet are ignored in the input. * * @param string * The data to decode. * @param enc * Character encoding to use when converting to and from bytes. * @throws UnsupportedEncodingException * if the character encoding specified is not supported. * @return A decoded String. * * @since ostermillerutils 1.00.00 */ public static String decode(String string, String enc) throws UnsupportedEncodingException { return new String(decode(string.getBytes(enc)), enc); } /** * Decode a Base64 encoded String. Characters that are not part of the * Base64 alphabet are ignored in the input. * * @param string * The data to decode. * @param encIn * Character encoding to use when converting input to bytes * (should not matter because Base64 data is designed to survive * most character encodings) * @param encOut * Character encoding to use when converting decoded bytes to * output. * @throws UnsupportedEncodingException * if the character encoding specified is not supported. * @return A decoded String. * * @since ostermillerutils 1.00.00 */ public static String decode(String string, String encIn, String encOut) throws UnsupportedEncodingException { return new String(decode(string.getBytes(encIn)), encOut); } /** * Decode a Base64 encoded String. Characters that are not part of the * Base64 alphabet are ignored in the input. The String is converted to and * from bytes according to the platform's default character encoding. * * @param string * The data to decode. * @return A decoded String. * * @since ostermillerutils 1.02.16 */ public static String decodeToString(String string) { return new String(decode(string.getBytes())); } /** * Decode a Base64 encoded String. Characters that are not part of the * Base64 alphabet are ignored in the input. * * @param string * The data to decode. * @param enc * Character encoding to use when converting to and from bytes. * @throws UnsupportedEncodingException * if the character encoding specified is not supported. * @return A decoded String. * * @since ostermillerutils 1.02.16 */ public static String decodeToString(String string, String enc) throws UnsupportedEncodingException { return new String(decode(string.getBytes(enc)), enc); } /** * Decode a Base64 encoded String. Characters that are not part of the * Base64 alphabet are ignored in the input. * * @param string * The data to decode. * @param encIn * Character encoding to use when converting input to bytes * (should not matter because Base64 data is designed to survive * most character encodings) * @param encOut * Character encoding to use when converting decoded bytes to * output. * @throws UnsupportedEncodingException * if the character encoding specified is not supported. * @return A decoded String. * * @since ostermillerutils 1.02.16 */ public static String decodeToString(String string, String encIn, String encOut) throws UnsupportedEncodingException { return new String(decode(string.getBytes(encIn)), encOut); } /** * Decode a Base64 encoded String to an OutputStream. Characters that are * not part of the Base64 alphabet are ignored in the input. The String is * converted from bytes according to the platform's default character * encoding. * * @param string * The data to decode. * @param out * Stream to which to write decoded data. * @throws IOException * if an IO error occurs. * * @since ostermillerutils 1.02.16 */ public static void decodeToStream(String string, OutputStream out) throws IOException { decode(new ByteArrayInputStream(string.getBytes()), out); } /** * Decode a Base64 encoded String to an OutputStream. Characters that are * not part of the Base64 alphabet are ignored in the input. * * @param string * The data to decode. * @param enc * Character encoding to use when converting to and from bytes. * @param out * Stream to which to write decoded data. * @throws UnsupportedEncodingException * if the character encoding specified is not supported. * @throws IOException * if an IO error occurs. * * @since ostermillerutils 1.02.16 */ public static void decodeToStream(String string, String enc, OutputStream out) throws UnsupportedEncodingException, IOException { decode(new ByteArrayInputStream(string.getBytes(enc)), out); } /** * Decode a Base64 encoded String. Characters that are not part of the * Base64 alphabet are ignored in the input. The String is converted from * bytes according to the platform's default character encoding. * * @param string * The data to decode. * @return decoded data. * * @since ostermillerutils 1.02.16 */ public static byte[] decodeToBytes(String string) { return decode(string.getBytes()); } /** * Decode a Base64 encoded String. Characters that are not part of the * Base64 alphabet are ignored in the input. * * @param string * The data to decode. * @param enc * Character encoding to use when converting from bytes. * @throws UnsupportedEncodingException * if the character encoding specified is not supported. * @return decoded data. * * @since ostermillerutils 1.02.16 */ public static byte[] decodeToBytes(String string, String enc) throws UnsupportedEncodingException { return decode(string.getBytes(enc)); } /** * Decode Base64 encoded bytes. Characters that are not part of the Base64 * alphabet are ignored in the input. The String is converted to bytes * according to the platform's default character encoding. * * @param bytes * The data to decode. * @return A decoded String. * * @since ostermillerutils 1.02.16 */ public static String decodeToString(byte[] bytes) { return new String(decode(bytes)); } /** * Decode Base64 encoded bytes. Characters that are not part of the Base64 * alphabet are ignored in the input. * * @param bytes * The data to decode. * @param enc * Character encoding to use when converting to and from bytes. * @throws UnsupportedEncodingException * if the character encoding specified is not supported. * @return A decoded String. * * @since ostermillerutils 1.02.16 */ public static String decodeToString(byte[] bytes, String enc) throws UnsupportedEncodingException { return new String(decode(bytes), enc); } /** * Decode Base64 encoded bytes. Characters that are not part of the Base64 * alphabet are ignored in the input. * * @param bytes * The data to decode. * @return Decoded bytes. * * @since ostermillerutils 1.02.16 */ public static byte[] decodeToBytes(byte[] bytes) { return decode(bytes); } /** * Decode Base64 encoded bytes. Characters that are not part of the Base64 * alphabet are ignored in the input. * * @param bytes * The data to decode. * @return Decoded bytes. * * @since ostermillerutils 1.00.00 */ public static byte[] decode(byte[] bytes) { ByteArrayInputStream in = new ByteArrayInputStream(bytes); // calculate the length of the resulting output. // in general it will be at most 3/4 the size of the input // but the input length must be divisible by four. // If it isn't the next largest size that is divisible // by four is used. int mod; int length = bytes.length; if ((mod = length % 4) != 0) { length += 4 - mod; } length = length * 3 / 4; ByteArrayOutputStream out = new ByteArrayOutputStream(length); try { decode(in, out, false); } catch (IOException x) { // This can't happen. // The input and output streams were constructed // on memory structures that don't actually use IO. throw new RuntimeException(x); } return out.toByteArray(); } /** * Decode Base64 encoded bytes to the an OutputStream. Characters that are * not part of the Base64 alphabet are ignored in the input. * * @param bytes * The data to decode. * @param out * Stream to which to write decoded data. * @throws IOException * if an IO error occurs. * * @since ostermillerutils 1.00.00 */ public static void decode(byte[] bytes, OutputStream out) throws IOException { ByteArrayInputStream in = new ByteArrayInputStream(bytes); decode(in, out, false); } /** * Decode Base64 encoded bytes to the an OutputStream. Characters that are * not part of the Base64 alphabet are ignored in the input. * * @param bytes * The data to decode. * @param out * Stream to which to write decoded data. * @throws IOException * if an IO error occurs. * * @since ostermillerutils 1.02.16 */ public static void decodeToStream(byte[] bytes, OutputStream out) throws IOException { ByteArrayInputStream in = new ByteArrayInputStream(bytes); decode(in, out, false); } /** * Decode Base64 encoded data from one file to the other. Characters in the * Base64 alphabet, white space and equals sign are expected to be in url * encoded data. The presence of other characters could be a sign that the * data is corrupted. * * @param fIn * File to be decoded (will be overwritten). * @throws IOException * if an IO error occurs. * @throws Base64DecodingException * if unexpected data is encountered. * * @since ostermillerutils 1.00.00 */ public static void decode(File fIn) throws IOException { decode(fIn, fIn, true); } /** * Decode Base64 encoded data from one file to the other. Characters in the * Base64 alphabet, white space and equals sign are expected to be in url * encoded data. The presence of other characters could be a sign that the * data is corrupted. * * @param fIn * File to be decoded (will be overwritten). * @param throwExceptions * Whether to throw exceptions when unexpected data is * encountered. * @throws IOException * if an IO error occurs. * @throws Base64DecodingException * if unexpected data is encountered when throwExceptions is * specified. * * @since ostermillerutils 1.00.00 */ public static void decode(File fIn, boolean throwExceptions) throws IOException { decode(fIn, fIn, throwExceptions); } /** * Decode Base64 encoded data from one file to the other. Characters in the * Base64 alphabet, white space and equals sign are expected to be in url * encoded data. The presence of other characters could be a sign that the * data is corrupted. * * @param fIn * File to be decoded. * @param fOut * File to which the results should be written (may be the same * as fIn). * @throws IOException * if an IO error occurs. * @throws Base64DecodingException * if unexpected data is encountered. * * @since ostermillerutils 1.00.00 */ public static void decode(File fIn, File fOut) throws IOException { decode(fIn, fOut, true); } /** * Decode Base64 encoded data from one file to the other. Characters in the * Base64 alphabet, white space and equals sign are expected to be in url * encoded data. The presence of other characters could be a sign that the * data is corrupted. * * @param fIn * File to be decoded. * @param fOut * File to which the results should be written (may be the same * as fIn). * @param throwExceptions * Whether to throw exceptions when unexpected data is * encountered. * @throws IOException * if an IO error occurs. * @throws Base64DecodingException * if unexpected data is encountered when throwExceptions is * specified. * * @since ostermillerutils 1.00.00 */ public static void decode(File fIn, File fOut, boolean throwExceptions) throws IOException { File temp = null; InputStream in = null; OutputStream out = null; try { in = new BufferedInputStream(new FileInputStream(fIn)); temp = File.createTempFile("Base64", null, null); out = new BufferedOutputStream(new FileOutputStream(temp)); decode(in, out, throwExceptions); in.close(); in = null; out.flush(); out.close(); out = null; FileHelper.move(temp, fOut, true); } finally { if (in != null) { try { in.close(); } catch (IOException ignore) { if (throwExceptions) throw ignore; } in = null; } if (out != null) { try { out.flush(); out.close(); } catch (IOException ignore) { if (throwExceptions) throw ignore; } out = null; } } } /** * Reads the next (decoded) Base64 character from the input stream. Non * Base64 characters are skipped. * * @param in * Stream from which bytes are read. * @param throwExceptions * Throw an exception if an unexpected character is encountered. * @return the next Base64 character from the stream or -1 if there are no * more Base64 characters on the stream. * @throws IOException * if an IO Error occurs. * @throws Base64DecodingException * if unexpected data is encountered when throwExceptions is * specified. * * @since ostermillerutils 1.00.00 */ private static final int readBase64(InputStream in, boolean throwExceptions) throws IOException { int read; int numPadding = 0; do { read = in.read(); if (read == END_OF_INPUT) return END_OF_INPUT; read = reverseBase64Chars[(byte) read]; if (throwExceptions && (read == NON_BASE_64 || (numPadding > 0 && read > NON_BASE_64))) { throw new Base64DecodingException(MessageFormat.format("unexpectedchar %s", (Object[]) new String[] { "'" + (char) read + "' (0x" + Integer.toHexString(read) + ")" }), (char) read); } if (read == NON_BASE_64_PADDING) { numPadding++; } } while (read <= NON_BASE_64); return read; } /** * Decode Base64 encoded data from the InputStream to a byte array. * Characters that are not part of the Base64 alphabet are ignored in the * input. * * @param in * Stream from which to read data that needs to be decoded. * @return decoded data. * @throws IOException * if an IO error occurs. * * @since ostermillerutils 1.00.00 */ public static byte[] decodeToBytes(InputStream in) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); decode(in, out, false); return out.toByteArray(); } /** * Decode Base64 encoded data from the InputStream to a String. Characters * that are not part of the Base64 alphabet are ignored in the input. Bytes * are converted to characters in the output String according to the * platform's default character encoding. * * @param in * Stream from which to read data that needs to be decoded. * @return decoded data. * @throws IOException * if an IO error occurs. * * @since ostermillerutils 1.02.16 */ public static String decodeToString(InputStream in) throws IOException { return new String(decodeToBytes(in)); } /** * Decode Base64 encoded data from the InputStream to a String. Characters * that are not part of the Base64 alphabet are ignored in the input. * * @param in * Stream from which to read data that needs to be decoded. * @param enc * Character encoding to use when converting bytes to characters. * @return decoded data. * @throws IOException * if an IO error occurs.Throws: * @throws UnsupportedEncodingException * if the character encoding specified is not supported. * * @since ostermillerutils 1.02.16 */ public static String decodeToString(InputStream in, String enc) throws IOException { return new String(decodeToBytes(in), enc); } /** * Decode Base64 encoded data from the InputStream to the OutputStream. * Characters in the Base64 alphabet, white space and equals sign are * expected to be in url encoded data. The presence of other characters * could be a sign that the data is corrupted. * * @param in * Stream from which to read data that needs to be decoded. * @param out * Stream to which to write decoded data. * @throws IOException * if an IO error occurs. * @throws Base64DecodingException * if unexpected data is encountered. * * @since ostermillerutils 1.00.00 */ public static void decode(InputStream in, OutputStream out) throws IOException { decode(in, out, true); } /** * Decode Base64 encoded data from the InputStream to the OutputStream. * Characters in the Base64 alphabet, white space and equals sign are * expected to be in url encoded data. The presence of other characters * could be a sign that the data is corrupted. * * @param in * Stream from which to read data that needs to be decoded. * @param out * Stream to which to write decoded data. * @param throwExceptions * Whether to throw exceptions when unexpected data is * encountered. * @throws IOException * if an IO error occurs. * @throws Base64DecodingException * if unexpected data is encountered when throwExceptions is * specified. * * @since ostermillerutils 1.00.00 */ public static void decode(InputStream in, OutputStream out, boolean throwExceptions) throws IOException { // Base64 decoding converts four bytes of input to three bytes of output int[] inBuffer = new int[4]; // read bytes unmapping them from their ASCII encoding in the process // we must read at least two bytes to be able to output anything boolean done = false; while (!done && (inBuffer[0] = readBase64(in, throwExceptions)) != END_OF_INPUT && (inBuffer[1] = readBase64(in, throwExceptions)) != END_OF_INPUT) { // Fill the buffer inBuffer[2] = readBase64(in, throwExceptions); inBuffer[3] = readBase64(in, throwExceptions); // Calculate the output // The first two bytes of our in buffer will always be valid // but we must check to make sure the other two bytes // are not END_OF_INPUT before using them. // The basic idea is that the four bytes will get reconstituted // into three bytes along these lines: // [xxAAAAAA] [xxBBBBBB] [xxCCCCCC] [xxDDDDDD] // [AAAAAABB] [BBBBCCCC] [CCDDDDDD] // bytes are considered to be zero when absent. // six A and two B out.write(inBuffer[0] << 2 | inBuffer[1] >> 4); if (inBuffer[2] != END_OF_INPUT) { // four B and four C out.write(inBuffer[1] << 4 | inBuffer[2] >> 2); if (inBuffer[3] != END_OF_INPUT) { // two C and six D out.write(inBuffer[2] << 6 | inBuffer[3]); } else { done = true; } } else { done = true; } } out.flush(); } /** * Determines if the byte array is in base64 format. * <p> * Data will be considered to be in base64 format if it contains only base64 * characters and whitespace with equals sign padding on the end so that the * number of base64 characters is divisible by four. * <p> * It is possible for data to be in base64 format but for it to not meet * these stringent requirements. It is also possible for data to meet these * requirements even though decoding it would not make any sense. This * method should be used as a guide but it is not authoritative because of * the possibility of these false positives and false negatives. * <p> * Additionally, extra data such as headers or footers may throw this method * off the scent and cause it to return false. * * @param bytes * data that could be in base64 format. * @return true iff the array appears to be in base64 format * * @since ostermillerutils 1.00.00 */ public static boolean isBase64(byte[] bytes) { try { return isBase64(new ByteArrayInputStream(bytes)); } catch (IOException x) { // This can't happen. // The input and output streams were constructed // on memory structures that don't actually use IO. return false; } } /** * Determines if the String is in base64 format. The String is converted to * and from bytes according to the platform's default character encoding. * <p> * Data will be considered to be in base64 format if it contains only base64 * characters and whitespace with equals sign padding on the end so that the * number of base64 characters is divisible by four. * <p> * It is possible for data to be in base64 format but for it to not meet * these stringent requirements. It is also possible for data to meet these * requirements even though decoding it would not make any sense. This * method should be used as a guide but it is not authoritative because of * the possibility of these false positives and false negatives. * <p> * Additionally, extra data such as headers or footers may throw this method * off the scent and cause it to return false. * * @param string * String that may be in base64 format. * @return Best guess as to whether the data is in base64 format. * * @since ostermillerutils 1.00.00 */ public static boolean isBase64(String string) { return isBase64(string.getBytes()); } /** * Determines if the String is in base64 format. * <p> * Data will be considered to be in base64 format if it contains only base64 * characters and whitespace with equals sign padding on the end so that the * number of base64 characters is divisible by four. * <p> * It is possible for data to be in base64 format but for it to not meet * these stringent requirements. It is also possible for data to meet these * requirements even though decoding it would not make any sense. This * method should be used as a guide but it is not authoritative because of * the possibility of these false positives and false negatives. * <p> * Additionally, extra data such as headers or footers may throw this method * off the scent and cause it to return false. * * @param string * String that may be in base64 format. * @param enc * Character encoding to use when converting to bytes. * @return Best guess as to whether the data is in base64 format. * @throws UnsupportedEncodingException * if the character encoding specified is not supported. */ public static boolean isBase64(String string, String enc) throws UnsupportedEncodingException { return isBase64(string.getBytes(enc)); } /** * Determines if the File is in base64 format. * <p> * Data will be considered to be in base64 format if it contains only base64 * characters and whitespace with equals sign padding on the end so that the * number of base64 characters is divisible by four. * <p> * It is possible for data to be in base64 format but for it to not meet * these stringent requirements. It is also possible for data to meet these * requirements even though decoding it would not make any sense. This * method should be used as a guide but it is not authoritative because of * the possibility of these false positives and false negatives. * <p> * Additionally, extra data such as headers or footers may throw this method * off the scent and cause it to return false. * * @param fIn * File that may be in base64 format. * @return Best guess as to whether the data is in base64 format. * @throws IOException * if an IO error occurs. * * @since ostermillerutils 1.00.00 */ public static boolean isBase64(File fIn) throws IOException { return isBase64(new BufferedInputStream(new FileInputStream(fIn))); } /** * Reads data from the stream and determines if it is in base64 format. * <p> * Data will be considered to be in base64 format if it contains only base64 * characters and whitespace with equals sign padding on the end so that the * number of base64 characters is divisible by four. * <p> * It is possible for data to be in base64 format but for it to not meet * these stringent requirements. It is also possible for data to meet these * requirements even though decoding it would not make any sense. This * method should be used as a guide but it is not authoritative because of * the possibility of these false positives and false negatives. * <p> * Additionally, extra data such as headers or footers may throw this method * off the scent and cause it to return false. * * @param in * Stream from which to read data to be tested. * @return Best guess as to whether the data is in base64 format. * @throws IOException * if an IO error occurs. * * @since ostermillerutils 1.00.00 */ public static boolean isBase64(InputStream in) throws IOException { long numBase64Chars = 0; int numPadding = 0; int read; while ((read = in.read()) != -1) { read = reverseBase64Chars[read]; if (read == NON_BASE_64) { return false; } else if (read == NON_BASE_64_WHITESPACE) { // ignore white space } else if (read == NON_BASE_64_PADDING) { numPadding++; numBase64Chars++; } else if (numPadding > 0) { return false; } else { numBase64Chars++; } } if (numBase64Chars == 0) return false; if (numBase64Chars % 4 != 0) return false; return true; } }