001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.geronimo.crypto.encoders;
019
020 import java.io.IOException;
021 import java.io.OutputStream;
022
023 public class Base64Encoder
024 implements Encoder
025 {
026 protected final byte[] encodingTable =
027 {
028 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
029 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
030 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
031 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
032 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
033 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
034 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
035 (byte)'v',
036 (byte)'w', (byte)'x', (byte)'y', (byte)'z',
037 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
038 (byte)'7', (byte)'8', (byte)'9',
039 (byte)'+', (byte)'/'
040 };
041
042 protected byte padding = (byte)'=';
043
044 /*
045 * set up the decoding table.
046 */
047 protected final byte[] decodingTable = new byte[128];
048
049 protected void initialiseDecodingTable()
050 {
051 for (int i = 0; i < encodingTable.length; i++)
052 {
053 decodingTable[encodingTable[i]] = (byte)i;
054 }
055 }
056
057 public Base64Encoder()
058 {
059 initialiseDecodingTable();
060 }
061
062 /**
063 * encode the input data producing a base 64 output stream.
064 *
065 * @return the number of bytes produced.
066 */
067 public int encode(
068 byte[] data,
069 int off,
070 int length,
071 OutputStream out)
072 throws IOException
073 {
074 int modulus = length % 3;
075 int dataLength = (length - modulus);
076 int a1, a2, a3;
077
078 for (int i = off; i < off + dataLength; i += 3)
079 {
080 a1 = data[i] & 0xff;
081 a2 = data[i + 1] & 0xff;
082 a3 = data[i + 2] & 0xff;
083
084 out.write(encodingTable[(a1 >>> 2) & 0x3f]);
085 out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
086 out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
087 out.write(encodingTable[a3 & 0x3f]);
088 }
089
090 /*
091 * process the tail end.
092 */
093 int b1, b2, b3;
094 int d1, d2;
095
096 switch (modulus)
097 {
098 case 0: /* nothing left to do */
099 break;
100 case 1:
101 d1 = data[off + dataLength] & 0xff;
102 b1 = (d1 >>> 2) & 0x3f;
103 b2 = (d1 << 4) & 0x3f;
104
105 out.write(encodingTable[b1]);
106 out.write(encodingTable[b2]);
107 out.write(padding);
108 out.write(padding);
109 break;
110 case 2:
111 d1 = data[off + dataLength] & 0xff;
112 d2 = data[off + dataLength + 1] & 0xff;
113
114 b1 = (d1 >>> 2) & 0x3f;
115 b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
116 b3 = (d2 << 2) & 0x3f;
117
118 out.write(encodingTable[b1]);
119 out.write(encodingTable[b2]);
120 out.write(encodingTable[b3]);
121 out.write(padding);
122 break;
123 }
124
125 return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4);
126 }
127
128 private boolean ignore(
129 char c)
130 {
131 return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
132 }
133
134 /**
135 * decode the base 64 encoded byte data writing it to the given output stream,
136 * whitespace characters will be ignored.
137 *
138 * @return the number of bytes produced.
139 */
140 public int decode(
141 byte[] data,
142 int off,
143 int length,
144 OutputStream out)
145 throws IOException
146 {
147 byte[] bytes;
148 byte b1, b2, b3, b4;
149 int outLen = 0;
150
151 int end = off + length;
152
153 while (end > 0)
154 {
155 if (!ignore((char)data[end - 1]))
156 {
157 break;
158 }
159
160 end--;
161 }
162
163 int i = off;
164 int finish = end - 4;
165
166 while (i < finish)
167 {
168 while ((i < finish) && ignore((char)data[i]))
169 {
170 i++;
171 }
172
173 b1 = decodingTable[data[i++]];
174
175 while ((i < finish) && ignore((char)data[i]))
176 {
177 i++;
178 }
179
180 b2 = decodingTable[data[i++]];
181
182 while ((i < finish) && ignore((char)data[i]))
183 {
184 i++;
185 }
186
187 b3 = decodingTable[data[i++]];
188
189 while ((i < finish) && ignore((char)data[i]))
190 {
191 i++;
192 }
193
194 b4 = decodingTable[data[i++]];
195
196 out.write((b1 << 2) | (b2 >> 4));
197 out.write((b2 << 4) | (b3 >> 2));
198 out.write((b3 << 6) | b4);
199
200 outLen += 3;
201 }
202
203 if (data[end - 2] == padding)
204 {
205 b1 = decodingTable[data[end - 4]];
206 b2 = decodingTable[data[end - 3]];
207
208 out.write((b1 << 2) | (b2 >> 4));
209
210 outLen += 1;
211 }
212 else if (data[end - 1] == padding)
213 {
214 b1 = decodingTable[data[end - 4]];
215 b2 = decodingTable[data[end - 3]];
216 b3 = decodingTable[data[end - 2]];
217
218 out.write((b1 << 2) | (b2 >> 4));
219 out.write((b2 << 4) | (b3 >> 2));
220
221 outLen += 2;
222 }
223 else
224 {
225 b1 = decodingTable[data[end - 4]];
226 b2 = decodingTable[data[end - 3]];
227 b3 = decodingTable[data[end - 2]];
228 b4 = decodingTable[data[end - 1]];
229
230 out.write((b1 << 2) | (b2 >> 4));
231 out.write((b2 << 4) | (b3 >> 2));
232 out.write((b3 << 6) | b4);
233
234 outLen += 3;
235 }
236
237 return outLen;
238 }
239
240 /**
241 * decode the base 64 encoded String data writing it to the given output stream,
242 * whitespace characters will be ignored.
243 *
244 * @return the number of bytes produced.
245 */
246 public int decode(
247 String data,
248 OutputStream out)
249 throws IOException
250 {
251 byte[] bytes;
252 byte b1, b2, b3, b4;
253 int length = 0;
254
255 int end = data.length();
256
257 while (end > 0)
258 {
259 if (!ignore(data.charAt(end - 1)))
260 {
261 break;
262 }
263
264 end--;
265 }
266
267 int i = 0;
268 int finish = end - 4;
269
270 while (i < finish)
271 {
272 while ((i < finish) && ignore(data.charAt(i)))
273 {
274 i++;
275 }
276
277 b1 = decodingTable[data.charAt(i++)];
278
279 while ((i < finish) && ignore(data.charAt(i)))
280 {
281 i++;
282 }
283 b2 = decodingTable[data.charAt(i++)];
284
285 while ((i < finish) && ignore(data.charAt(i)))
286 {
287 i++;
288 }
289 b3 = decodingTable[data.charAt(i++)];
290
291 while ((i < finish) && ignore(data.charAt(i)))
292 {
293 i++;
294 }
295 b4 = decodingTable[data.charAt(i++)];
296
297 out.write((b1 << 2) | (b2 >> 4));
298 out.write((b2 << 4) | (b3 >> 2));
299 out.write((b3 << 6) | b4);
300
301 length += 3;
302 }
303
304 if (data.charAt(end - 2) == padding)
305 {
306 b1 = decodingTable[data.charAt(end - 4)];
307 b2 = decodingTable[data.charAt(end - 3)];
308
309 out.write((b1 << 2) | (b2 >> 4));
310
311 length += 1;
312 }
313 else if (data.charAt(end - 1) == padding)
314 {
315 b1 = decodingTable[data.charAt(end - 4)];
316 b2 = decodingTable[data.charAt(end - 3)];
317 b3 = decodingTable[data.charAt(end - 2)];
318
319 out.write((b1 << 2) | (b2 >> 4));
320 out.write((b2 << 4) | (b3 >> 2));
321
322 length += 2;
323 }
324 else
325 {
326 b1 = decodingTable[data.charAt(end - 4)];
327 b2 = decodingTable[data.charAt(end - 3)];
328 b3 = decodingTable[data.charAt(end - 2)];
329 b4 = decodingTable[data.charAt(end - 1)];
330
331 out.write((b1 << 2) | (b2 >> 4));
332 out.write((b2 << 4) | (b3 >> 2));
333 out.write((b3 << 6) | b4);
334
335 length += 3;
336 }
337
338 return length;
339 }
340 }