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