001    package org.apache.fulcrum.crypto.impl;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    /**
023     * Base64 encoder/decoder taken from commons-codec. Copying
024     * and wasting the code allows to minimize the dependencies.
025     *
026     * @author Siegfried Goeschl
027     */
028    public class Base64
029    {
030    
031        public Base64() {
032        }
033    
034        private static boolean isBase64(byte octect) {
035            if(octect == 61)
036                return true;
037            return base64Alphabet[octect] != -1;
038        }
039    
040        public static boolean isArrayByteBase64(byte arrayOctect[]) {
041            arrayOctect = discardWhitespace(arrayOctect);
042            int length = arrayOctect.length;
043            if(length == 0)
044                return true;
045            for(int i = 0; i < length; i++)
046                if(!isBase64(arrayOctect[i]))
047                    return false;
048    
049            return true;
050        }
051    
052        public static byte[] encodeBase64(byte binaryData[]) {
053            return encodeBase64(binaryData, false);
054        }
055    
056        public static byte[] encodeBase64Chunked(byte binaryData[]) {
057            return encodeBase64(binaryData, true);
058        }
059    
060        public Object decode(Object pObject)
061            throws Exception {
062            if(!(pObject instanceof byte[]))
063                throw new Exception("Parameter supplied to Base64 decode is not a byte[]");
064            else
065                return decode((byte[])pObject);
066        }
067    
068        public byte[] decode(byte pArray[]) {
069            return decodeBase64(pArray);
070        }
071    
072        public static byte[] encodeBase64(byte binaryData[], boolean isChunked) {
073            int lengthDataBits = binaryData.length * 8;
074            int fewerThan24bits = lengthDataBits % 24;
075            int numberTriplets = lengthDataBits / 24;
076            byte encodedData[] = null;
077            int encodedDataLength = 0;
078            int nbrChunks = 0;
079            if(fewerThan24bits != 0)
080                encodedDataLength = (numberTriplets + 1) * 4;
081            else
082                encodedDataLength = numberTriplets * 4;
083            if(isChunked) {
084                nbrChunks = CHUNK_SEPARATOR.length != 0 ? (int)Math.ceil((float)encodedDataLength / 76F) : 0;
085                encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
086            }
087            encodedData = new byte[encodedDataLength];
088            byte k = 0;
089            byte l = 0;
090            byte b1 = 0;
091            byte b2 = 0;
092            byte b3 = 0;
093            int encodedIndex = 0;
094            int dataIndex = 0;
095            int i = 0;
096            int nextSeparatorIndex = 76;
097            int chunksSoFar = 0;
098            for(i = 0; i < numberTriplets; i++) {
099                dataIndex = i * 3;
100                b1 = binaryData[dataIndex];
101                b2 = binaryData[dataIndex + 1];
102                b3 = binaryData[dataIndex + 2];
103                l = (byte)(b2 & 0xf);
104                k = (byte)(b1 & 3);
105                byte val1 = (b1 & 0xffffff80) != 0 ? (byte)(b1 >> 2 ^ 0xc0) : (byte)(b1 >> 2);
106                byte val2 = (b2 & 0xffffff80) != 0 ? (byte)(b2 >> 4 ^ 0xf0) : (byte)(b2 >> 4);
107                byte val3 = (b3 & 0xffffff80) != 0 ? (byte)(b3 >> 6 ^ 0xfc) : (byte)(b3 >> 6);
108                encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
109                encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | k << 4];
110                encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2 | val3];
111                encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
112                encodedIndex += 4;
113                if(isChunked && encodedIndex == nextSeparatorIndex) {
114                    System.arraycopy(CHUNK_SEPARATOR, 0, encodedData, encodedIndex, CHUNK_SEPARATOR.length);
115                    chunksSoFar++;
116                    nextSeparatorIndex = 76 * (chunksSoFar + 1) + chunksSoFar * CHUNK_SEPARATOR.length;
117                    encodedIndex += CHUNK_SEPARATOR.length;
118                }
119            }
120    
121            dataIndex = i * 3;
122            if(fewerThan24bits == 8) {
123                b1 = binaryData[dataIndex];
124                k = (byte)(b1 & 3);
125                byte val1 = (b1 & 0xffffff80) != 0 ? (byte)(b1 >> 2 ^ 0xc0) : (byte)(b1 >> 2);
126                encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
127                encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
128                encodedData[encodedIndex + 2] = 61;
129                encodedData[encodedIndex + 3] = 61;
130            } else
131            if(fewerThan24bits == 16) {
132                b1 = binaryData[dataIndex];
133                b2 = binaryData[dataIndex + 1];
134                l = (byte)(b2 & 0xf);
135                k = (byte)(b1 & 3);
136                byte val1 = (b1 & 0xffffff80) != 0 ? (byte)(b1 >> 2 ^ 0xc0) : (byte)(b1 >> 2);
137                byte val2 = (b2 & 0xffffff80) != 0 ? (byte)(b2 >> 4 ^ 0xf0) : (byte)(b2 >> 4);
138                encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
139                encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | k << 4];
140                encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
141                encodedData[encodedIndex + 3] = 61;
142            }
143            if(isChunked && chunksSoFar < nbrChunks)
144                System.arraycopy(CHUNK_SEPARATOR, 0, encodedData, encodedDataLength - CHUNK_SEPARATOR.length, CHUNK_SEPARATOR.length);
145            return encodedData;
146        }
147    
148        public static byte[] decodeBase64(byte base64Data[]) {
149            base64Data = discardNonBase64(base64Data);
150            if(base64Data.length == 0)
151                return new byte[0];
152            int numberQuadruple = base64Data.length / 4;
153            byte decodedData[] = null;
154            byte b1 = 0;
155            byte b2 = 0;
156            byte b3 = 0;
157            byte b4 = 0;
158            byte marker0 = 0;
159            byte marker1 = 0;
160            int encodedIndex = 0;
161            int dataIndex = 0;
162            int lastData;
163            for(lastData = base64Data.length; base64Data[lastData - 1] == 61;)
164                if(--lastData == 0)
165                    return new byte[0];
166    
167            decodedData = new byte[lastData - numberQuadruple];
168            for(int i = 0; i < numberQuadruple; i++) {
169                dataIndex = i * 4;
170                marker0 = base64Data[dataIndex + 2];
171                marker1 = base64Data[dataIndex + 3];
172                b1 = base64Alphabet[base64Data[dataIndex]];
173                b2 = base64Alphabet[base64Data[dataIndex + 1]];
174                if(marker0 != 61 && marker1 != 61) {
175                    b3 = base64Alphabet[marker0];
176                    b4 = base64Alphabet[marker1];
177                    decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4);
178                    decodedData[encodedIndex + 1] = (byte)((b2 & 0xf) << 4 | b3 >> 2 & 0xf);
179                    decodedData[encodedIndex + 2] = (byte)(b3 << 6 | b4);
180                } else
181                if(marker0 == 61)
182                    decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4);
183                else
184                if(marker1 == 61) {
185                    b3 = base64Alphabet[marker0];
186                    decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4);
187                    decodedData[encodedIndex + 1] = (byte)((b2 & 0xf) << 4 | b3 >> 2 & 0xf);
188                }
189                encodedIndex += 3;
190            }
191    
192            return decodedData;
193        }
194    
195        static byte[] discardWhitespace(byte data[]) {
196            byte groomedData[] = new byte[data.length];
197            int bytesCopied = 0;
198            int i = 0;
199            do
200                if(i < data.length) {
201                    switch(data[i]) {
202                    default:
203                        groomedData[bytesCopied++] = data[i];
204                        // fall through
205    
206                    case 9: // '\t'
207                    case 10: // '\n'
208                    case 13: // '\r'
209                    case 32: // ' '
210                        i++;
211                        break;
212                    }
213                } else {
214                    byte packedData[] = new byte[bytesCopied];
215                    System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
216                    return packedData;
217                }
218            while(true);
219        }
220    
221        static byte[] discardNonBase64(byte data[]) {
222            byte groomedData[] = new byte[data.length];
223            int bytesCopied = 0;
224            for(int i = 0; i < data.length; i++)
225                if(isBase64(data[i]))
226                    groomedData[bytesCopied++] = data[i];
227    
228            byte packedData[] = new byte[bytesCopied];
229            System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
230            return packedData;
231        }
232    
233        public Object encode(Object pObject)
234            throws Exception {
235            if(!(pObject instanceof byte[]))
236                throw new Exception("Parameter supplied to Base64 encode is not a byte[]");
237            else
238                return encode((byte[])pObject);
239        }
240    
241        public byte[] encode(byte pArray[]) {
242            return encodeBase64(pArray, false);
243        }
244    
245        static final int CHUNK_SIZE = 76;
246        static final byte CHUNK_SEPARATOR[] = "\r\n".getBytes();
247        static final int BASELENGTH = 255;
248        static final int LOOKUPLENGTH = 64;
249        static final int EIGHTBIT = 8;
250        static final int SIXTEENBIT = 16;
251        static final int TWENTYFOURBITGROUP = 24;
252        static final int FOURBYTE = 4;
253        static final int SIGN = -128;
254        static final byte PAD = 61;
255        private static byte base64Alphabet[];
256        private static byte lookUpBase64Alphabet[];
257    
258        static  {
259            base64Alphabet = new byte[255];
260            lookUpBase64Alphabet = new byte[64];
261            int i;
262            for(i = 0; i < 255; i++)
263                base64Alphabet[i] = -1;
264    
265            for(i = 90; i >= 65; i--)
266                base64Alphabet[i] = (byte)(i - 65);
267    
268            for(i = 122; i >= 97; i--)
269                base64Alphabet[i] = (byte)((i - 97) + 26);
270    
271            for(i = 57; i >= 48; i--)
272                base64Alphabet[i] = (byte)((i - 48) + 52);
273    
274            base64Alphabet[43] = 62;
275            base64Alphabet[47] = 63;
276            for(i = 0; i <= 25; i++)
277                lookUpBase64Alphabet[i] = (byte)(65 + i);
278    
279            i = 26;
280            for(int j = 0; i <= 51; j++) {
281                lookUpBase64Alphabet[i] = (byte)(97 + j);
282                i++;
283            }
284    
285            i = 52;
286            for(int j = 0; i <= 61; j++) {
287                lookUpBase64Alphabet[i] = (byte)(48 + j);
288                i++;
289            }
290    
291            lookUpBase64Alphabet[62] = 43;
292            lookUpBase64Alphabet[63] = 47;
293        }
294    }