Base64.java

  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 static java.nio.charset.StandardCharsets.UTF_8;

  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
  17.  * <a href="http://iharder.net/base64">http://iharder.net/base64</a>
  18.  * periodically to check for updates or to contribute improvements.
  19.  * </p>
  20.  *
  21.  * @author Robert Harder
  22.  * @author rob@iharder.net
  23.  */
  24. public class Base64 {
  25.     /** The equals sign (=) as a byte. */
  26.     private static final byte EQUALS_SIGN = (byte) '=';

  27.     /** Indicates equals sign in encoding. */
  28.     private static final byte EQUALS_SIGN_DEC = -1;

  29.     /** Indicates white space in encoding. */
  30.     private static final byte WHITE_SPACE_DEC = -2;

  31.     /** Indicates an invalid byte during decoding. */
  32.     private static final byte INVALID_DEC = -3;

  33.     /** The 64 valid Base64 values. */
  34.     private static final byte[] ENC;

  35.     /**
  36.      * Translates a Base64 value to either its 6-bit reconstruction value or a
  37.      * negative number indicating some other meaning. The table is only 7 bits
  38.      * wide, as the 8th bit is discarded during decoding.
  39.      */
  40.     private static final byte[] DEC;

  41.     static {
  42.         ENC = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" // //$NON-NLS-1$
  43.                 + "abcdefghijklmnopqrstuvwxyz" // //$NON-NLS-1$
  44.                 + "0123456789" // //$NON-NLS-1$
  45.                 + "+/" // //$NON-NLS-1$
  46.         ).getBytes(UTF_8);

  47.         DEC = new byte[128];
  48.         Arrays.fill(DEC, INVALID_DEC);

  49.         for (int i = 0; i < 64; i++)
  50.             DEC[ENC[i]] = (byte) i;
  51.         DEC[EQUALS_SIGN] = EQUALS_SIGN_DEC;

  52.         DEC['\t'] = WHITE_SPACE_DEC;
  53.         DEC['\n'] = WHITE_SPACE_DEC;
  54.         DEC['\r'] = WHITE_SPACE_DEC;
  55.         DEC[' '] = WHITE_SPACE_DEC;
  56.     }

  57.     /** Defeats instantiation. */
  58.     private Base64() {
  59.         // Suppress empty block warning.
  60.     }

  61.     /**
  62.      * Encodes up to three bytes of the array <var>source</var> and writes the
  63.      * resulting four Base64 bytes to <var>destination</var>. The source and
  64.      * destination arrays can be manipulated anywhere along their length by
  65.      * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
  66.      * does not check to make sure your arrays are large enough to accommodate
  67.      * <var>srcOffset</var> + 3 for the <var>source</var> array or
  68.      * <var>destOffset</var> + 4 for the <var>destination</var> array. The
  69.      * actual number of significant bytes in your array is given by
  70.      * <var>numSigBytes</var>.
  71.      *
  72.      * @param source
  73.      *            the array to convert
  74.      * @param srcOffset
  75.      *            the index where conversion begins
  76.      * @param numSigBytes
  77.      *            the number of significant bytes in your array
  78.      * @param destination
  79.      *            the array to hold the conversion
  80.      * @param destOffset
  81.      *            the index where output will be put
  82.      */
  83.     private static void encode3to4(byte[] source, int srcOffset,
  84.             int numSigBytes, byte[] destination, int destOffset) {
  85.         // We have to shift left 24 in order to flush out the 1's that appear
  86.         // when Java treats a value as negative that is cast from a byte.

  87.         int inBuff = 0;
  88.         switch (numSigBytes) {
  89.         case 3:
  90.             inBuff |= (source[srcOffset + 2] << 24) >>> 24;
  91.             //$FALL-THROUGH$

  92.         case 2:
  93.             inBuff |= (source[srcOffset + 1] << 24) >>> 16;
  94.             //$FALL-THROUGH$

  95.         case 1:
  96.             inBuff |= (source[srcOffset] << 24) >>> 8;
  97.         }

  98.         switch (numSigBytes) {
  99.         case 3:
  100.             destination[destOffset] = ENC[(inBuff >>> 18)];
  101.             destination[destOffset + 1] = ENC[(inBuff >>> 12) & 0x3f];
  102.             destination[destOffset + 2] = ENC[(inBuff >>> 6) & 0x3f];
  103.             destination[destOffset + 3] = ENC[(inBuff) & 0x3f];
  104.             break;

  105.         case 2:
  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] = EQUALS_SIGN;
  110.             break;

  111.         case 1:
  112.             destination[destOffset] = ENC[(inBuff >>> 18)];
  113.             destination[destOffset + 1] = ENC[(inBuff >>> 12) & 0x3f];
  114.             destination[destOffset + 2] = EQUALS_SIGN;
  115.             destination[destOffset + 3] = EQUALS_SIGN;
  116.             break;
  117.         }
  118.     }

  119.     /**
  120.      * Encodes a byte array into Base64 notation.
  121.      *
  122.      * @param source
  123.      *            The data to convert
  124.      * @return encoded base64 representation of source.
  125.      */
  126.     public static String encodeBytes(byte[] source) {
  127.         return encodeBytes(source, 0, source.length);
  128.     }

  129.     /**
  130.      * Encodes a byte array into Base64 notation.
  131.      *
  132.      * @param source
  133.      *            The data to convert
  134.      * @param off
  135.      *            Offset in array where conversion should begin
  136.      * @param len
  137.      *            Length of data to convert
  138.      * @return encoded base64 representation of source.
  139.      */
  140.     public static String encodeBytes(byte[] source, int off, int len) {
  141.         final int len43 = len * 4 / 3;

  142.         byte[] outBuff = new byte[len43 + ((len % 3) > 0 ? 4 : 0)];
  143.         int d = 0;
  144.         int e = 0;
  145.         int len2 = len - 2;

  146.         for (; d < len2; d += 3, e += 4)
  147.             encode3to4(source, d + off, 3, outBuff, e);

  148.         if (d < len) {
  149.             encode3to4(source, d + off, len - d, outBuff, e);
  150.             e += 4;
  151.         }

  152.         return new String(outBuff, 0, e, UTF_8);
  153.     }

  154.     /**
  155.      * Decodes four bytes from array <var>source</var> and writes the resulting
  156.      * bytes (up to three of them) to <var>destination</var>. The source and
  157.      * destination arrays can be manipulated anywhere along their length by
  158.      * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
  159.      * does not check to make sure your arrays are large enough to accommodate
  160.      * <var>srcOffset</var> + 4 for the <var>source</var> array or
  161.      * <var>destOffset</var> + 3 for the <var>destination</var> array. This
  162.      * method returns the actual number of bytes that were converted from the
  163.      * Base64 encoding.
  164.      *
  165.      * @param source
  166.      *            the array to convert
  167.      * @param srcOffset
  168.      *            the index where conversion begins
  169.      * @param destination
  170.      *            the array to hold the conversion
  171.      * @param destOffset
  172.      *            the index where output will be put
  173.      * @return the number of decoded bytes converted
  174.      */
  175.     private static int decode4to3(byte[] source, int srcOffset,
  176.             byte[] destination, int destOffset) {
  177.         // Example: Dk==
  178.         if (source[srcOffset + 2] == EQUALS_SIGN) {
  179.             int outBuff = ((DEC[source[srcOffset]] & 0xFF) << 18)
  180.                     | ((DEC[source[srcOffset + 1]] & 0xFF) << 12);
  181.             destination[destOffset] = (byte) (outBuff >>> 16);
  182.             return 1;
  183.         }

  184.         // Example: DkL=
  185.         else if (source[srcOffset + 3] == EQUALS_SIGN) {
  186.             int outBuff = ((DEC[source[srcOffset]] & 0xFF) << 18)
  187.                     | ((DEC[source[srcOffset + 1]] & 0xFF) << 12)
  188.                     | ((DEC[source[srcOffset + 2]] & 0xFF) << 6);
  189.             destination[destOffset] = (byte) (outBuff >>> 16);
  190.             destination[destOffset + 1] = (byte) (outBuff >>> 8);
  191.             return 2;
  192.         }

  193.         // Example: DkLE
  194.         else {
  195.             int outBuff = ((DEC[source[srcOffset]] & 0xFF) << 18)
  196.                     | ((DEC[source[srcOffset + 1]] & 0xFF) << 12)
  197.                     | ((DEC[source[srcOffset + 2]] & 0xFF) << 6)
  198.                     | ((DEC[source[srcOffset + 3]] & 0xFF));

  199.             destination[destOffset] = (byte) (outBuff >> 16);
  200.             destination[destOffset + 1] = (byte) (outBuff >> 8);
  201.             destination[destOffset + 2] = (byte) (outBuff);

  202.             return 3;
  203.         }
  204.     }

  205.     /**
  206.      * Low-level decoding ASCII characters from a byte array.
  207.      *
  208.      * @param source
  209.      *            The Base64 encoded data
  210.      * @param off
  211.      *            The offset of where to begin decoding
  212.      * @param len
  213.      *            The length of characters to decode
  214.      * @return decoded data
  215.      * @throws java.lang.IllegalArgumentException
  216.      *             the input is not a valid Base64 sequence.
  217.      */
  218.     public static byte[] decode(byte[] source, int off, int len) {
  219.         byte[] outBuff = new byte[len * 3 / 4]; // Upper limit on size of output
  220.         int outBuffPosn = 0;

  221.         byte[] b4 = new byte[4];
  222.         int b4Posn = 0;

  223.         for (int i = off; i < off + len; i++) {
  224.             byte sbiCrop = (byte) (source[i] & 0x7f);
  225.             byte sbiDecode = DEC[sbiCrop];

  226.             if (EQUALS_SIGN_DEC <= sbiDecode) {
  227.                 b4[b4Posn++] = sbiCrop;
  228.                 if (b4Posn > 3) {
  229.                     outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
  230.                     b4Posn = 0;

  231.                     // If that was the equals sign, break out of 'for' loop
  232.                     if (sbiCrop == EQUALS_SIGN)
  233.                         break;
  234.                 }

  235.             } else if (sbiDecode != WHITE_SPACE_DEC)
  236.                 throw new IllegalArgumentException(MessageFormat.format(
  237.                         JGitText.get().badBase64InputCharacterAt,
  238.                         Integer.valueOf(i), Integer.valueOf(source[i] & 0xff)));
  239.         }

  240.         if (outBuff.length == outBuffPosn)
  241.             return outBuff;

  242.         byte[] out = new byte[outBuffPosn];
  243.         System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
  244.         return out;
  245.     }

  246.     /**
  247.      * Decodes data from Base64 notation.
  248.      *
  249.      * @param s
  250.      *            the string to decode
  251.      * @return the decoded data
  252.      */
  253.     public static byte[] decode(String s) {
  254.         byte[] bytes = s.getBytes(UTF_8);
  255.         return decode(bytes, 0, bytes.length);
  256.     }
  257. }