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