All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.csource.common.Base64 Maven / Gradle / Ivy

There is a newer version: 1.29.4
Show newest version
/**
 * Freeware from:
 * Roedy Green
 * Canadian Mind Products
 * #327 - 964 Heywood Avenue
 * Victoria, BC Canada V8V 2Y5
 * tel:(250) 361-9093
 * mailto:[email protected]
 */
package org.csource.common;

/**
 * Encode arbitrary binary into printable ASCII using BASE64 encoding.
 * very loosely based on the Base64 Reader by
 * Dr. Mark Thornton
 * Optrak Distribution Software Ltd.
 * http://www.optrak.co.uk
 * and Kevin Kelley's  http://www.ruralnet.net/~kelley/java/Base64.java
 * 

* Base64 is a way of encoding 8-bit characters using only ASCII printable * characters similar to UUENCODE. UUENCODE includes a filename where BASE64 does not. * The spec is described in RFC 2045. Base64 is a scheme where * 3 bytes are concatenated, then split to form 4 groups of 6-bits each; and * each 6-bits gets translated to an encoded printable ASCII character, via a * table lookup. An encoded string is therefore longer than the original by * about 1/3. The "=" character is used to pad the end. Base64 is used, * among other things, to encode the user:password string in an * Authorization: header for HTTP. Don't confuse Base64 with * x-www-form-urlencoded which is handled by * Java.net.URLEncoder.encode/decode * If you don't like this code, there is another implementation at http://www.ruffboy.com/download.htm * Sun has an undocumented method called sun.misc.Base64Encoder.encode. * You could use hex, simpler to code, but not as compact. *

* If you wanted to encode a giant file, you could do it in large chunks that * are even multiples of 3 bytes, except for the last chunk, and append the outputs. *

* To encode a string, rather than binary data java.net.URLEncoder may be better. See * printable characters in the Java glossary for a discussion of the differences. *

* version 1.4 2002 February 15 -- correct bugs with uneven line lengths, * allow you to configure line separator. * now need Base64 object and instance methods. * new mailing address. * version 1.3 2000 September 12 -- fix problems with estimating output length in encode * version 1.2 2000 September 09 -- now handles decode as well. * version 1.1 1999 December 04 -- more symmetrical encoding algorithm. * more accurate StringBuffer allocation size. * version 1.0 1999 December 03 -- posted in comp.lang.java.programmer. * Futures Streams or files. */ public class Base64 { /** * Marker value for chars we just ignore, e.g. \n \r high ascii */ static final int IGNORE = -1; /** * Marker for = trailing pad */ static final int PAD = -2; /** * used to disable test driver */ private static final boolean debug = true; /** * how we separate lines, e.g. \n, \r\n, \r etc. */ private String lineSeparator = System.getProperty("line.separator"); /** * max chars per line, excluding lineSeparator. A multiple of 4. */ private int lineLength = 72; private char[] valueToChar = new char[64]; /** * binary value encoded by a given letter of the alphabet 0..63 */ private int[] charToValue = new int[256]; private int[] charToPad = new int[4]; /* constructor */ public Base64() { this.init('+', '/', '='); } /* constructor */ public Base64(char chPlus, char chSplash, char chPad, int lineLength) { this.init(chPlus, chSplash, chPad); this.lineLength = lineLength; } public Base64(int lineLength) { this.lineLength = lineLength; } /** * debug display array * * @param b b */ public static void show(byte[] b) { int count = 0; int rows = 0; for (int i = 0; i < b.length; i++) { if (count == 8) { System.out.print(" "); } else if (count == 16) { System.out.println(""); count = 0; continue; } System.out.print(Integer.toHexString(b[i] & 0xFF).toUpperCase() + " "); count++; } System.out.println(); } /** * debug display array * * @param b b */ public static void display(byte[] b) { for (int i = 0; i < b.length; i++) { System.out.print((char) b[i]); } System.out.println(); } /* initialise defaultValueToChar and defaultCharToValue tables */ private void init(char chPlus, char chSplash, char chPad) { int index = 0; // build translate this.valueToChar table only once. // 0..25 -> 'A'..'Z' for (int i = 'A'; i <= 'Z'; i++) { this.valueToChar[index++] = (char) i; } // 26..51 -> 'a'..'z' for (int i = 'a'; i <= 'z'; i++) { this.valueToChar[index++] = (char) i; } // 52..61 -> '0'..'9' for (int i = '0'; i <= '9'; i++) { this.valueToChar[index++] = (char) i; } this.valueToChar[index++] = chPlus; this.valueToChar[index++] = chSplash; // build translate defaultCharToValue table only once. for (int i = 0; i < 256; i++) { this.charToValue[i] = IGNORE; // default is to ignore } for (int i = 0; i < 64; i++) { this.charToValue[this.valueToChar[i]] = i; } this.charToValue[chPad] = PAD; java.util.Arrays.fill(this.charToPad, chPad); } /** * Encode an arbitrary array of bytes as Base64 printable ASCII. * It will be broken into lines of 72 chars each. The last line is not * terminated with a line separator. * The output will always have an even multiple of data characters, * exclusive of \n. It is padded out with =. * * @param b b * @return string */ public String encode(byte[] b) { // Each group or partial group of 3 bytes becomes four chars // covered quotient int outputLength = ((b.length + 2) / 3) * 4; // account for trailing newlines, on all but the very last line if (lineLength != 0) { int lines = (outputLength + lineLength - 1) / lineLength - 1; if (lines > 0) { outputLength += lines * lineSeparator.length(); } } // must be local for recursion to work. StringBuffer sb = new StringBuffer(outputLength); // must be local for recursion to work. int linePos = 0; // first deal with even multiples of 3 bytes. int len = (b.length / 3) * 3; int leftover = b.length - len; for (int i = 0; i < len; i += 3) { // Start a new line if next 4 chars won't fit on the current line // We can't encapsulete the following code since the variable need to // be local to this incarnation of encode. linePos += 4; if (linePos > lineLength) { if (lineLength != 0) { sb.append(lineSeparator); } linePos = 4; } // get next three bytes in unsigned form lined up, // in big-endian order int combined = b[i + 0] & 0xff; combined <<= 8; combined |= b[i + 1] & 0xff; combined <<= 8; combined |= b[i + 2] & 0xff; // break those 24 bits into a 4 groups of 6 bits, // working LSB to MSB. int c3 = combined & 0x3f; combined >>>= 6; int c2 = combined & 0x3f; combined >>>= 6; int c1 = combined & 0x3f; combined >>>= 6; int c0 = combined & 0x3f; // Translate into the equivalent alpha character // emitting them in big-endian order. sb.append(valueToChar[c0]); sb.append(valueToChar[c1]); sb.append(valueToChar[c2]); sb.append(valueToChar[c3]); } // deal with leftover bytes switch (leftover) { case 0: default: // nothing to do break; case 1: // One leftover byte generates xx== // Start a new line if next 4 chars won't fit on the current line linePos += 4; if (linePos > lineLength) { if (lineLength != 0) { sb.append(lineSeparator); } linePos = 4; } // Handle this recursively with a faked complete triple. // Throw away last two chars and replace with == sb.append(encode(new byte[]{b[len], 0, 0}).substring(0, 2)); sb.append("=="); break; case 2: // Two leftover bytes generates xxx= // Start a new line if next 4 chars won't fit on the current line linePos += 4; if (linePos > lineLength) { if (lineLength != 0) { sb.append(lineSeparator); } linePos = 4; } // Handle this recursively with a faked complete triple. // Throw away last char and replace with = sb.append(encode(new byte[]{b[len], b[len + 1], 0}).substring(0, 3)); sb.append("="); break; } // end switch; if (outputLength != sb.length()) { System.out.println("oops: minor program flaw: output length mis-estimated"); System.out.println("estimate:" + outputLength); System.out.println("actual:" + sb.length()); } return sb.toString(); }// end encode /** * decode a well-formed complete Base64 string back into an array of bytes. * It must have an even multiple of 4 data characters (not counting \n), * padded out with = as needed. * * @param s s * @return bytes */ public byte[] decodeAuto(String s) { int nRemain = s.length() % 4; if (nRemain == 0) { return this.decode(s); } else { return this.decode(s + new String(this.charToPad, 0, 4 - nRemain)); } } /** * decode a well-formed complete Base64 string back into an array of bytes. * It must have an even multiple of 4 data characters (not counting \n), * padded out with = as needed. * * @param s s * @return bytes */ public byte[] decode(String s) { // estimate worst case size of output array, no embedded newlines. byte[] b = new byte[(s.length() / 4) * 3]; // tracks where we are in a cycle of 4 input chars. int cycle = 0; // where we combine 4 groups of 6 bits and take apart as 3 groups of 8. int combined = 0; // how many bytes we have prepared. int j = 0; // will be an even multiple of 4 chars, plus some embedded \n int len = s.length(); int dummies = 0; for (int i = 0; i < len; i++) { int c = s.charAt(i); int value = (c <= 255) ? charToValue[c] : IGNORE; // there are two magic values PAD (=) and IGNORE. switch (value) { case IGNORE: // e.g. \n, just ignore it. break; case PAD: value = 0; dummies++; // fallthrough default: /* regular value character */ switch (cycle) { case 0: combined = value; cycle = 1; break; case 1: combined <<= 6; combined |= value; cycle = 2; break; case 2: combined <<= 6; combined |= value; cycle = 3; break; case 3: combined <<= 6; combined |= value; // we have just completed a cycle of 4 chars. // the four 6-bit values are in combined in big-endian order // peel them off 8 bits at a time working lsb to msb // to get our original 3 8-bit bytes back b[j + 2] = (byte) combined; combined >>>= 8; b[j + 1] = (byte) combined; combined >>>= 8; b[j] = (byte) combined; j += 3; cycle = 0; break; } break; } } // end for if (cycle != 0) { throw new ArrayIndexOutOfBoundsException("Input to decode not an even multiple of 4 characters; pad with =."); } j -= dummies; if (b.length != j) { byte[] b2 = new byte[j]; System.arraycopy(b, 0, b2, 0, j); b = b2; } return b; }// end decode /** * determines how long the lines are that are generated by encode. * Ignored by decode. * * @param length 0 means no newlines inserted. Must be a multiple of 4. */ public void setLineLength(int length) { this.lineLength = (length / 4) * 4; } /** * How lines are separated. * Ignored by decode. * * @param lineSeparator may be "" but not null. * Usually contains only a combination of chars \n and \r. * Could be any chars not in set A-Z a-z 0-9 + /. */ public void setLineSeparator(String lineSeparator) { this.lineSeparator = lineSeparator; } public static void test() { try { Base64 b64 = new Base64(); //encode //str64 = b64.encode(str.getBytes()); //System.out.println(str64); String str64 = "CwUEFYoAAAADjQMC7ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267EI="; //decode byte[] theBytes = b64.decode(str64); show(theBytes); } catch (Exception e) { e.printStackTrace(); } } // // /** // * test driver // */ // public static void main(String[] args) { // test(); // System.exit(1); // // if (debug) { // try { // Base64 b64 = new Base64(); // String str = "agfrtu¿¦etʲ1234¼Ù´óerty¿Õ234·¢¿¦2344ʲµÄ"; // String str64 = ""; // // //encode // str64 = b64.encode(str.getBytes()); // System.out.println(str64); // // //decode // byte[] theBytes = b64.decode(str64); // show(theBytes); // String rst = new String(theBytes); // System.out.println(rst); // System.out.println(str); // } catch (Exception e) { // e.printStackTrace(); // } // //getBytes(String charsetName); ///* // byte[] a = { (byte)0xfc, (byte)0x0f, (byte)0xc0}; // byte[] b = { (byte)0x03, (byte)0xf0, (byte)0x3f}; // byte[] c = { (byte)0x00, (byte)0x00, (byte)0x00}; // byte[] d = { (byte)0xff, (byte)0xff, (byte)0xff}; // byte[] e = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1}; // byte[] f = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1, (byte)2}; // byte[] g = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1, (byte)2, (byte)3}; // byte[] h = "AAAAAAAAAAB".getBytes(); // // // // show(a); // show(b); // show(c); // show(d); // show(e); // show(f); // show(g); // show(h); // Base64 b64 = new Base64(); // show(b64.decode(b64.encode(a))); // show(b64.decode(b64.encode(b))); // show(b64.decode(b64.encode(c))); // show(b64.decode(b64.encode(d))); // show(b64.decode(b64.encode(e))); // show(b64.decode(b64.encode(f))); // show(b64.decode(b64.encode(g))); // show(b64.decode(b64.encode(h))); // b64.setLineLength(8); // show((b64.encode(h)).getBytes()); //*/ // } // }// end main } // end Base64





© 2015 - 2024 Weber Informatics LLC | Privacy Policy