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.http.security;
15
16 import java.io.UnsupportedEncodingException;
17
18 import org.eclipse.jetty.util.StringUtil;
19
20 /* ------------------------------------------------------------ */
21
22 /** Fast B64 Encoder/Decoder as described in RFC 1421.
23 * <p>Does not insert or interpret whitespace as described in RFC
24 * 1521. If you require this you must pre/post process your data.
25 * <p> Note that in a web context the usual case is to not want
26 * linebreaks or other white space in the encoded output.
27 *
28 *
29 *
30 */
31 public class B64Code
32 {
33 // ------------------------------------------------------------------
34 static final char pad='=';
35 static final char[] nibble2code=
36 {
37 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
38 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
39 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
40 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
41 };
42
43 static final byte[] code2nibble;
44
45 static
46 {
47 code2nibble=new byte[256];
48 for (int i=0;i<256;i++)
49 code2nibble[i]=-1;
50 for (byte b=0;b<64;b++)
51 code2nibble[(byte)nibble2code[b]]=b;
52 code2nibble[(byte)pad]=0;
53 }
54
55 // ------------------------------------------------------------------
56 /**
57 * Base 64 encode as described in RFC 1421.
58 * <p>Does not insert whitespace as described in RFC 1521.
59 * @param s String to encode.
60 * @return String containing the encoded form of the input.
61 */
62 static public String encode(String s)
63 {
64 try
65 {
66 return encode(s,null);
67 }
68 catch (UnsupportedEncodingException e)
69 {
70 throw new IllegalArgumentException(e.toString());
71 }
72 }
73
74 // ------------------------------------------------------------------
75 /**
76 * Base 64 encode as described in RFC 1421.
77 * <p>Does not insert whitespace as described in RFC 1521.
78 * @param s String to encode.
79 * @param charEncoding String representing the name of
80 * the character encoding of the provided input String.
81 * @return String containing the encoded form of the input.
82 */
83 static public String encode(String s,String charEncoding)
84 throws UnsupportedEncodingException
85 {
86 byte[] bytes;
87 if (charEncoding==null)
88 bytes=s.getBytes(StringUtil.__ISO_8859_1);
89 else
90 bytes=s.getBytes(charEncoding);
91
92 return new String(encode(bytes));
93 }
94
95 // ------------------------------------------------------------------
96 /**
97 * Fast Base 64 encode as described in RFC 1421.
98 * <p>Does not insert whitespace as described in RFC 1521.
99 * <p> Avoids creating extra copies of the input/output.
100 * @param b byte array to encode.
101 * @return char array containing the encoded form of the input.
102 */
103 static public char[] encode(byte[] b)
104 {
105 if (b==null)
106 return null;
107
108 int bLen=b.length;
109 char r[]=new char[((bLen+2)/3)*4];
110 int ri=0;
111 int bi=0;
112 byte b0, b1, b2;
113 int stop=(bLen/3)*3;
114 while (bi<stop)
115 {
116 b0=b[bi++];
117 b1=b[bi++];
118 b2=b[bi++];
119 r[ri++]=nibble2code[(b0>>>2)&0x3f];
120 r[ri++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
121 r[ri++]=nibble2code[(b1<<2)&0x3f|(b2>>>6)&0x03];
122 r[ri++]=nibble2code[b2&077];
123 }
124
125 if (bLen!=bi)
126 {
127 switch (bLen%3)
128 {
129 case 2:
130 b0=b[bi++];
131 b1=b[bi++];
132 r[ri++]=nibble2code[(b0>>>2)&0x3f];
133 r[ri++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
134 r[ri++]=nibble2code[(b1<<2)&0x3f];
135 r[ri++]=pad;
136 break;
137
138 case 1:
139 b0=b[bi++];
140 r[ri++]=nibble2code[(b0>>>2)&0x3f];
141 r[ri++]=nibble2code[(b0<<4)&0x3f];
142 r[ri++]=pad;
143 r[ri++]=pad;
144 break;
145
146 default:
147 break;
148 }
149 }
150
151 return r;
152 }
153
154 // ------------------------------------------------------------------
155 /**
156 * Base 64 decode as described in RFC 1421.
157 * <p>Does not attempt to cope with extra whitespace
158 * as described in RFC 1521.
159 * @param s String to decode
160 * @return String decoded byte array.
161 */
162 static public String decode(String s)
163 {
164 try
165 {
166 return decode(s,StringUtil.__ISO_8859_1);
167 }
168 catch (UnsupportedEncodingException e)
169 {
170 throw new IllegalArgumentException(e.toString());
171 }
172 }
173
174 // ------------------------------------------------------------------
175 /**
176 * Base 64 decode as described in RFC 1421.
177 * <p>Does not attempt to cope with extra whitespace
178 * as described in RFC 1521.
179 * @param s String to decode
180 * @param charEncoding String representing the character encoding
181 * used to map the decoded bytes into a String.
182 * @return String decoded byte array.
183 */
184 static public String decode(String s,String charEncoding)
185 throws UnsupportedEncodingException
186 {
187 byte[] decoded=decode(s.toCharArray());
188
189 if (charEncoding==null)
190 return new String(decoded);
191 return new String(decoded,charEncoding);
192 }
193
194 /* ------------------------------------------------------------ */
195 /**
196 * Fast Base 64 decode as described in RFC 1421.
197 * <p>Does not attempt to cope with extra whitespace
198 * as described in RFC 1521.
199 * <p> Avoids creating extra copies of the input/output.
200 * <p> Note this code has been flattened for performance.
201 * @param b char array to decode.
202 * @return byte array containing the decoded form of the input.
203 * @throws IllegalArgumentException if the input is not a valid
204 * B64 encoding.
205 */
206 static public byte[] decode(char[] b)
207 {
208 if (b==null)
209 return null;
210
211 int bLen=b.length;
212 if (bLen%4!=0)
213 throw new IllegalArgumentException("Input block size is not 4");
214
215 int li=bLen-1;
216 while (li>=0 && b[li]==(byte)pad)
217 li--;
218
219 if (li<0)
220 return new byte[0];
221
222 // Create result array of exact required size.
223 int rLen=((li+1)*3)/4;
224 byte r[]=new byte[rLen];
225 int ri=0;
226 int bi=0;
227 int stop=(rLen/3)*3;
228 byte b0,b1,b2,b3;
229 try
230 {
231 while (ri<stop)
232 {
233 b0=code2nibble[b[bi++]];
234 b1=code2nibble[b[bi++]];
235 b2=code2nibble[b[bi++]];
236 b3=code2nibble[b[bi++]];
237 if (b0<0 || b1<0 || b2<0 || b3<0)
238 throw new IllegalArgumentException("Not B64 encoded");
239
240 r[ri++]=(byte)(b0<<2|b1>>>4);
241 r[ri++]=(byte)(b1<<4|b2>>>2);
242 r[ri++]=(byte)(b2<<6|b3);
243 }
244
245 if (rLen!=ri)
246 {
247 switch (rLen%3)
248 {
249 case 2:
250 b0=code2nibble[b[bi++]];
251 b1=code2nibble[b[bi++]];
252 b2=code2nibble[b[bi++]];
253 if (b0<0 || b1<0 || b2<0)
254 throw new IllegalArgumentException("Not B64 encoded");
255 r[ri++]=(byte)(b0<<2|b1>>>4);
256 r[ri++]=(byte)(b1<<4|b2>>>2);
257 break;
258
259 case 1:
260 b0=code2nibble[b[bi++]];
261 b1=code2nibble[b[bi++]];
262 if (b0<0 || b1<0)
263 throw new IllegalArgumentException("Not B64 encoded");
264 r[ri++]=(byte)(b0<<2|b1>>>4);
265 break;
266
267 default:
268 break;
269 }
270 }
271 }
272 catch (IndexOutOfBoundsException e)
273 {
274 throw new IllegalArgumentException("char "+bi
275 +" was not B64 encoded");
276 }
277
278 return r;
279 }
280 }