View Javadoc
1   //
2   //  NOTE: The following source code is heavily derived from the
3   //  iHarder.net public domain Base64 library.  See the original at
4   //  http://iharder.sourceforge.net/current/java/base64/
5   //
6   
7   package org.eclipse.jgit.util;
8   
9   import java.io.UnsupportedEncodingException;
10  import java.text.MessageFormat;
11  import java.util.Arrays;
12  
13  import org.eclipse.jgit.internal.JGitText;
14  
15  /**
16   * Encodes and decodes to and from Base64 notation.
17   * <p>
18   * I am placing this code in the Public Domain. Do with it as you will. This
19   * software comes with no guarantees or warranties but with plenty of
20   * well-wishing instead! Please visit <a
21   * href="http://iharder.net/base64">http://iharder.net/base64</a> periodically
22   * to check for updates or to contribute improvements.
23   * </p>
24   *
25   * @author Robert Harder
26   * @author rob@iharder.net
27   * @version 2.1, stripped to minimum feature set used by JGit.
28   */
29  public class Base64 {
30  	/** The equals sign (=) as a byte. */
31  	private final static byte EQUALS_SIGN = (byte) '=';
32  
33  	/** Indicates equals sign in encoding. */
34  	private final static byte EQUALS_SIGN_DEC = -1;
35  
36  	/** Indicates white space in encoding. */
37  	private final static byte WHITE_SPACE_DEC = -2;
38  
39  	/** Indicates an invalid byte during decoding. */
40  	private final static byte INVALID_DEC = -3;
41  
42  	/** Preferred encoding. */
43  	private final static String UTF_8 = "UTF-8"; //$NON-NLS-1$
44  
45  	/** The 64 valid Base64 values. */
46  	private final static byte[] ENC;
47  
48  	/**
49  	 * Translates a Base64 value to either its 6-bit reconstruction value or a
50  	 * negative number indicating some other meaning. The table is only 7 bits
51  	 * wide, as the 8th bit is discarded during decoding.
52  	 */
53  	private final static byte[] DEC;
54  
55  	static {
56  		try {
57  			ENC = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" // //$NON-NLS-1$
58  					+ "abcdefghijklmnopqrstuvwxyz" // //$NON-NLS-1$
59  					+ "0123456789" // //$NON-NLS-1$
60  					+ "+/" // //$NON-NLS-1$
61  			).getBytes(UTF_8);
62  		} catch (UnsupportedEncodingException uee) {
63  			throw new RuntimeException(uee.getMessage(), uee);
64  		}
65  
66  		DEC = new byte[128];
67  		Arrays.fill(DEC, INVALID_DEC);
68  
69  		for (int i = 0; i < 64; i++)
70  			DEC[ENC[i]] = (byte) i;
71  		DEC[EQUALS_SIGN] = EQUALS_SIGN_DEC;
72  
73  		DEC['\t'] = WHITE_SPACE_DEC;
74  		DEC['\n'] = WHITE_SPACE_DEC;
75  		DEC['\r'] = WHITE_SPACE_DEC;
76  		DEC[' '] = WHITE_SPACE_DEC;
77  	}
78  
79  	/** Defeats instantiation. */
80  	private Base64() {
81  		// Suppress empty block warning.
82  	}
83  
84  	/**
85  	 * Encodes up to three bytes of the array <var>source</var> and writes the
86  	 * resulting four Base64 bytes to <var>destination</var>. The source and
87  	 * destination arrays can be manipulated anywhere along their length by
88  	 * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
89  	 * does not check to make sure your arrays are large enough to accommodate
90  	 * <var>srcOffset</var> + 3 for the <var>source</var> array or
91  	 * <var>destOffset</var> + 4 for the <var>destination</var> array. The
92  	 * actual number of significant bytes in your array is given by
93  	 * <var>numSigBytes</var>.
94  	 *
95  	 * @param source
96  	 *            the array to convert
97  	 * @param srcOffset
98  	 *            the index where conversion begins
99  	 * @param numSigBytes
100 	 *            the number of significant bytes in your array
101 	 * @param destination
102 	 *            the array to hold the conversion
103 	 * @param destOffset
104 	 *            the index where output will be put
105 	 */
106 	private static void encode3to4(byte[] source, int srcOffset,
107 			int numSigBytes, byte[] destination, int destOffset) {
108 		// We have to shift left 24 in order to flush out the 1's that appear
109 		// when Java treats a value as negative that is cast from a byte.
110 
111 		int inBuff = 0;
112 		switch (numSigBytes) {
113 		case 3:
114 			inBuff |= (source[srcOffset + 2] << 24) >>> 24;
115 			//$FALL-THROUGH$
116 
117 		case 2:
118 			inBuff |= (source[srcOffset + 1] << 24) >>> 16;
119 			//$FALL-THROUGH$
120 
121 		case 1:
122 			inBuff |= (source[srcOffset] << 24) >>> 8;
123 		}
124 
125 		switch (numSigBytes) {
126 		case 3:
127 			destination[destOffset] = ENC[(inBuff >>> 18)];
128 			destination[destOffset + 1] = ENC[(inBuff >>> 12) & 0x3f];
129 			destination[destOffset + 2] = ENC[(inBuff >>> 6) & 0x3f];
130 			destination[destOffset + 3] = ENC[(inBuff) & 0x3f];
131 			break;
132 
133 		case 2:
134 			destination[destOffset] = ENC[(inBuff >>> 18)];
135 			destination[destOffset + 1] = ENC[(inBuff >>> 12) & 0x3f];
136 			destination[destOffset + 2] = ENC[(inBuff >>> 6) & 0x3f];
137 			destination[destOffset + 3] = EQUALS_SIGN;
138 			break;
139 
140 		case 1:
141 			destination[destOffset] = ENC[(inBuff >>> 18)];
142 			destination[destOffset + 1] = ENC[(inBuff >>> 12) & 0x3f];
143 			destination[destOffset + 2] = EQUALS_SIGN;
144 			destination[destOffset + 3] = EQUALS_SIGN;
145 			break;
146 		}
147 	}
148 
149 	/**
150 	 * Encodes a byte array into Base64 notation.
151 	 *
152 	 * @param source
153 	 *            The data to convert
154 	 * @return encoded base64 representation of source.
155 	 */
156 	public static String encodeBytes(byte[] source) {
157 		return encodeBytes(source, 0, source.length);
158 	}
159 
160 	/**
161 	 * Encodes a byte array into Base64 notation.
162 	 *
163 	 * @param source
164 	 *            The data to convert
165 	 * @param off
166 	 *            Offset in array where conversion should begin
167 	 * @param len
168 	 *            Length of data to convert
169 	 * @return encoded base64 representation of source.
170 	 */
171 	public static String encodeBytes(byte[] source, int off, int len) {
172 		final int len43 = len * 4 / 3;
173 
174 		byte[] outBuff = new byte[len43 + ((len % 3) > 0 ? 4 : 0)];
175 		int d = 0;
176 		int e = 0;
177 		int len2 = len - 2;
178 
179 		for (; d < len2; d += 3, e += 4)
180 			encode3to4(source, d + off, 3, outBuff, e);
181 
182 		if (d < len) {
183 			encode3to4(source, d + off, len - d, outBuff, e);
184 			e += 4;
185 		}
186 
187 		try {
188 			return new String(outBuff, 0, e, UTF_8);
189 		} catch (UnsupportedEncodingException uue) {
190 			return new String(outBuff, 0, e);
191 		}
192 	}
193 
194 	/**
195 	 * Decodes four bytes from array <var>source</var> and writes the resulting
196 	 * bytes (up to three of them) to <var>destination</var>. The source and
197 	 * destination arrays can be manipulated anywhere along their length by
198 	 * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
199 	 * does not check to make sure your arrays are large enough to accommodate
200 	 * <var>srcOffset</var> + 4 for the <var>source</var> array or
201 	 * <var>destOffset</var> + 3 for the <var>destination</var> array. This
202 	 * method returns the actual number of bytes that were converted from the
203 	 * Base64 encoding.
204 	 *
205 	 * @param source
206 	 *            the array to convert
207 	 * @param srcOffset
208 	 *            the index where conversion begins
209 	 * @param destination
210 	 *            the array to hold the conversion
211 	 * @param destOffset
212 	 *            the index where output will be put
213 	 * @return the number of decoded bytes converted
214 	 */
215 	private static int decode4to3(byte[] source, int srcOffset,
216 			byte[] destination, int destOffset) {
217 		// Example: Dk==
218 		if (source[srcOffset + 2] == EQUALS_SIGN) {
219 			int outBuff = ((DEC[source[srcOffset]] & 0xFF) << 18)
220 					| ((DEC[source[srcOffset + 1]] & 0xFF) << 12);
221 			destination[destOffset] = (byte) (outBuff >>> 16);
222 			return 1;
223 		}
224 
225 		// Example: DkL=
226 		else if (source[srcOffset + 3] == EQUALS_SIGN) {
227 			int outBuff = ((DEC[source[srcOffset]] & 0xFF) << 18)
228 					| ((DEC[source[srcOffset + 1]] & 0xFF) << 12)
229 					| ((DEC[source[srcOffset + 2]] & 0xFF) << 6);
230 			destination[destOffset] = (byte) (outBuff >>> 16);
231 			destination[destOffset + 1] = (byte) (outBuff >>> 8);
232 			return 2;
233 		}
234 
235 		// Example: DkLE
236 		else {
237 			int outBuff = ((DEC[source[srcOffset]] & 0xFF) << 18)
238 					| ((DEC[source[srcOffset + 1]] & 0xFF) << 12)
239 					| ((DEC[source[srcOffset + 2]] & 0xFF) << 6)
240 					| ((DEC[source[srcOffset + 3]] & 0xFF));
241 
242 			destination[destOffset] = (byte) (outBuff >> 16);
243 			destination[destOffset + 1] = (byte) (outBuff >> 8);
244 			destination[destOffset + 2] = (byte) (outBuff);
245 
246 			return 3;
247 		}
248 	}
249 
250 	/**
251 	 * Low-level decoding ASCII characters from a byte array.
252 	 *
253 	 * @param source
254 	 *            The Base64 encoded data
255 	 * @param off
256 	 *            The offset of where to begin decoding
257 	 * @param len
258 	 *            The length of characters to decode
259 	 * @return decoded data
260 	 * @throws IllegalArgumentException
261 	 *             the input is not a valid Base64 sequence.
262 	 */
263 	public static byte[] decode(byte[] source, int off, int len) {
264 		byte[] outBuff = new byte[len * 3 / 4]; // Upper limit on size of output
265 		int outBuffPosn = 0;
266 
267 		byte[] b4 = new byte[4];
268 		int b4Posn = 0;
269 
270 		for (int i = off; i < off + len; i++) {
271 			byte sbiCrop = (byte) (source[i] & 0x7f);
272 			byte sbiDecode = DEC[sbiCrop];
273 
274 			if (EQUALS_SIGN_DEC <= sbiDecode) {
275 				b4[b4Posn++] = sbiCrop;
276 				if (b4Posn > 3) {
277 					outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
278 					b4Posn = 0;
279 
280 					// If that was the equals sign, break out of 'for' loop
281 					if (sbiCrop == EQUALS_SIGN)
282 						break;
283 				}
284 
285 			} else if (sbiDecode != WHITE_SPACE_DEC)
286 				throw new IllegalArgumentException(MessageFormat.format(
287 						JGitText.get().badBase64InputCharacterAt,
288 						Integer.valueOf(i), Integer.valueOf(source[i] & 0xff)));
289 		}
290 
291 		if (outBuff.length == outBuffPosn)
292 			return outBuff;
293 
294 		byte[] out = new byte[outBuffPosn];
295 		System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
296 		return out;
297 	}
298 
299 	/**
300 	 * Decodes data from Base64 notation.
301 	 *
302 	 * @param s
303 	 *            the string to decode
304 	 * @return the decoded data
305 	 */
306 	public static byte[] decode(String s) {
307 		byte[] bytes;
308 		try {
309 			bytes = s.getBytes(UTF_8);
310 		} catch (UnsupportedEncodingException uee) {
311 			bytes = s.getBytes();
312 		}
313 		return decode(bytes, 0, bytes.length);
314 	}
315 }