1 // ========================================================================
2 // Copyright (c) 1999-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14 package org.eclipse.jetty.util;
15
16 import java.io.ByteArrayOutputStream;
17 import java.io.UnsupportedEncodingException;
18
19
20 /* ------------------------------------------------------------ */
21 /** Fast B64 Encoder/Decoder as described in RFC 1421.
22 * <p>Does not insert or interpret whitespace as described in RFC
23 * 1521. If you require this you must pre/post process your data.
24 * <p> Note that in a web context the usual case is to not want
25 * linebreaks or other white space in the encoded output.
26 *
27 */
28 public class B64Code
29 {
30 // ------------------------------------------------------------------
31 static final char pad='=';
32 static final char[] rfc1421alphabet=
33 {
34 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
35 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
36 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
37 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
38 };
39
40 static final byte[] rfc1421nibbles;
41
42 static
43 {
44 rfc1421nibbles=new byte[256];
45 for (int i=0;i<256;i++)
46 rfc1421nibbles[i]=-1;
47 for (byte b=0;b<64;b++)
48 rfc1421nibbles[(byte)rfc1421alphabet[b]]=b;
49 rfc1421nibbles[(byte)pad]=0;
50 }
51
52 // ------------------------------------------------------------------
53 /**
54 * Base 64 encode as described in RFC 1421.
55 * <p>Does not insert whitespace as described in RFC 1521.
56 * @param s String to encode.
57 * @return String containing the encoded form of the input.
58 */
59 static public String encode(String s)
60 {
61 try
62 {
63 return encode(s,null);
64 }
65 catch (UnsupportedEncodingException e)
66 {
67 throw new IllegalArgumentException(e.toString());
68 }
69 }
70
71 // ------------------------------------------------------------------
72 /**
73 * Base 64 encode as described in RFC 1421.
74 * <p>Does not insert whitespace as described in RFC 1521.
75 * @param s String to encode.
76 * @param charEncoding String representing the name of
77 * the character encoding of the provided input String.
78 * @return String containing the encoded form of the input.
79 */
80 static public String encode(String s,String charEncoding)
81 throws UnsupportedEncodingException
82 {
83 byte[] bytes;
84 if (charEncoding==null)
85 bytes=s.getBytes(StringUtil.__ISO_8859_1);
86 else
87 bytes=s.getBytes(charEncoding);
88
89 return new String(encode(bytes));
90 }
91
92 // ------------------------------------------------------------------
93 /**
94 * Fast Base 64 encode as described in RFC 1421.
95 * <p>Does not insert whitespace as described in RFC 1521.
96 * <p> Avoids creating extra copies of the input/output.
97 * @param b byte array to encode.
98 * @return char array containing the encoded form of the input.
99 */
100 static public char[] encode(byte[] b)
101 {
102 return encode(b,false);
103 }
104
105 // ------------------------------------------------------------------
106 /**
107 * Fast Base 64 encode as described in RFC 1421 and RFC2045
108 * <p>Does not insert whitespace as described in RFC 1521, unless rfc2045 is passed as true.
109 * <p> Avoids creating extra copies of the input/output.
110 * @param b byte array to encode.
111 * @param rfc2045 If true, break lines at 76 characters with CRLF
112 * @return char array containing the encoded form of the input.
113 */
114 static public char[] encode(byte[] b, boolean rfc2045)
115 {
116 if (b==null)
117 return null;
118
119 int bLen=b.length;
120 int cLen=((bLen+2)/3)*4;
121 if (rfc2045)
122 cLen+=2+2*cLen/76;
123 char c[]=new char[cLen];
124 int ci=0;
125 int bi=0;
126 byte b0, b1, b2;
127 int stop=(bLen/3)*3;
128 int l=0;
129 while (bi<stop)
130 {
131 b0=b[bi++];
132 b1=b[bi++];
133 b2=b[bi++];
134 c[ci++]=rfc1421alphabet[(b0>>>2)&0x3f];
135 c[ci++]=rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
136 c[ci++]=rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03];
137 c[ci++]=rfc1421alphabet[b2&077];
138 l+=4;
139 if (rfc2045 && l%76==0)
140 {
141 c[ci++]=13;
142 c[ci++]=10;
143 }
144 }
145
146 if (bLen!=bi)
147 {
148 switch (bLen%3)
149 {
150 case 2:
151 b0=b[bi++];
152 b1=b[bi++];
153 c[ci++]=rfc1421alphabet[(b0>>>2)&0x3f];
154 c[ci++]=rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
155 c[ci++]=rfc1421alphabet[(b1<<2)&0x3f];
156 c[ci++]=pad;
157 break;
158
159 case 1:
160 b0=b[bi++];
161 c[ci++]=rfc1421alphabet[(b0>>>2)&0x3f];
162 c[ci++]=rfc1421alphabet[(b0<<4)&0x3f];
163 c[ci++]=pad;
164 c[ci++]=pad;
165 break;
166
167 default:
168 break;
169 }
170 }
171
172 if (rfc2045)
173 {
174 c[ci++]=13;
175 c[ci++]=10;
176 }
177 return c;
178 }
179
180 // ------------------------------------------------------------------
181 /**
182 * Base 64 decode as described in RFC 2045.
183 * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
184 * @param encoded String to decode.
185 * @param charEncoding String representing the character encoding
186 * used to map the decoded bytes into a String.
187 * @return String decoded byte array.
188 * @throws UnsupportedEncodingException if the encoding is not supported
189 * @throws IllegalArgumentException if the input is not a valid
190 * B64 encoding.
191 */
192 static public String decode(String encoded,String charEncoding)
193 throws UnsupportedEncodingException
194 {
195 byte[] decoded=decode(encoded);
196 if (charEncoding==null)
197 return new String(decoded);
198 return new String(decoded,charEncoding);
199 }
200
201 /* ------------------------------------------------------------ */
202 /**
203 * Fast Base 64 decode as described in RFC 1421.
204 *
205 * <p>Unlike other decode methods, this does not attempt to
206 * cope with extra whitespace as described in RFC 1521/2045.
207 * <p> Avoids creating extra copies of the input/output.
208 * <p> Note this code has been flattened for performance.
209 * @param b char array to decode.
210 * @return byte array containing the decoded form of the input.
211 * @throws IllegalArgumentException if the input is not a valid
212 * B64 encoding.
213 */
214 static public byte[] decode(char[] b)
215 {
216 if (b==null)
217 return null;
218
219 int bLen=b.length;
220 if (bLen%4!=0)
221 throw new IllegalArgumentException("Input block size is not 4");
222
223 int li=bLen-1;
224 while (li>=0 && b[li]==(byte)pad)
225 li--;
226
227 if (li<0)
228 return new byte[0];
229
230 // Create result array of exact required size.
231 int rLen=((li+1)*3)/4;
232 byte r[]=new byte[rLen];
233 int ri=0;
234 int bi=0;
235 int stop=(rLen/3)*3;
236 byte b0,b1,b2,b3;
237 try
238 {
239 while (ri<stop)
240 {
241 b0=rfc1421nibbles[b[bi++]];
242 b1=rfc1421nibbles[b[bi++]];
243 b2=rfc1421nibbles[b[bi++]];
244 b3=rfc1421nibbles[b[bi++]];
245 if (b0<0 || b1<0 || b2<0 || b3<0)
246 throw new IllegalArgumentException("Not B64 encoded");
247
248 r[ri++]=(byte)(b0<<2|b1>>>4);
249 r[ri++]=(byte)(b1<<4|b2>>>2);
250 r[ri++]=(byte)(b2<<6|b3);
251 }
252
253 if (rLen!=ri)
254 {
255 switch (rLen%3)
256 {
257 case 2:
258 b0=rfc1421nibbles[b[bi++]];
259 b1=rfc1421nibbles[b[bi++]];
260 b2=rfc1421nibbles[b[bi++]];
261 if (b0<0 || b1<0 || b2<0)
262 throw new IllegalArgumentException("Not B64 encoded");
263 r[ri++]=(byte)(b0<<2|b1>>>4);
264 r[ri++]=(byte)(b1<<4|b2>>>2);
265 break;
266
267 case 1:
268 b0=rfc1421nibbles[b[bi++]];
269 b1=rfc1421nibbles[b[bi++]];
270 if (b0<0 || b1<0)
271 throw new IllegalArgumentException("Not B64 encoded");
272 r[ri++]=(byte)(b0<<2|b1>>>4);
273 break;
274
275 default:
276 break;
277 }
278 }
279 }
280 catch (IndexOutOfBoundsException e)
281 {
282 throw new IllegalArgumentException("char "+bi
283 +" was not B64 encoded");
284 }
285
286 return r;
287 }
288
289 /* ------------------------------------------------------------ */
290 /**
291 * Base 64 decode as described in RFC 2045.
292 * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
293 * @param encoded String to decode.
294 * @return byte array containing the decoded form of the input.
295 * @throws IllegalArgumentException if the input is not a valid
296 * B64 encoding.
297 */
298 static public byte[] decode(String encoded)
299 {
300 if (encoded==null)
301 return null;
302
303 int ci=0;
304 byte nibbles[] = new byte[4];
305 int s=0;
306 ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);
307
308 while (ci<encoded.length())
309 {
310 char c=encoded.charAt(ci++);
311
312 if (c==pad)
313 break;
314
315 if (Character.isWhitespace(c))
316 continue;
317
318 byte nibble=rfc1421nibbles[c];
319 if (nibble<0)
320 throw new IllegalArgumentException("Not B64 encoded");
321
322 nibbles[s++]=rfc1421nibbles[c];
323
324 switch(s)
325 {
326 case 1:
327 break;
328 case 2:
329 bout.write(nibbles[0]<<2|nibbles[1]>>>4);
330 break;
331 case 3:
332 bout.write(nibbles[1]<<4|nibbles[2]>>>2);
333 break;
334 case 4:
335 bout.write(nibbles[2]<<6|nibbles[3]);
336 s=0;
337 break;
338 }
339
340 }
341
342 return bout.toByteArray();
343 }
344 }