View Javadoc
1   /*
2    * Copyright 2005 Ralf Joachim
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.castor.core.util;
17  
18  import java.io.ByteArrayOutputStream;
19  
20  /**
21   * Class decodes a Base64 encoded string back into the original byte representation
22   * that can be read as byte array.
23   *
24   * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
25   * @version $Revision: 6907 $ $Date: 2005-08-05 13:58:36 -0600 (Fri, 05 Aug 2005) $
26   * @since 0.9.9
27   */
28  public final class Base64Decoder {
29      /** Mask buffer to or with first sextet. */
30      private static final int    SEXTET_1_MASK = 0x03FFFF;
31      
32      /** Mask buffer to or with second sextet. */
33      private static final int    SEXTET_2_MASK = 0xFC0FFF;
34  
35      /** Mask buffer to or with third sextet. */
36      private static final int    SEXTET_3_MASK = 0xFFF03F;
37      
38      /** Mask buffer to or with forth sextet. */
39      private static final int    SEXTET_4_MASK = 0xFFFFC0;
40      
41      /** Number of bits to shift for one sextet. */
42      private static final int    SHIFT_1_SEXTET = 6;
43      
44      /** Number of bits to shift for two sextet. */
45      private static final int    SHIFT_2_SEXTET = 12;
46      
47      /** Number of bits to shift for three sextet. */
48      private static final int    SHIFT_3_SEXTET = 18;
49      
50      /** Second sextets in buffer. */
51      private static final int    SEXTET_2 = 2;
52      
53      /** Third sextets in buffer. */
54      private static final int    SEXTET_3 = 3;
55      
56      /** Forth sextets in buffer. */
57      private static final int    SEXTET_4 = 4;
58      
59      /** Mask an octet. */
60      private static final int    OCTET_MASK = 0xFF;
61      
62      /** Number of bits to shift for one octet. */
63      private static final int    SHIFT_1_OCTET = 8;
64      
65      /** Number of bits to shift for two octet. */
66      private static final int    SHIFT_2_OCTET = 16;
67      
68      /** White space character (out of range 0 - 63). */
69      private static final byte SPC = 127;
70  
71      /** Padding character (out of range 0 - 63). */
72      private static final byte PAD = 64;
73  
74      /** Array to translate base64 characters into sextet byte values. */
75      private static final byte[] MAP = {
76              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // 00-07
77              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // 08-0F
78              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // 10-17
79              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // 18-1F
80              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // 20-27
81              SPC, SPC, SPC,  62, SPC, SPC, SPC,  63,     // 28-2F '   +   /'
82               52,  53,  54,  55,  56,  57,  58,  59,     // 30-37 '01234567'
83               60,  61, SPC, SPC, SPC, PAD, SPC, SPC,     // 38-3F '89   =  '
84              SPC,   0,   1,   2,   3,   4,   5,   6,     // 40-47 ' ABCDEFG'
85                7,   8,   9,  10,  11,  12,  13,  14,     // 48-4F 'HIJKLMNO'
86               15,  16,  17,  18,  19,  20,  21,  22,     // 50-57 'PQRSTUVW'
87               23,  24,  25, SPC, SPC, SPC, SPC, SPC,     // 58-5F 'XYZ     '
88              SPC,  26,  27,  28,  29,  30,  31,  32,     // 60-67 ' abcdefg'
89               33,  34,  35,  36,  37,  38,  39,  40,     // 68-6F 'hijklmno'
90               41,  42,  43,  44,  45,  46,  47,  48,     // 70-77 'pqrstuvw'
91               49,  50,  51, SPC, SPC, SPC, SPC, SPC,     // 78-7F 'xyz     '
92              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // 80-87
93              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // 88-8F
94              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // 90-97
95              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // 98-9F
96              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // A0-A7
97              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // A8-AF
98              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // B0-B7
99              SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // B8-BF
100             SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // C0-C7
101             SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // C8-CF
102             SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // D0-D7
103             SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // D8-DF
104             SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // E0-E7
105             SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // E8-EF
106             SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // F0-F7
107             SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC,     // F8-FF
108         };
109 
110     /** 24-bit buffer to translate 4 sextets into 3 octets. */
111     private int                     _buffer = 0;
112 
113     /** Number of octets in buffer. */
114     private int                     _sextets = 0;
115 
116     /** Stream buffer for decoded octets waiting to be read. */
117     private ByteArrayOutputStream   _stream = new ByteArrayOutputStream();
118     
119     /**
120      * Decode given string into a decoded byte array.
121      * 
122      * @param str Base64 String to be decoded.
123      * @return All decoded octets as byte array.
124      */
125     public static byte[] decode(final String str) {
126         Base64Decoder dec = new Base64Decoder();
127         dec.translate(str);
128         return dec.getByteArray();
129     }
130 
131     /**
132      * Construct a default Base64Decoder waiting on calls to its translate()
133      * method.
134      */
135     public Base64Decoder() { }
136 
137     /**
138      * Translate every base64 character from given string into a sextet byte value
139      * by using above translation array. The sextets are then shiftet into an buffer
140      * until the buffer contains 4 sextets which are then decoded into 3 octets.
141      * The translate and decode process is continued until all characters of given
142      * string are evaluated. If there are remaing sextets in the buffer they also
143      * will be converted into octets at the end. All the converted octets are added
144      * to the list for later read.
145      * 
146      * @param string    Base64 String to be decoded.
147      */
148     public void translate(final String string) {
149         int len = string.length();
150         int index = 0;
151         int data = MAP[string.charAt(index)];
152         while ((index < len) && (data != PAD)) {
153             if (data != SPC) {
154                 if (_sextets == 0) {
155                     _buffer = (_buffer & SEXTET_1_MASK) | (data << SHIFT_3_SEXTET);
156                 } else if (_sextets == 1) {
157                     _buffer = (_buffer & SEXTET_2_MASK) | (data << SHIFT_2_SEXTET);
158                 } else if (_sextets == 2) {
159                     _buffer = (_buffer & SEXTET_3_MASK) | (data << SHIFT_1_SEXTET);
160                 } else {
161                     _buffer = (_buffer & SEXTET_4_MASK) | data;
162                 }
163 
164                 if ((++_sextets) == SEXTET_4) { decode(); }
165             }
166 
167             if (++index < len) { data = MAP[string.charAt(index)]; }
168         }
169 
170         if (_sextets > 0) { decodeWithPadding(); }
171     }
172 
173     /**
174      * Decode 3 octets from buffer and add them to list of octets to read.
175      */
176     private void decode() {
177         _stream.write((byte) ((_buffer >> SHIFT_2_OCTET) & OCTET_MASK));     // octet 1
178         _stream.write((byte) ((_buffer >> SHIFT_1_OCTET) & OCTET_MASK));     // octet 2
179         _stream.write((byte) (_buffer & OCTET_MASK));                        // octet 3
180         _buffer = 0;
181         _sextets = 0;
182     }
183 
184     /**
185      * Decode the remaining octets from buffer and add them to list of octets to read.
186      */
187     private void decodeWithPadding() {
188         if (_sextets >= SEXTET_2) {                                     // octet 1
189             _stream.write((byte) ((_buffer >> SHIFT_2_OCTET) & OCTET_MASK));
190         }
191         if (_sextets >= SEXTET_3) {                                     // octet 2
192             _stream.write((byte) ((_buffer >> SHIFT_1_OCTET) & OCTET_MASK));
193         }
194         if (_sextets >= SEXTET_4) {                                     // octet 3
195             _stream.write((byte) (_buffer & OCTET_MASK));
196         }
197         _buffer = 0;
198         _sextets = 0;
199     }
200 
201     /**
202      * Get all decoded octets as byte array.
203      * 
204      * @return All decoded octets as byte array.
205      */
206     public byte[] getByteArray() {
207         return _stream.toByteArray();
208     }
209 }