View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.ws.security.util;
19  
20  import org.apache.ws.security.WSSecurityException;
21  
22  /**
23   * This class provides encode/decode for RFC 2045 Base64 as defined by RFC 2045,
24   * N. Freed and N. Borenstein. RFC 2045: Multipurpose Internet Mail Extensions
25   * (MIME) Part One: Format of Internet Message Bodies. Reference 1996 Available
26   * at: http://www.ietf.org/rfc/rfc2045.txt This class is used by XML Schema
27   * binary format validation
28   * 
29   * This implementation does not encode/decode streaming data. You need the data
30   * that you will encode/decode already on a byte arrray.
31   * 
32   * @author Jeffrey Rodriguez
33   * @author Sandy Gao
34   * @version $Id$
35   */
36  public final class Base64 {
37  
38      private static final int BASELENGTH = 128;
39      private static final int LOOKUPLENGTH = 64;
40      private static final int TWENTYFOURBITGROUP = 24;
41      private static final int EIGHTBIT = 8;
42      private static final int SIXTEENBIT = 16;
43      private static final int FOURBYTE = 4;
44      private static final int SIGN = -128;
45      private static final char PAD = '=';
46      private static final byte[] BASE_64_ALPHABET = new byte[BASELENGTH];
47      private static final char[] LOOKUP_BASE_64_ALPHABET = new char[LOOKUPLENGTH];
48  
49      static {
50  
51          for (int i = 0; i < BASELENGTH; ++i) {
52              BASE_64_ALPHABET[i] = -1;
53          }
54          for (int i = 'Z'; i >= 'A'; i--) {
55              BASE_64_ALPHABET[i] = (byte) (i - 'A');
56          }
57          for (int i = 'z'; i >= 'a'; i--) {
58              BASE_64_ALPHABET[i] = (byte) (i - 'a' + 26);
59          }
60  
61          for (int i = '9'; i >= '0'; i--) {
62              BASE_64_ALPHABET[i] = (byte) (i - '0' + 52);
63          }
64  
65          BASE_64_ALPHABET['+'] = 62;
66          BASE_64_ALPHABET['/'] = 63;
67  
68          for (int i = 0; i <= 25; i++) {
69              LOOKUP_BASE_64_ALPHABET[i] = (char) ('A' + i);
70          }
71  
72          for (int i = 26, j = 0; i <= 51; i++, j++) {
73              LOOKUP_BASE_64_ALPHABET[i] = (char) ('a' + j);
74          }
75  
76          for (int i = 52, j = 0; i <= 61; i++, j++) {
77              LOOKUP_BASE_64_ALPHABET[i] = (char) ('0' + j);
78          }
79          LOOKUP_BASE_64_ALPHABET[62] = (char) '+';
80          LOOKUP_BASE_64_ALPHABET[63] = (char) '/';
81  
82      }
83  
84      protected static boolean isWhiteSpace(char octect) {
85          return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
86      }
87  
88      protected static boolean isPad(char octect) {
89          return (octect == PAD);
90      }
91  
92      protected static boolean isData(char octect) {
93          return (octect < BASELENGTH && BASE_64_ALPHABET[octect] != -1);
94      }
95  
96      protected static boolean isBase64(char octect) {
97          return (isWhiteSpace(octect) || isPad(octect) || isData(octect));
98      }
99  
100     /**
101      * Encodes hex octects into Base64
102      * 
103      * @param binaryData
104      *            Array containing binaryData
105      * @return Encoded Base64 array
106      */
107     public static String encode(byte[] binaryData) {
108 
109         if (binaryData == null) {
110             return null;
111         }
112 
113         int lengthDataBits = binaryData.length * EIGHTBIT;
114         if (lengthDataBits == 0) {
115             return "";
116         }
117 
118         int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
119         int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
120         int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1
121                 : numberTriplets;
122         char encodedData[] = null;
123 
124         encodedData = new char[numberQuartet * 4];
125 
126         byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
127 
128         int encodedIndex = 0;
129         int dataIndex = 0;
130 
131         for (int i = 0; i < numberTriplets; i++) {
132             b1 = binaryData[dataIndex++];
133             b2 = binaryData[dataIndex++];
134             b3 = binaryData[dataIndex++];
135 
136             l = (byte) (b2 & 0x0f);
137             k = (byte) (b1 & 0x03);
138 
139             byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
140                     : (byte) ((b1) >> 2 ^ 0xc0);
141 
142             byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
143                     : (byte) ((b2) >> 4 ^ 0xf0);
144             byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)
145                     : (byte) ((b3) >> 6 ^ 0xfc);
146 
147             encodedData[encodedIndex++] = LOOKUP_BASE_64_ALPHABET[val1];
148             encodedData[encodedIndex++] = LOOKUP_BASE_64_ALPHABET[val2
149                     | (k << 4)];
150             encodedData[encodedIndex++] = LOOKUP_BASE_64_ALPHABET[(l << 2)
151                     | val3];
152             encodedData[encodedIndex++] = LOOKUP_BASE_64_ALPHABET[b3 & 0x3f];
153         }
154 
155         // form integral number of 6-bit groups
156         if (fewerThan24bits == EIGHTBIT) {
157             b1 = binaryData[dataIndex];
158             k = (byte) (b1 & 0x03);
159 
160             byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
161                     : (byte) ((b1) >> 2 ^ 0xc0);
162             encodedData[encodedIndex++] = LOOKUP_BASE_64_ALPHABET[val1];
163             encodedData[encodedIndex++] = LOOKUP_BASE_64_ALPHABET[k << 4];
164             encodedData[encodedIndex++] = PAD;
165             encodedData[encodedIndex++] = PAD;
166         } else if (fewerThan24bits == SIXTEENBIT) {
167             b1 = binaryData[dataIndex];
168             b2 = binaryData[dataIndex + 1];
169             l = (byte) (b2 & 0x0f);
170             k = (byte) (b1 & 0x03);
171 
172             byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
173                     : (byte) ((b1) >> 2 ^ 0xc0);
174             byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
175                     : (byte) ((b2) >> 4 ^ 0xf0);
176 
177             encodedData[encodedIndex++] = LOOKUP_BASE_64_ALPHABET[val1];
178             encodedData[encodedIndex++] = LOOKUP_BASE_64_ALPHABET[val2
179                     | (k << 4)];
180             encodedData[encodedIndex++] = LOOKUP_BASE_64_ALPHABET[l << 2];
181             encodedData[encodedIndex++] = PAD;
182         }
183 
184         return new String(encodedData);
185     }
186 
187     /**
188      * Decodes Base64 data into octets
189      * 
190      * @param encoded
191      *            string containing Base64 data
192      * @return Array containing decoded data.
193      */
194     public static byte[] decode(String encoded) throws WSSecurityException {
195 
196         if (encoded == null) {
197             return null;
198         }
199 
200         char[] base64Data = encoded.toCharArray();
201         // remove white spaces
202         int len = removeWhiteSpace(base64Data);
203 
204         if (len % FOURBYTE != 0) {
205             throw new WSSecurityException("decoding.divisible.four");
206         }
207 
208         int numberQuadruple = (len / FOURBYTE);
209 
210         if (numberQuadruple == 0) {
211             return new byte[0];
212         }
213 
214         byte decodedData[] = null;
215         byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
216         char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
217 
218         int i = 0;
219         int encodedIndex = 0;
220         int dataIndex = 0;
221         decodedData = new byte[(numberQuadruple) * 3];
222 
223         for (; i < numberQuadruple - 1; i++) {
224 
225             if (!isData((d1 = base64Data[dataIndex++]))
226                     || !isData((d2 = base64Data[dataIndex++]))
227                     || !isData((d3 = base64Data[dataIndex++]))
228                     || !isData((d4 = base64Data[dataIndex++]))) {
229                 throw new WSSecurityException("decoding.general");// if found
230                                                                   // "no data"
231                                                                   // just return
232                                                                   // null
233             }
234 
235             b1 = BASE_64_ALPHABET[d1];
236             b2 = BASE_64_ALPHABET[d2];
237             b3 = BASE_64_ALPHABET[d3];
238             b4 = BASE_64_ALPHABET[d4];
239 
240             decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
241             decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
242             decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
243         }
244 
245         if (!isData((d1 = base64Data[dataIndex++]))
246                 || !isData((d2 = base64Data[dataIndex++]))) {
247             throw new WSSecurityException("decoding.general");// if found
248                                                               // "no data" just
249                                                               // return null
250         }
251 
252         b1 = BASE_64_ALPHABET[d1];
253         b2 = BASE_64_ALPHABET[d2];
254 
255         d3 = base64Data[dataIndex++];
256         d4 = base64Data[dataIndex++];
257         if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters
258             if (isPad(d3) && isPad(d4)) { // Two PAD e.g. 3c[Pad][Pad]
259                 if ((b2 & 0xf) != 0) {// last 4 bits should be zero
260                     throw new WSSecurityException("decoding.general");
261                 }
262                 byte[] tmp = new byte[i * 3 + 1];
263                 System.arraycopy(decodedData, 0, tmp, 0, i * 3);
264                 tmp[encodedIndex] = (byte)(b1 << 2 | b2 >> 4);
265                 return tmp;
266             } else if (!isPad(d3) && isPad(d4)) { // One PAD e.g. 3cQ[Pad]
267                 b3 = BASE_64_ALPHABET[d3];
268                 if ((b3 & 0x3) != 0) { // last 2 bits should be zero
269                     throw new WSSecurityException("decoding.general");
270                 }
271                 byte[] tmp = new byte[i * 3 + 2];
272                 System.arraycopy(decodedData, 0, tmp, 0, i * 3);
273                 tmp[encodedIndex++] = (byte)(b1 << 2 | b2 >> 4);
274                 tmp[encodedIndex] = (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
275                 return tmp;
276             } else {
277                 throw new WSSecurityException("decoding.general");// an error
278                                                                   // like
279                                                                   // "3c[Pad]r",
280                                                                   // "3cdX",
281                                                                   // "3cXd",
282                                                                   // "3cXX"
283                                                                   // where X is
284                                                                   // non data
285             }
286         } else { // No PAD e.g 3cQl
287             b3 = BASE_64_ALPHABET[d3];
288             b4 = BASE_64_ALPHABET[d4];
289             decodedData[encodedIndex++] = (byte)(b1 << 2 | b2 >> 4);
290             decodedData[encodedIndex++] = (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
291             decodedData[encodedIndex++] = (byte)(b3 << 6 | b4);
292         }
293 
294         return decodedData;
295     }
296 
297     /**
298      * remove WhiteSpace from MIME containing encoded Base64 data.
299      * 
300      * @param data
301      *            the byte array of base64 data (with WS)
302      * @return the new length
303      */
304     protected static int removeWhiteSpace(char[] data) {
305         if (data == null) {
306             return 0;
307         }
308 
309         // count characters that's not whitespace
310         int newSize = 0;
311         int len = data.length;
312         for (int i = 0; i < len; i++) {
313             if (!isWhiteSpace(data[i])) {
314                 data[newSize++] = data[i];
315             }
316         }
317         return newSize;
318     }
319 }