Home | History | Annotate | Download | only in encoders
      1 package org.bouncycastle.util.encoders;
      2 
      3 import java.io.IOException;
      4 import java.io.OutputStream;
      5 
      6 public class Base64Encoder
      7     implements Encoder
      8 {
      9     protected final byte[] encodingTable =
     10         {
     11             (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
     12             (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
     13             (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
     14             (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
     15             (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
     16             (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
     17             (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
     18             (byte)'v',
     19             (byte)'w', (byte)'x', (byte)'y', (byte)'z',
     20             (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
     21             (byte)'7', (byte)'8', (byte)'9',
     22             (byte)'+', (byte)'/'
     23         };
     24 
     25     protected byte    padding = (byte)'=';
     26 
     27     /*
     28      * set up the decoding table.
     29      */
     30     protected final byte[] decodingTable = new byte[128];
     31 
     32     protected void initialiseDecodingTable()
     33     {
     34         for (int i = 0; i < encodingTable.length; i++)
     35         {
     36             decodingTable[encodingTable[i]] = (byte)i;
     37         }
     38     }
     39 
     40     public Base64Encoder()
     41     {
     42         initialiseDecodingTable();
     43     }
     44 
     45     /**
     46      * encode the input data producing a base 64 output stream.
     47      *
     48      * @return the number of bytes produced.
     49      */
     50     public int encode(
     51         byte[]                data,
     52         int                    off,
     53         int                    length,
     54         OutputStream    out)
     55         throws IOException
     56     {
     57         int modulus = length % 3;
     58         int dataLength = (length - modulus);
     59         int a1, a2, a3;
     60 
     61         for (int i = off; i < off + dataLength; i += 3)
     62         {
     63             a1 = data[i] & 0xff;
     64             a2 = data[i + 1] & 0xff;
     65             a3 = data[i + 2] & 0xff;
     66 
     67             out.write(encodingTable[(a1 >>> 2) & 0x3f]);
     68             out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
     69             out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
     70             out.write(encodingTable[a3 & 0x3f]);
     71         }
     72 
     73         /*
     74          * process the tail end.
     75          */
     76         int    b1, b2, b3;
     77         int    d1, d2;
     78 
     79         switch (modulus)
     80         {
     81         case 0:        /* nothing left to do */
     82             break;
     83         case 1:
     84             d1 = data[off + dataLength] & 0xff;
     85             b1 = (d1 >>> 2) & 0x3f;
     86             b2 = (d1 << 4) & 0x3f;
     87 
     88             out.write(encodingTable[b1]);
     89             out.write(encodingTable[b2]);
     90             out.write(padding);
     91             out.write(padding);
     92             break;
     93         case 2:
     94             d1 = data[off + dataLength] & 0xff;
     95             d2 = data[off + dataLength + 1] & 0xff;
     96 
     97             b1 = (d1 >>> 2) & 0x3f;
     98             b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
     99             b3 = (d2 << 2) & 0x3f;
    100 
    101             out.write(encodingTable[b1]);
    102             out.write(encodingTable[b2]);
    103             out.write(encodingTable[b3]);
    104             out.write(padding);
    105             break;
    106         }
    107 
    108         return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4);
    109     }
    110 
    111     private boolean ignore(
    112         char    c)
    113     {
    114         return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
    115     }
    116 
    117     /**
    118      * decode the base 64 encoded byte data writing it to the given output stream,
    119      * whitespace characters will be ignored.
    120      *
    121      * @return the number of bytes produced.
    122      */
    123     public int decode(
    124         byte[]          data,
    125         int             off,
    126         int             length,
    127         OutputStream    out)
    128         throws IOException
    129     {
    130         byte    b1, b2, b3, b4;
    131         int     outLen = 0;
    132 
    133         int     end = off + length;
    134 
    135         while (end > off)
    136         {
    137             if (!ignore((char)data[end - 1]))
    138             {
    139                 break;
    140             }
    141 
    142             end--;
    143         }
    144 
    145         int  i = off;
    146         int  finish = end - 4;
    147 
    148         i = nextI(data, i, finish);
    149 
    150         while (i < finish)
    151         {
    152             b1 = decodingTable[data[i++]];
    153 
    154             i = nextI(data, i, finish);
    155 
    156             b2 = decodingTable[data[i++]];
    157 
    158             i = nextI(data, i, finish);
    159 
    160             b3 = decodingTable[data[i++]];
    161 
    162             i = nextI(data, i, finish);
    163 
    164             b4 = decodingTable[data[i++]];
    165 
    166             out.write((b1 << 2) | (b2 >> 4));
    167             out.write((b2 << 4) | (b3 >> 2));
    168             out.write((b3 << 6) | b4);
    169 
    170             outLen += 3;
    171 
    172             i = nextI(data, i, finish);
    173         }
    174 
    175         outLen += decodeLastBlock(out, (char)data[end - 4], (char)data[end - 3], (char)data[end - 2], (char)data[end - 1]);
    176 
    177         return outLen;
    178     }
    179 
    180     private int nextI(byte[] data, int i, int finish)
    181     {
    182         while ((i < finish) && ignore((char)data[i]))
    183         {
    184             i++;
    185         }
    186         return i;
    187     }
    188 
    189     /**
    190      * decode the base 64 encoded String data writing it to the given output stream,
    191      * whitespace characters will be ignored.
    192      *
    193      * @return the number of bytes produced.
    194      */
    195     public int decode(
    196         String          data,
    197         OutputStream    out)
    198         throws IOException
    199     {
    200         byte    b1, b2, b3, b4;
    201         int     length = 0;
    202 
    203         int     end = data.length();
    204 
    205         while (end > 0)
    206         {
    207             if (!ignore(data.charAt(end - 1)))
    208             {
    209                 break;
    210             }
    211 
    212             end--;
    213         }
    214 
    215         int  i = 0;
    216         int  finish = end - 4;
    217 
    218         i = nextI(data, i, finish);
    219 
    220         while (i < finish)
    221         {
    222             b1 = decodingTable[data.charAt(i++)];
    223 
    224             i = nextI(data, i, finish);
    225 
    226             b2 = decodingTable[data.charAt(i++)];
    227 
    228             i = nextI(data, i, finish);
    229 
    230             b3 = decodingTable[data.charAt(i++)];
    231 
    232             i = nextI(data, i, finish);
    233 
    234             b4 = decodingTable[data.charAt(i++)];
    235 
    236             out.write((b1 << 2) | (b2 >> 4));
    237             out.write((b2 << 4) | (b3 >> 2));
    238             out.write((b3 << 6) | b4);
    239 
    240             length += 3;
    241 
    242             i = nextI(data, i, finish);
    243         }
    244 
    245         length += decodeLastBlock(out, data.charAt(end - 4), data.charAt(end - 3), data.charAt(end - 2), data.charAt(end - 1));
    246 
    247         return length;
    248     }
    249 
    250     private int decodeLastBlock(OutputStream out, char c1, char c2, char c3, char c4)
    251         throws IOException
    252     {
    253         byte    b1, b2, b3, b4;
    254 
    255         if (c3 == padding)
    256         {
    257             b1 = decodingTable[c1];
    258             b2 = decodingTable[c2];
    259 
    260             out.write((b1 << 2) | (b2 >> 4));
    261 
    262             return 1;
    263         }
    264         else if (c4 == padding)
    265         {
    266             b1 = decodingTable[c1];
    267             b2 = decodingTable[c2];
    268             b3 = decodingTable[c3];
    269 
    270             out.write((b1 << 2) | (b2 >> 4));
    271             out.write((b2 << 4) | (b3 >> 2));
    272 
    273             return 2;
    274         }
    275         else
    276         {
    277             b1 = decodingTable[c1];
    278             b2 = decodingTable[c2];
    279             b3 = decodingTable[c3];
    280             b4 = decodingTable[c4];
    281 
    282             out.write((b1 << 2) | (b2 >> 4));
    283             out.write((b2 << 4) | (b3 >> 2));
    284             out.write((b3 << 6) | b4);
    285 
    286             return 3;
    287         }
    288     }
    289 
    290     private int nextI(String data, int i, int finish)
    291     {
    292         while ((i < finish) && ignore(data.charAt(i)))
    293         {
    294             i++;
    295         }
    296         return i;
    297     }
    298 }
    299