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.5KB

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