View Javadoc

1   /*
2    * The MIT License
3    *
4    * Original work sponsored and donated by National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
5    *
6    * Copyright (C) 2011 National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
7    *
8    * Permission is hereby granted, free of charge, to any person obtaining a copy of
9    * this software and associated documentation files (the "Software"), to deal in
10   * the Software without restriction, including without limitation the rights to
11   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12   * of the Software, and to permit persons to whom the Software is furnished to do
13   * so, subject to the following conditions:
14   *
15   * The above copyright notice and this permission notice shall be included in all
16   * copies or substantial portions of the Software.
17   *
18   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   * SOFTWARE.
25   *
26   * $HeadURL: https://svn.softwareborsen.dk/sosi/trunk/modules/seal/src/main/java/dk/sosi/seal/xml/Base64.java $
27   * $Id: Base64.java 8697 2011-09-02 10:33:55Z chg@lakeside.dk $
28   */
29  
30  package dk.sosi.seal.xml;
31  
32  /**
33   * This class provides encode/decode for RFC 2045 Base64 as defined by RFC 2045,
34   * N. Freed and N. Borenstein. RFC 2045: Multipurpose Internet Mail Extensions
35   * (MIME) Part One: Format of Internet Message Bodies. Reference 1996 Available
36   * at: http://www.ietf.org/rfc/rfc2045.txt This class is used by XML Schema
37   * binary format validation
38   * 
39   * This implementation does not encode/decode streaming data. You need the data
40   * that you will encode/decode already on a byte arrray.
41   * 
42   * @author Jeffrey Rodriguez
43   * @author Sandy Gao
44   * @version $Id: Base64.java 8697 2011-09-02 10:33:55Z chg@lakeside.dk $
45   */
46  @Deprecated
47  public final class Base64 { //NOPMD
48  
49  	static private final int BASELENGTH = 128;
50  
51  	static private final int LOOKUPLENGTH = 64;
52  
53  	static private final int TWENTYFOURBITGROUP = 24;
54  
55  	static private final int EIGHTBIT = 8;
56  
57  	static private final int SIXTEENBIT = 16;
58  
59  	static private final int FOURBYTE = 4;
60  
61  	static private final int SIGN = -128;
62  
63  	static private final char PAD = '=';
64  
65  	static final private byte[] base64Alphabet = new byte[BASELENGTH];
66  
67  	static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
68  
69  	static {
70  
71  		for (int i = 0; i < BASELENGTH; ++i) {
72  			base64Alphabet[i] = -1;
73  		}
74  		for (int i = 'Z'; i >= 'A'; i--) {
75  			base64Alphabet[i] = (byte) (i - 'A');
76  		}
77  		for (int i = 'z'; i >= 'a'; i--) {
78  			base64Alphabet[i] = (byte) (i - 'a' + 26);
79  		}
80  
81  		for (int i = '9'; i >= '0'; i--) {
82  			base64Alphabet[i] = (byte) (i - '0' + 52);
83  		}
84  
85  		base64Alphabet['+'] = 62;
86  		base64Alphabet['/'] = 63;
87  
88  		for (int i = 0; i <= 25; i++)
89  			lookUpBase64Alphabet[i] = (char) ('A' + i);
90  
91  		for (int i = 26, j = 0; i <= 51; i++, j++)
92  			lookUpBase64Alphabet[i] = (char) ('a' + j);
93  
94  		for (int i = 52, j = 0; i <= 61; i++, j++)
95  			lookUpBase64Alphabet[i] = (char) ('0' + j);
96  		lookUpBase64Alphabet[62] = '+';
97  		lookUpBase64Alphabet[63] = '/';
98  
99  	}
100 
101 	private static boolean isWhiteSpace(char octect) {
102 		return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
103 	}
104 
105 	private static boolean isPad(char octect) {
106 		return (octect == PAD);
107 	}
108 
109 	private static boolean isData(char octect) {
110 		return (octect < BASELENGTH && base64Alphabet[octect] != -1);
111 	}
112 
113 	/**
114 	 * Encodes hex octects into Base64
115 	 * 
116 	 * @param binaryData
117 	 *            Array containing binaryData
118 	 * @return Encoded Base64 array
119 	 */
120 	public static String encode(byte[] binaryData) {
121 		if (binaryData == null || binaryData.length == 0)
122 			return null;
123 
124 		int lengthDataBits = binaryData.length * EIGHTBIT;
125 		int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
126 		int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
127 		int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1
128 				: numberTriplets;
129 		char encodedData[] = null;
130 
131 		encodedData = new char[numberQuartet * 4];
132 
133 		byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
134 
135 		int encodedIndex = 0;
136 		int dataIndex = 0;
137 
138 		for (int i = 0; i < numberTriplets; i++) {
139 			b1 = binaryData[dataIndex++];
140 			b2 = binaryData[dataIndex++];
141 			b3 = binaryData[dataIndex++];
142 
143 			l = (byte) (b2 & 0x0f);
144 			k = (byte) (b1 & 0x03);
145 
146 			byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
147 					: (byte) ((b1) >> 2 ^ 0xc0);
148 
149 			byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
150 					: (byte) ((b2) >> 4 ^ 0xf0);
151 			byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)
152 					: (byte) ((b3) >> 6 ^ 0xfc);
153 
154 			encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
155 			encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
156 			encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
157 			encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
158 		}
159 
160 		// form integral number of 6-bit groups
161 		if (fewerThan24bits == EIGHTBIT) {
162 			b1 = binaryData[dataIndex];
163 			k = (byte) (b1 & 0x03);
164 			byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
165 					: (byte) ((b1) >> 2 ^ 0xc0);
166 			encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
167 			encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
168 			encodedData[encodedIndex++] = PAD;
169 			encodedData[encodedIndex++] = PAD;
170 		} else if (fewerThan24bits == SIXTEENBIT) {
171 			b1 = binaryData[dataIndex];
172 			b2 = binaryData[dataIndex + 1];
173 			l = (byte) (b2 & 0x0f);
174 			k = (byte) (b1 & 0x03);
175 
176 			byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
177 					: (byte) ((b1) >> 2 ^ 0xc0);
178 			byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
179 					: (byte) ((b2) >> 4 ^ 0xf0);
180 
181 			encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
182 			encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
183 			encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
184 			encodedData[encodedIndex++] = PAD;
185 		}
186 
187 		return new String(encodedData);
188 	}
189 
190 	/**
191 	 * Decodes Base64 data into octects
192 	 * 
193 	 * @param encoded
194 	 *            string containing Base64 data
195 	 * @return Array containind decoded data.
196 	 */
197 	public static byte[] decode(String encoded) {
198 
199 		if (encoded == null)
200 			return null;
201 
202 		char[] base64Data = encoded.toCharArray();
203 		// remove white spaces
204 		int len = removeWhiteSpace(base64Data);
205 
206 		if (len % FOURBYTE != 0) {
207 			return null;// should be divisible by four
208 		}
209 
210 		int numberQuadruple = (len / FOURBYTE);
211 
212 		if (numberQuadruple == 0)
213 			return new byte[0];
214 
215 		byte decodedData[] = null;
216 		byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
217 		char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
218 
219 		int i = 0;
220 		int encodedIndex = 0;
221 		int dataIndex = 0;
222 		decodedData = new byte[(numberQuadruple) * 3];
223 
224 		for (; i < numberQuadruple - 1; i++) {
225 
226 			if (!isData((d1 = base64Data[dataIndex++]))
227 					|| !isData((d2 = base64Data[dataIndex++]))
228 					|| !isData((d3 = base64Data[dataIndex++]))
229 					|| !isData((d4 = base64Data[dataIndex++])))
230 				return null;// if found "no data" just return null
231 
232 			b1 = base64Alphabet[d1];
233 			b2 = base64Alphabet[d2];
234 			b3 = base64Alphabet[d3];
235 			b4 = base64Alphabet[d4];
236 
237 			decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
238 			decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
239 			decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
240 		}
241 
242 		if (!isData((d1 = base64Data[dataIndex++]))
243 				|| !isData((d2 = base64Data[dataIndex++]))) {
244 			return null;// if found "no data" just return null
245 		}
246 
247 		b1 = base64Alphabet[d1];
248 		b2 = base64Alphabet[d2];
249 
250 		d3 = base64Data[dataIndex++];
251 		d4 = base64Data[dataIndex++];
252 		if (!isData((d3)) || !isData((d4))) {// Check if they are PAD
253 												// characters
254 			if (isPad(d3) && isPad(d4)) { // Two PAD e.g. 3c[Pad][Pad]
255 				if ((b2 & 0xf) != 0)// last 4 bits should be zero
256 					return null;
257 				byte[] tmp = new byte[i * 3 + 1];
258 				System.arraycopy(decodedData, 0, tmp, 0, i * 3);
259 				tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
260 				return tmp;
261 			} else if (!isPad(d3) && isPad(d4)) { // One PAD e.g. 3cQ[Pad]
262 				b3 = base64Alphabet[d3];
263 				if ((b3 & 0x3) != 0)// last 2 bits should be zero
264 					return null;
265 				byte[] tmp = new byte[i * 3 + 2];
266 				System.arraycopy(decodedData, 0, tmp, 0, i * 3);
267 				tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
268 				tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
269 				return tmp;
270 			} else {
271 				return null;// an error like "3c[Pad]r", "3cdX", "3cXd", "3cXX"
272 							// where X is non data
273 			}
274 		}
275 
276 		// No PAD e.g 3cQl
277 		b3 = base64Alphabet[d3];
278 		b4 = base64Alphabet[d4];
279 		decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
280 		decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
281 		decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
282 
283 		return decodedData;
284 	}
285 
286 	/**
287 	 * remove WhiteSpace from MIME containing encoded Base64 data.
288 	 * 
289 	 * @param data
290 	 *            the byte array of base64 data (with WS)
291 	 * @return the new length
292 	 */
293 	private static int removeWhiteSpace(char[] data) {
294 		// count characters that's not whitespace
295 		int newSize = 0;
296 		int len = data.length;
297 		for (int i = 0; i < len; i++) {
298 			if (!isWhiteSpace(data[i]))
299 				data[newSize++] = data[i];
300 		}
301 		return newSize;
302 	}
303 }