001: // ========================================================================
002: // Copyright 1999-2005 Mort Bay Consulting Pty. Ltd.
003: // ------------------------------------------------------------------------
004: // Licensed under the Apache License, Version 2.0 (the "License");
005: // you may not use this file except in compliance with the License.
006: // You may obtain a copy of the License at
007: // http://www.apache.org/licenses/LICENSE-2.0
008: // Unless required by applicable law or agreed to in writing, software
009: // distributed under the License is distributed on an "AS IS" BASIS,
010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: // See the License for the specific language governing permissions and
012: // limitations under the License.
013: // ========================================================================
014:
015: package org.mortbay.jetty.security;
016:
017: import java.io.UnsupportedEncodingException;
018:
019: import org.mortbay.util.StringUtil;
020:
021: /* ------------------------------------------------------------ */
022:
023: /** Fast B64 Encoder/Decoder as described in RFC 1421.
024: * <p>Does not insert or interpret whitespace as described in RFC
025: * 1521. If you require this you must pre/post process your data.
026: * <p> Note that in a web context the usual case is to not want
027: * linebreaks or other white space in the encoded output.
028: *
029: * @author Brett Sealey (bretts)
030: * @author Greg Wilkins (gregw)
031: */
032: public class B64Code {
033: // ------------------------------------------------------------------
034: static final char pad = '=';
035: static final char[] nibble2code = { 'A', 'B', 'C', 'D', 'E', 'F',
036: 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
037: 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
038: 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
039: 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1',
040: '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
041:
042: static byte[] code2nibble = null;
043:
044: static {
045: code2nibble = new byte[256];
046: for (int i = 0; i < 256; i++)
047: code2nibble[i] = -1;
048: for (byte b = 0; b < 64; b++)
049: code2nibble[(byte) nibble2code[b]] = b;
050: code2nibble[(byte) pad] = 0;
051: }
052:
053: // ------------------------------------------------------------------
054: /**
055: * Base 64 encode as described in RFC 1421.
056: * <p>Does not insert whitespace as described in RFC 1521.
057: * @param s String to encode.
058: * @return String containing the encoded form of the input.
059: */
060: static public String encode(String s) {
061: try {
062: return encode(s, null);
063: } catch (UnsupportedEncodingException e) {
064: throw new IllegalArgumentException(e.toString());
065: }
066: }
067:
068: // ------------------------------------------------------------------
069: /**
070: * Base 64 encode as described in RFC 1421.
071: * <p>Does not insert whitespace as described in RFC 1521.
072: * @param s String to encode.
073: * @param charEncoding String representing the name of
074: * the character encoding of the provided input String.
075: * @return String containing the encoded form of the input.
076: */
077: static public String encode(String s, String charEncoding)
078: throws UnsupportedEncodingException {
079: byte[] bytes;
080: if (charEncoding == null)
081: bytes = s.getBytes(StringUtil.__ISO_8859_1);
082: else
083: bytes = s.getBytes(charEncoding);
084:
085: return new String(encode(bytes));
086: }
087:
088: // ------------------------------------------------------------------
089: /**
090: * Fast Base 64 encode as described in RFC 1421.
091: * <p>Does not insert whitespace as described in RFC 1521.
092: * <p> Avoids creating extra copies of the input/output.
093: * @param b byte array to encode.
094: * @return char array containing the encoded form of the input.
095: */
096: static public char[] encode(byte[] b) {
097: if (b == null)
098: return null;
099:
100: int bLen = b.length;
101: char r[] = new char[((bLen + 2) / 3) * 4];
102: int ri = 0;
103: int bi = 0;
104: byte b0, b1, b2;
105: int stop = (bLen / 3) * 3;
106: while (bi < stop) {
107: b0 = b[bi++];
108: b1 = b[bi++];
109: b2 = b[bi++];
110: r[ri++] = nibble2code[(b0 >>> 2) & 0x3f];
111: r[ri++] = nibble2code[(b0 << 4) & 0x3f | (b1 >>> 4) & 0x0f];
112: r[ri++] = nibble2code[(b1 << 2) & 0x3f | (b2 >>> 6) & 0x03];
113: r[ri++] = nibble2code[b2 & 077];
114: }
115:
116: if (bLen != bi) {
117: switch (bLen % 3) {
118: case 2:
119: b0 = b[bi++];
120: b1 = b[bi++];
121: r[ri++] = nibble2code[(b0 >>> 2) & 0x3f];
122: r[ri++] = nibble2code[(b0 << 4) & 0x3f | (b1 >>> 4)
123: & 0x0f];
124: r[ri++] = nibble2code[(b1 << 2) & 0x3f];
125: r[ri++] = pad;
126: break;
127:
128: case 1:
129: b0 = b[bi++];
130: r[ri++] = nibble2code[(b0 >>> 2) & 0x3f];
131: r[ri++] = nibble2code[(b0 << 4) & 0x3f];
132: r[ri++] = pad;
133: r[ri++] = pad;
134: break;
135:
136: default:
137: break;
138: }
139: }
140:
141: return r;
142: }
143:
144: // ------------------------------------------------------------------
145: /**
146: * Base 64 decode as described in RFC 1421.
147: * <p>Does not attempt to cope with extra whitespace
148: * as described in RFC 1521.
149: * @param s String to decode
150: * @return String decoded byte array.
151: */
152: static public String decode(String s) {
153: try {
154: return decode(s, StringUtil.__ISO_8859_1);
155: } catch (UnsupportedEncodingException e) {
156: throw new IllegalArgumentException(e.toString());
157: }
158: }
159:
160: // ------------------------------------------------------------------
161: /**
162: * Base 64 decode as described in RFC 1421.
163: * <p>Does not attempt to cope with extra whitespace
164: * as described in RFC 1521.
165: * @param s String to decode
166: * @param charEncoding String representing the character encoding
167: * used to map the decoded bytes into a String.
168: * @return String decoded byte array.
169: */
170: static public String decode(String s, String charEncoding)
171: throws UnsupportedEncodingException {
172: byte[] decoded = decode(s.toCharArray());
173:
174: if (charEncoding == null)
175: return new String(decoded);
176: return new String(decoded, charEncoding);
177: }
178:
179: /* ------------------------------------------------------------ */
180: /**
181: * Fast Base 64 decode as described in RFC 1421.
182: * <p>Does not attempt to cope with extra whitespace
183: * as described in RFC 1521.
184: * <p> Avoids creating extra copies of the input/output.
185: * <p> Note this code has been flattened for performance.
186: * @param b char array to decode.
187: * @return byte array containing the decoded form of the input.
188: * @throws IllegalArgumentException if the input is not a valid
189: * B64 encoding.
190: */
191: static public byte[] decode(char[] b) {
192: if (b == null)
193: return null;
194:
195: int bLen = b.length;
196: if (bLen % 4 != 0)
197: throw new IllegalArgumentException(
198: "Input block size is not 4");
199:
200: int li = bLen - 1;
201: while (li >= 0 && b[li] == (byte) pad)
202: li--;
203:
204: if (li < 0)
205: return new byte[0];
206:
207: // Create result array of exact required size.
208: int rLen = ((li + 1) * 3) / 4;
209: byte r[] = new byte[rLen];
210: int ri = 0;
211: int bi = 0;
212: int stop = (rLen / 3) * 3;
213: byte b0, b1, b2, b3;
214: try {
215: while (ri < stop) {
216: b0 = code2nibble[b[bi++]];
217: b1 = code2nibble[b[bi++]];
218: b2 = code2nibble[b[bi++]];
219: b3 = code2nibble[b[bi++]];
220: if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
221: throw new IllegalArgumentException(
222: "Not B64 encoded");
223:
224: r[ri++] = (byte) (b0 << 2 | b1 >>> 4);
225: r[ri++] = (byte) (b1 << 4 | b2 >>> 2);
226: r[ri++] = (byte) (b2 << 6 | b3);
227: }
228:
229: if (rLen != ri) {
230: switch (rLen % 3) {
231: case 2:
232: b0 = code2nibble[b[bi++]];
233: b1 = code2nibble[b[bi++]];
234: b2 = code2nibble[b[bi++]];
235: if (b0 < 0 || b1 < 0 || b2 < 0)
236: throw new IllegalArgumentException(
237: "Not B64 encoded");
238: r[ri++] = (byte) (b0 << 2 | b1 >>> 4);
239: r[ri++] = (byte) (b1 << 4 | b2 >>> 2);
240: break;
241:
242: case 1:
243: b0 = code2nibble[b[bi++]];
244: b1 = code2nibble[b[bi++]];
245: if (b0 < 0 || b1 < 0)
246: throw new IllegalArgumentException(
247: "Not B64 encoded");
248: r[ri++] = (byte) (b0 << 2 | b1 >>> 4);
249: break;
250:
251: default:
252: break;
253: }
254: }
255: } catch (IndexOutOfBoundsException e) {
256: throw new IllegalArgumentException("char " + bi
257: + " was not B64 encoded");
258: }
259:
260: return r;
261: }
262: }
|