You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Base64.java 9.2KB

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