Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
Base64 |
|
| 4.666666666666667;4.667 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | */ | |
19 | package org.apache.shiro.codec; | |
20 | ||
21 | /** | |
22 | * Provides <a href="http://en.wikipedia.org/wiki/Base64">Base 64</a> encoding and decoding as defined by | |
23 | * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>. | |
24 | * <p/> | |
25 | * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose | |
26 | * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein. | |
27 | * <p/> | |
28 | * This class was borrowed from Apache Commons Codec SVN repository (rev. 618419) with modifications | |
29 | * to enable Base64 conversion without a full dependecny on Commons Codec. We didn't want to reinvent the wheel of | |
30 | * great work they've done, but also didn't want to force every Shiro user to depend on the commons-codec.jar | |
31 | * <p/> | |
32 | * As per the Apache 2.0 license, the original copyright notice and all author and copyright information have | |
33 | * remained in tact. | |
34 | * | |
35 | * @see <a href="http://en.wikipedia.org/wiki/Base64">Wikipedia: Base 64</a> | |
36 | * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a> | |
37 | * @since 0.9 | |
38 | */ | |
39 | 0 | public class Base64 { |
40 | ||
41 | /** | |
42 | * Chunk size per RFC 2045 section 6.8. | |
43 | * <p/> | |
44 | * The character limit does not count the trailing CRLF, but counts all other characters, including any | |
45 | * equal signs. | |
46 | * | |
47 | * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a> | |
48 | */ | |
49 | static final int CHUNK_SIZE = 76; | |
50 | ||
51 | /** | |
52 | * Chunk separator per RFC 2045 section 2.1. | |
53 | * | |
54 | * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a> | |
55 | */ | |
56 | 1 | static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes(); |
57 | ||
58 | /** | |
59 | * The base length. | |
60 | */ | |
61 | private static final int BASELENGTH = 255; | |
62 | ||
63 | /** | |
64 | * Lookup length. | |
65 | */ | |
66 | private static final int LOOKUPLENGTH = 64; | |
67 | ||
68 | /** | |
69 | * Used to calculate the number of bits in a byte. | |
70 | */ | |
71 | private static final int EIGHTBIT = 8; | |
72 | ||
73 | /** | |
74 | * Used when encoding something which has fewer than 24 bits. | |
75 | */ | |
76 | private static final int SIXTEENBIT = 16; | |
77 | ||
78 | /** | |
79 | * Used to determine how many bits data contains. | |
80 | */ | |
81 | private static final int TWENTYFOURBITGROUP = 24; | |
82 | ||
83 | /** | |
84 | * Used to get the number of Quadruples. | |
85 | */ | |
86 | private static final int FOURBYTE = 4; | |
87 | ||
88 | /** | |
89 | * Used to test the sign of a byte. | |
90 | */ | |
91 | private static final int SIGN = -128; | |
92 | ||
93 | /** | |
94 | * Byte used to pad output. | |
95 | */ | |
96 | private static final byte PAD = (byte) '='; | |
97 | ||
98 | /** | |
99 | * Contains the Base64 values <code>0</code> through <code>63</code> accessed by using character encodings as | |
100 | * indices. | |
101 | * <p/> | |
102 | * <p>For example, <code>base64Alphabet['+']</code> returns <code>62</code>.</p> | |
103 | * <p/> | |
104 | * <p>The value of undefined encodings is <code>-1</code>.</p> | |
105 | */ | |
106 | 1 | private static final byte[] base64Alphabet = new byte[BASELENGTH]; |
107 | ||
108 | /** | |
109 | * <p>Contains the Base64 encodings <code>A</code> through <code>Z</code>, followed by <code>a</code> through | |
110 | * <code>z</code>, followed by <code>0</code> through <code>9</code>, followed by <code>+</code>, and | |
111 | * <code>/</code>.</p> | |
112 | * <p/> | |
113 | * <p>This array is accessed by using character values as indices.</p> | |
114 | * <p/> | |
115 | * <p>For example, <code>lookUpBase64Alphabet[62] </code> returns <code>'+'</code>.</p> | |
116 | */ | |
117 | 1 | private static final byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH]; |
118 | ||
119 | // Populating the lookup and character arrays | |
120 | ||
121 | static { | |
122 | 256 | for (int i = 0; i < BASELENGTH; i++) { |
123 | 255 | base64Alphabet[i] = (byte) -1; |
124 | } | |
125 | 27 | for (int i = 'Z'; i >= 'A'; i--) { |
126 | 26 | base64Alphabet[i] = (byte) (i - 'A'); |
127 | } | |
128 | 27 | for (int i = 'z'; i >= 'a'; i--) { |
129 | 26 | base64Alphabet[i] = (byte) (i - 'a' + 26); |
130 | } | |
131 | 11 | for (int i = '9'; i >= '0'; i--) { |
132 | 10 | base64Alphabet[i] = (byte) (i - '0' + 52); |
133 | } | |
134 | ||
135 | 1 | base64Alphabet['+'] = 62; |
136 | 1 | base64Alphabet['/'] = 63; |
137 | ||
138 | 27 | for (int i = 0; i <= 25; i++) { |
139 | 26 | lookUpBase64Alphabet[i] = (byte) ('A' + i); |
140 | } | |
141 | ||
142 | 27 | for (int i = 26, j = 0; i <= 51; i++, j++) { |
143 | 26 | lookUpBase64Alphabet[i] = (byte) ('a' + j); |
144 | } | |
145 | ||
146 | 11 | for (int i = 52, j = 0; i <= 61; i++, j++) { |
147 | 10 | lookUpBase64Alphabet[i] = (byte) ('0' + j); |
148 | } | |
149 | ||
150 | 1 | lookUpBase64Alphabet[62] = (byte) '+'; |
151 | 1 | lookUpBase64Alphabet[63] = (byte) '/'; |
152 | 1 | } |
153 | ||
154 | /** | |
155 | * Returns whether or not the <code>octect</code> is in the base 64 alphabet. | |
156 | * | |
157 | * @param octect The value to test | |
158 | * @return <code>true</code> if the value is defined in the the base 64 alphabet, <code>false</code> otherwise. | |
159 | */ | |
160 | private static boolean isBase64(byte octect) { | |
161 | 590 | if (octect == PAD) { |
162 | 25 | return true; |
163 | } else //noinspection RedundantIfStatement | |
164 | 565 | if (octect < 0 || base64Alphabet[octect] == -1) { |
165 | 0 | return false; |
166 | } else { | |
167 | 565 | return true; |
168 | } | |
169 | } | |
170 | ||
171 | /** | |
172 | * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. | |
173 | * | |
174 | * @param arrayOctect byte array to test | |
175 | * @return <code>true</code> if all bytes are valid characters in the Base64 alphabet or if the byte array is | |
176 | * empty; false, otherwise | |
177 | */ | |
178 | public static boolean isBase64(byte[] arrayOctect) { | |
179 | ||
180 | 0 | arrayOctect = discardWhitespace(arrayOctect); |
181 | ||
182 | 0 | int length = arrayOctect.length; |
183 | 0 | if (length == 0) { |
184 | // shouldn't a 0 length array be valid base64 data? | |
185 | // return false; | |
186 | 0 | return true; |
187 | } | |
188 | 0 | for (int i = 0; i < length; i++) { |
189 | 0 | if (!isBase64(arrayOctect[i])) { |
190 | 0 | return false; |
191 | } | |
192 | } | |
193 | 0 | return true; |
194 | } | |
195 | ||
196 | /** | |
197 | * Discards any whitespace from a base-64 encoded block. | |
198 | * | |
199 | * @param data The base-64 encoded data to discard the whitespace from. | |
200 | * @return The data, less whitespace (see RFC 2045). | |
201 | */ | |
202 | static byte[] discardWhitespace(byte[] data) { | |
203 | 0 | byte groomedData[] = new byte[data.length]; |
204 | 0 | int bytesCopied = 0; |
205 | ||
206 | 0 | for (byte aByte : data) { |
207 | 0 | switch (aByte) { |
208 | case (byte) ' ': | |
209 | case (byte) '\n': | |
210 | case (byte) '\r': | |
211 | case (byte) '\t': | |
212 | 0 | break; |
213 | default: | |
214 | 0 | groomedData[bytesCopied++] = aByte; |
215 | } | |
216 | } | |
217 | ||
218 | 0 | byte packedData[] = new byte[bytesCopied]; |
219 | ||
220 | 0 | System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); |
221 | ||
222 | 0 | return packedData; |
223 | } | |
224 | ||
225 | /** | |
226 | * Base64 encodes the specified byte array and then encodes it as a String using Shiro's preferred character | |
227 | * encoding (UTF-8). | |
228 | * | |
229 | * @param bytes the byte array to Base64 encode. | |
230 | * @return a UTF-8 encoded String of the resulting Base64 encoded byte array. | |
231 | */ | |
232 | public static String encodeToString(byte[] bytes) { | |
233 | 22 | byte[] encoded = encode(bytes); |
234 | 22 | return CodecSupport.toString(encoded); |
235 | } | |
236 | ||
237 | /** | |
238 | * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks | |
239 | * | |
240 | * @param binaryData binary data to encodeToChars | |
241 | * @return Base64 characters chunked in 76 character blocks | |
242 | */ | |
243 | public static byte[] encodeChunked(byte[] binaryData) { | |
244 | 0 | return encode(binaryData, true); |
245 | } | |
246 | ||
247 | /** | |
248 | * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet. | |
249 | * | |
250 | * @param pArray a byte array containing binary data | |
251 | * @return A byte array containing only Base64 character data | |
252 | */ | |
253 | public static byte[] encode(byte[] pArray) { | |
254 | 22 | return encode(pArray, false); |
255 | } | |
256 | ||
257 | /** | |
258 | * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. | |
259 | * | |
260 | * @param binaryData Array containing binary data to encodeToChars. | |
261 | * @param isChunked if <code>true</code> this encoder will chunk the base64 output into 76 character blocks | |
262 | * @return Base64-encoded data. | |
263 | * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} | |
264 | */ | |
265 | public static byte[] encode(byte[] binaryData, boolean isChunked) { | |
266 | 22 | long binaryDataLength = binaryData.length; |
267 | 22 | long lengthDataBits = binaryDataLength * EIGHTBIT; |
268 | 22 | long fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; |
269 | 22 | long tripletCount = lengthDataBits / TWENTYFOURBITGROUP; |
270 | long encodedDataLengthLong; | |
271 | 22 | int chunckCount = 0; |
272 | ||
273 | 22 | if (fewerThan24bits != 0) { |
274 | // data not divisible by 24 bit | |
275 | 22 | encodedDataLengthLong = (tripletCount + 1) * 4; |
276 | } else { | |
277 | // 16 or 8 bit | |
278 | 0 | encodedDataLengthLong = tripletCount * 4; |
279 | } | |
280 | ||
281 | // If the output is to be "chunked" into 76 character sections, | |
282 | // for compliance with RFC 2045 MIME, then it is important to | |
283 | // allow for extra length to account for the separator(s) | |
284 | 22 | if (isChunked) { |
285 | ||
286 | 0 | chunckCount = (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math |
287 | .ceil((float) encodedDataLengthLong / CHUNK_SIZE)); | |
288 | 0 | encodedDataLengthLong += chunckCount * CHUNK_SEPARATOR.length; |
289 | } | |
290 | ||
291 | 22 | if (encodedDataLengthLong > Integer.MAX_VALUE) { |
292 | 0 | throw new IllegalArgumentException( |
293 | "Input array too big, output array would be bigger than Integer.MAX_VALUE=" + Integer.MAX_VALUE); | |
294 | } | |
295 | 22 | int encodedDataLength = (int) encodedDataLengthLong; |
296 | 22 | byte encodedData[] = new byte[encodedDataLength]; |
297 | ||
298 | byte k, l, b1, b2, b3; | |
299 | ||
300 | 22 | int encodedIndex = 0; |
301 | int dataIndex; | |
302 | int i; | |
303 | 22 | int nextSeparatorIndex = CHUNK_SIZE; |
304 | 22 | int chunksSoFar = 0; |
305 | ||
306 | // log.debug("number of triplets = " + numberTriplets); | |
307 | 234 | for (i = 0; i < tripletCount; i++) { |
308 | 212 | dataIndex = i * 3; |
309 | 212 | b1 = binaryData[dataIndex]; |
310 | 212 | b2 = binaryData[dataIndex + 1]; |
311 | 212 | b3 = binaryData[dataIndex + 2]; |
312 | ||
313 | // log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3); | |
314 | ||
315 | 212 | l = (byte) (b2 & 0x0f); |
316 | 212 | k = (byte) (b1 & 0x03); |
317 | ||
318 | 212 | byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); |
319 | 212 | byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); |
320 | 212 | byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); |
321 | ||
322 | 212 | encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; |
323 | // log.debug( "val2 = " + val2 ); | |
324 | // log.debug( "k4 = " + (k<<4) ); | |
325 | // log.debug( "vak = " + (val2 | (k<<4)) ); | |
326 | 212 | encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | (k << 4)]; |
327 | 212 | encodedData[encodedIndex + 2] = lookUpBase64Alphabet[(l << 2) | val3]; |
328 | 212 | encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f]; |
329 | ||
330 | 212 | encodedIndex += 4; |
331 | ||
332 | // If we are chunking, let's put a chunk separator down. | |
333 | 212 | if (isChunked) { |
334 | // this assumes that CHUNK_SIZE % 4 == 0 | |
335 | 0 | if (encodedIndex == nextSeparatorIndex) { |
336 | 0 | System.arraycopy(CHUNK_SEPARATOR, 0, encodedData, encodedIndex, CHUNK_SEPARATOR.length); |
337 | 0 | chunksSoFar++; |
338 | 0 | nextSeparatorIndex = (CHUNK_SIZE * (chunksSoFar + 1)) + (chunksSoFar * CHUNK_SEPARATOR.length); |
339 | 0 | encodedIndex += CHUNK_SEPARATOR.length; |
340 | } | |
341 | } | |
342 | } | |
343 | ||
344 | // form integral number of 6-bit groups | |
345 | 22 | dataIndex = i * 3; |
346 | ||
347 | 22 | if (fewerThan24bits == EIGHTBIT) { |
348 | 15 | b1 = binaryData[dataIndex]; |
349 | 15 | k = (byte) (b1 & 0x03); |
350 | // log.debug("b1=" + b1); | |
351 | // log.debug("b1<<2 = " + (b1>>2) ); | |
352 | 15 | byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); |
353 | 15 | encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; |
354 | 15 | encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4]; |
355 | 15 | encodedData[encodedIndex + 2] = PAD; |
356 | 15 | encodedData[encodedIndex + 3] = PAD; |
357 | 15 | } else if (fewerThan24bits == SIXTEENBIT) { |
358 | ||
359 | 7 | b1 = binaryData[dataIndex]; |
360 | 7 | b2 = binaryData[dataIndex + 1]; |
361 | 7 | l = (byte) (b2 & 0x0f); |
362 | 7 | k = (byte) (b1 & 0x03); |
363 | ||
364 | 7 | byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); |
365 | 7 | byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); |
366 | ||
367 | 7 | encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; |
368 | 7 | encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | (k << 4)]; |
369 | 7 | encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2]; |
370 | 7 | encodedData[encodedIndex + 3] = PAD; |
371 | } | |
372 | ||
373 | 22 | if (isChunked) { |
374 | // we also add a separator to the end of the final chunk. | |
375 | 0 | if (chunksSoFar < chunckCount) { |
376 | 0 | System.arraycopy(CHUNK_SEPARATOR, 0, encodedData, encodedDataLength - CHUNK_SEPARATOR.length, |
377 | CHUNK_SEPARATOR.length); | |
378 | } | |
379 | } | |
380 | ||
381 | 22 | return encodedData; |
382 | } | |
383 | ||
384 | /** | |
385 | * Converts the specified UTF-8 Base64 encoded String and decodes it to a resultant UTF-8 encoded string. | |
386 | * | |
387 | * @param base64Encoded a UTF-8 Base64 encoded String | |
388 | * @return the decoded String, UTF-8 encoded. | |
389 | */ | |
390 | public static String decodeToString(String base64Encoded) { | |
391 | 0 | byte[] encodedBytes = CodecSupport.toBytes(base64Encoded); |
392 | 0 | return decodeToString(encodedBytes); |
393 | } | |
394 | ||
395 | /** | |
396 | * Decodes the specified Base64 encoded byte array and returns the decoded result as a UTF-8 encoded. | |
397 | * | |
398 | * @param base64Encoded a Base64 encoded byte array | |
399 | * @return the decoded String, UTF-8 encoded. | |
400 | */ | |
401 | public static String decodeToString(byte[] base64Encoded) { | |
402 | 0 | byte[] decoded = decode(base64Encoded); |
403 | 0 | return CodecSupport.toString(decoded); |
404 | } | |
405 | ||
406 | /** | |
407 | * Converts the specified UTF-8 Base64 encoded String and decodes it to a raw Base64 decoded byte array. | |
408 | * | |
409 | * @param base64Encoded a UTF-8 Base64 encoded String | |
410 | * @return the raw Base64 decoded byte array. | |
411 | */ | |
412 | public static byte[] decode(String base64Encoded) { | |
413 | 16 | byte[] bytes = CodecSupport.toBytes(base64Encoded); |
414 | 16 | return decode(bytes); |
415 | } | |
416 | ||
417 | /** | |
418 | * Decodes Base64 data into octects | |
419 | * | |
420 | * @param base64Data Byte array containing Base64 data | |
421 | * @return Array containing decoded data. | |
422 | */ | |
423 | public static byte[] decode(byte[] base64Data) { | |
424 | // RFC 2045 requires that we discard ALL non-Base64 characters | |
425 | 16 | base64Data = discardNonBase64(base64Data); |
426 | ||
427 | // handle the edge case, so we don't have to worry about it later | |
428 | 16 | if (base64Data.length == 0) { |
429 | 0 | return new byte[0]; |
430 | } | |
431 | ||
432 | 16 | int numberQuadruple = base64Data.length / FOURBYTE; |
433 | byte decodedData[]; | |
434 | byte b1, b2, b3, b4, marker0, marker1; | |
435 | ||
436 | // Throw away anything not in base64Data | |
437 | ||
438 | 16 | int encodedIndex = 0; |
439 | int dataIndex; | |
440 | { | |
441 | // this sizes the output array properly - rlw | |
442 | 16 | int lastData = base64Data.length; |
443 | // ignore the '=' padding | |
444 | 41 | while (base64Data[lastData - 1] == PAD) { |
445 | 25 | if (--lastData == 0) { |
446 | 0 | return new byte[0]; |
447 | } | |
448 | } | |
449 | 16 | decodedData = new byte[lastData - numberQuadruple]; |
450 | } | |
451 | ||
452 | 162 | for (int i = 0; i < numberQuadruple; i++) { |
453 | 146 | dataIndex = i * 4; |
454 | 146 | marker0 = base64Data[dataIndex + 2]; |
455 | 146 | marker1 = base64Data[dataIndex + 3]; |
456 | ||
457 | 146 | b1 = base64Alphabet[base64Data[dataIndex]]; |
458 | 146 | b2 = base64Alphabet[base64Data[dataIndex + 1]]; |
459 | ||
460 | 146 | if (marker0 != PAD && marker1 != PAD) { |
461 | // No PAD e.g 3cQl | |
462 | 132 | b3 = base64Alphabet[marker0]; |
463 | 132 | b4 = base64Alphabet[marker1]; |
464 | ||
465 | 132 | decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); |
466 | 132 | decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); |
467 | 132 | decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4); |
468 | 14 | } else if (marker0 == PAD) { |
469 | // Two PAD e.g. 3c[Pad][Pad] | |
470 | 11 | decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); |
471 | } else { | |
472 | // One PAD e.g. 3cQ[Pad] | |
473 | 3 | b3 = base64Alphabet[marker0]; |
474 | 3 | decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); |
475 | 3 | decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); |
476 | } | |
477 | 146 | encodedIndex += 3; |
478 | } | |
479 | 16 | return decodedData; |
480 | } | |
481 | ||
482 | /** | |
483 | * Discards any characters outside of the base64 alphabet, per the requirements on page 25 of RFC 2045 - "Any | |
484 | * characters outside of the base64 alphabet are to be ignored in base64 encoded data." | |
485 | * | |
486 | * @param data The base-64 encoded data to groom | |
487 | * @return The data, less non-base64 characters (see RFC 2045). | |
488 | */ | |
489 | static byte[] discardNonBase64(byte[] data) { | |
490 | 16 | byte groomedData[] = new byte[data.length]; |
491 | 16 | int bytesCopied = 0; |
492 | ||
493 | 606 | for (byte aByte : data) { |
494 | 590 | if (isBase64(aByte)) { |
495 | 590 | groomedData[bytesCopied++] = aByte; |
496 | } | |
497 | } | |
498 | ||
499 | 16 | byte packedData[] = new byte[bytesCopied]; |
500 | ||
501 | 16 | System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); |
502 | ||
503 | 16 | return packedData; |
504 | } | |
505 | ||
506 | } |