Jay
2024-11-01 03e8aca3ad6201c0d74e00d4c8d7367cdaaa54f9
提交 | 用户 | 时间
ce910c 1 /*
H 2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4  *
5  *
6  *
7  *
8  *
9  *
10  *
11  *
12  *
13  *
14  *
15  *
16  *
17  *
18  *
19  *
20  *
21  *
22  *
23  *
24  */
25
26
27 // Base64解码 使用1.8版本源码  鉴于1.8部分特性1.6版本使用不到 故只保留能使用到的代码 其余删除 
28 package com.netsdk.common;
29 import java.nio.ByteBuffer;
30 import java.nio.charset.Charset;
31 import java.util.Arrays;
32
33 /**
34  * This class consists exclusively of static methods for obtaining
35  * encoders and decoders for the Base64 encoding scheme. The
36  * implementation of this class supports the following types of Base64
37  * as specified in
38  * <a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a> and
39  *
40  * <ul>
41  * <li><a name="basic"><b>Basic</b></a>
42  * <p> Uses "The Base64 Alphabet" as specified in Table 1 of
43  *     RFC 4648 and RFC 2045 for encoding and decoding operation.
44  *     The encoder does not add any line feed (line separator)
45  *     character. The decoder rejects data that contains characters
46  *     outside the base64 alphabet.</p></li>
47  *
48  * <p> Unless otherwise noted, passing a {@code null} argument to a
49  * method of this class will cause a {@link NullPointerException
50  * NullPointerException} to be thrown.
51  *
52  * @author  Xueming Shen
53  * @since   1.8
54  */
55
56 public class Base64 {
57
58     private Base64() {}
59
60     /**
61      * Returns a {@link Encoder} that encodes using the
62      * <a href="#basic">Basic</a> type base64 encoding scheme.
63      *
64      * @return  A Base64 encoder.
65      */
66     public static Encoder getEncoder() {
67          return Encoder.RFC4648;
68     }
69
70     /**
71      * This class implements an encoder for encoding byte data using
72      * the Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
73      *
74      * <p> Instances of {@link Encoder} class are safe for use by
75      * multiple concurrent threads.
76      *
77      * <p> Unless otherwise noted, passing a {@code null} argument to
78      * a method of this class will cause a
79      * {@link NullPointerException NullPointerException} to
80      * be thrown.
81      *
82      * @see     Decoder
83      * @since   1.8
84      */
85     public static class Encoder {
86
87         private final byte[] newline;
88         private final int linemax;
89         private final boolean isURL;
90         private final boolean doPadding;
91
92         private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) {
93             this.isURL = isURL;
94             this.newline = newline;
95             this.linemax = linemax;
96             this.doPadding = doPadding;
97         }
98
99         /**
100          * This array is a lookup table that translates 6-bit positive integer
101          * index values into their "Base64 Alphabet" equivalents as specified
102          * in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).
103          */
104         private static final char[] toBase64 = {
105             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
106             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
107             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
108             'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
109             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
110         };
111
112         /**
113          * It's the lookup table for "URL and Filename safe Base64" as specified
114          * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
115          * '_'. This table is used when BASE64_URL is specified.
116          */
117         private static final char[] toBase64URL = {
118             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
119             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
120             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
121             'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
122             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
123         };
124
125         static final Encoder RFC4648 = new Encoder(false, null, -1, true);
126
127         private final int outLength(int srclen) {
128             int len = 0;
129             if (doPadding) {
130                 len = 4 * ((srclen + 2) / 3);
131             } else {
132                 int n = srclen % 3;
133                 len = 4 * (srclen / 3) + (n == 0 ? 0 : n + 1);
134             }
135             if (linemax > 0)                                  // line separators
136                 len += (len - 1) / linemax * newline.length;
137             return len;
138         }
139
140         /**
141          * Encodes all bytes from the specified byte array into a newly-allocated
142          * byte array using the {@link Base64} encoding scheme. The returned byte
143          * array is of the length of the resulting bytes.
144          *
145          * @param   src
146          *          the byte array to encode
147          * @return  A newly-allocated byte array containing the resulting
148          *          encoded bytes.
149          */
150         public byte[] encode(byte[] src) {
151             int len = outLength(src.length);          // dst array size
152             byte[] dst = new byte[len];
153             int ret = encode0(src, 0, src.length, dst);
154             if (ret != dst.length)
155                  return Arrays.copyOf(dst, ret);
156             return dst;
157         }
158
159         /**
160          * Encodes all bytes from the specified byte array using the
161          * {@link Base64} encoding scheme, writing the resulting bytes to the
162          * given output byte array, starting at offset 0.
163          *
164          * <p> It is the responsibility of the invoker of this method to make
165          * sure the output byte array {@code dst} has enough space for encoding
166          * all bytes from the input byte array. No bytes will be written to the
167          * output byte array if the output byte array is not big enough.
168          *
169          * @param   src
170          *          the byte array to encode
171          * @param   dst
172          *          the output byte array
173          * @return  The number of bytes written to the output byte array
174          *
175          * @throws  IllegalArgumentException if {@code dst} does not have enough
176          *          space for encoding all input bytes.
177          */
178         public int encode(byte[] src, byte[] dst) {
179             int len = outLength(src.length);         // dst array size
180             if (dst.length < len)
181                 throw new IllegalArgumentException(
182                     "Output byte array is too small for encoding all input bytes");
183             return encode0(src, 0, src.length, dst);
184         }
185
186         /**
187          * Encodes the specified byte array into a String using the {@link Base64}
188          * encoding scheme.
189          *
190          * <p> In other words, an invocation of this method has exactly the same
191          * effect as invoking
192          * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.
193          *
194          * @param   src
195          *          the byte array to encode
196          * @return  A String containing the resulting Base64 encoded characters
197          */
198         @SuppressWarnings("deprecation")
199         public String encodeToString(byte[] src) {
200             byte[] encoded = encode(src);
201             return new String(encoded, 0, 0, encoded.length);
202         }
203
204         /**
205          * Returns an encoder instance that encodes equivalently to this one,
206          * but without adding any padding character at the end of the encoded
207          * byte data.
208          *
209          * <p> The encoding scheme of this encoder instance is unaffected by
210          * this invocation. The returned encoder instance should be used for
211          * non-padding encoding operation.
212          *
213          * @return an equivalent encoder that encodes without adding any
214          *         padding character at the end
215          */
216         public Encoder withoutPadding() {
217             if (!doPadding)
218                 return this;
219             return new Encoder(isURL, newline, linemax, false);
220         }
221
222         private int encode0(byte[] src, int off, int end, byte[] dst) {
223             char[] base64 = isURL ? toBase64URL : toBase64;
224             int sp = off;
225             int slen = (end - off) / 3 * 3;
226             int sl = off + slen;
227             if (linemax > 0 && slen  > linemax / 4 * 3)
228                 slen = linemax / 4 * 3;
229             int dp = 0;
230             while (sp < sl) {
231                 int sl0 = Math.min(sp + slen, sl);
232                 for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
233                     int bits = (src[sp0++] & 0xff) << 16 |
234                                (src[sp0++] & 0xff) <<  8 |
235                                (src[sp0++] & 0xff);
236                     dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
237                     dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
238                     dst[dp0++] = (byte)base64[(bits >>> 6)  & 0x3f];
239                     dst[dp0++] = (byte)base64[bits & 0x3f];
240                 }
241                 int dlen = (sl0 - sp) / 3 * 4;
242                 dp += dlen;
243                 sp = sl0;
244                 if (dlen == linemax && sp < end) {
245                     for (byte b : newline){
246                         dst[dp++] = b;
247                     }
248                 }
249             }
250             if (sp < end) {               // 1 or 2 leftover bytes
251                 int b0 = src[sp++] & 0xff;
252                 dst[dp++] = (byte)base64[b0 >> 2];
253                 if (sp == end) {
254                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];
255                     if (doPadding) {
256                         dst[dp++] = '=';
257                         dst[dp++] = '=';
258                     }
259                 } else {
260                     int b1 = src[sp++] & 0xff;
261                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
262                     dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];
263                     if (doPadding) {
264                         dst[dp++] = '=';
265                     }
266                 }
267             }
268             return dp;
269         }
270     }
271
272     /**
273      * Returns a {@link Decoder} that decodes using the
274      * <a href="#basic">Basic</a> type base64 encoding scheme.
275      *
276      * @return  A Base64 decoder.
277      */
278     public static Decoder getDecoder() {
279         return Decoder.RFC4648;
280     }
281     /**
282      * This class implements a decoder for decoding byte data using the
283      * Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
284      *
285      * <p> The Base64 padding character {@code '='} is accepted and
286      * interpreted as the end of the encoded byte data, but is not
287      * required. So if the final unit of the encoded byte data only has
288      * two or three Base64 characters (without the corresponding padding
289      * character(s) padded), they are decoded as if followed by padding
290      * character(s). If there is a padding character present in the
291      * final unit, the correct number of padding character(s) must be
292      * present, otherwise {@code IllegalArgumentException} (
293      * {@code IOException} when reading from a Base64 stream) is thrown
294      * during decoding.
295      *
296      * <p> Instances of {@link Decoder} class are safe for use by
297      * multiple concurrent threads.
298      *
299      * <p> Unless otherwise noted, passing a {@code null} argument to
300      * a method of this class will cause a
301      * {@link NullPointerException NullPointerException} to
302      * be thrown.
303      *
304      * @see     Encoder
305      * @since   1.8
306      */
307     public static class Decoder {
308
309         private final boolean isURL;
310         private final boolean isMIME;
311
312         private Decoder(boolean isURL, boolean isMIME) {
313             this.isURL = isURL;
314             this.isMIME = isMIME;
315         }
316
317         /**
318          * Lookup table for decoding unicode characters drawn from the
319          * "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into
320          * their 6-bit positive integer equivalents.  Characters that
321          * are not in the Base64 alphabet but fall within the bounds of
322          * the array are encoded to -1.
323          *
324          */
325         private static final int[] fromBase64 = new int[256];
326         static {
327             Arrays.fill(fromBase64, -1);
328             for (int i = 0; i < Encoder.toBase64.length; i++)
329                 fromBase64[Encoder.toBase64[i]] = i;
330             fromBase64['='] = -2;
331         }
332
333         /**
334          * Lookup table for decoding "URL and Filename safe Base64 Alphabet"
335          * as specified in Table2 of the RFC 4648.
336          */
337         private static final int[] fromBase64URL = new int[256];
338
339         static {
340             Arrays.fill(fromBase64URL, -1);
341             for (int i = 0; i < Encoder.toBase64URL.length; i++)
342                 fromBase64URL[Encoder.toBase64URL[i]] = i;
343             fromBase64URL['='] = -2;
344         }
345
346         static final Decoder RFC4648         = new Decoder(false, false);
347         static final Decoder RFC4648_URLSAFE = new Decoder(true, false);
348         static final Decoder RFC2045         = new Decoder(false, true);
349
350         /**
351          * Decodes all bytes from the input byte array using the {@link Base64}
352          * encoding scheme, writing the results into a newly-allocated output
353          * byte array. The returned byte array is of the length of the resulting
354          * bytes.
355          *
356          * @param   src
357          *          the byte array to decode
358          *
359          * @return  A newly-allocated byte array containing the decoded bytes.
360          *
361          * @throws  IllegalArgumentException
362          *          if {@code src} is not in valid Base64 scheme
363          */
364         public byte[] decode(byte[] src) {
365             byte[] dst = new byte[outLength(src, 0, src.length)];
366             int ret = decode0(src, 0, src.length, dst);
367             if (ret != dst.length) {
368                 dst = Arrays.copyOf(dst, ret);
369             }
370             return dst;
371         }
372
373         public byte[] decode(String src) {
374             return decode(src.getBytes(Charset.forName("ISO-8859-1")));
375         }
376
377         /**
378          * Decodes all bytes from the input byte array using the {@link Base64}
379          * encoding scheme, writing the results into the given output byte array,
380          * starting at offset 0.
381          *
382          * <p> It is the responsibility of the invoker of this method to make
383          * sure the output byte array {@code dst} has enough space for decoding
384          * all bytes from the input byte array. No bytes will be be written to
385          * the output byte array if the output byte array is not big enough.
386          *
387          * <p> If the input byte array is not in valid Base64 encoding scheme
388          * then some bytes may have been written to the output byte array before
389          * IllegalargumentException is thrown.
390          *
391          * @param   src
392          *          the byte array to decode
393          * @param   dst
394          *          the output byte array
395          *
396          * @return  The number of bytes written to the output byte array
397          *
398          * @throws  IllegalArgumentException
399          *          if {@code src} is not in valid Base64 scheme, or {@code dst}
400          *          does not have enough space for decoding all input bytes.
401          */
402         public int decode(byte[] src, byte[] dst) {
403             int len = outLength(src, 0, src.length);
404             if (dst.length < len)
405                 throw new IllegalArgumentException(
406                         "Output byte array is too small for decoding all input bytes");
407             return decode0(src, 0, src.length, dst);
408         }
409
410         /**
411          * Decodes all bytes from the input byte buffer using the {@link Base64}
412          * encoding scheme, writing the results into a newly-allocated ByteBuffer.
413          *
414          * <p> Upon return, the source buffer's position will be updated to
415          * its limit; its limit will not have been changed. The returned
416          * output buffer's position will be zero and its limit will be the
417          * number of resulting decoded bytes
418          *
419          * <p> {@code IllegalArgumentException} is thrown if the input buffer
420          * is not in valid Base64 encoding scheme. The position of the input
421          * buffer will not be advanced in this case.
422          *
423          * @param   buffer
424          *          the ByteBuffer to decode
425          *
426          * @return  A newly-allocated byte buffer containing the decoded bytes
427          *
428          * @throws  IllegalArgumentException
429          *          if {@code src} is not in valid Base64 scheme.
430          */
431         public ByteBuffer decode(ByteBuffer buffer) {
432             int pos0 = buffer.position();
433             try {
434                 byte[] src;
435                 int sp, sl;
436                 if (buffer.hasArray()) {
437                     src = buffer.array();
438                     sp = buffer.arrayOffset() + buffer.position();
439                     sl = buffer.arrayOffset() + buffer.limit();
440                     buffer.position(buffer.limit());
441                 } else {
442                     src = new byte[buffer.remaining()];
443                     buffer.get(src);
444                     sp = 0;
445                     sl = src.length;
446                 }
447                 byte[] dst = new byte[outLength(src, sp, sl)];
448                 return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst));
449             } catch (IllegalArgumentException iae) {
450                 buffer.position(pos0);
451                 throw iae;
452             }
453         }
454
455         private int outLength(byte[] src, int sp, int sl) {
456             int[] base64 = isURL ? fromBase64URL : fromBase64;
457             int paddings = 0;
458             int len = sl - sp;
459             if (len == 0)
460                 return 0;
461             if (len < 2) {
462                 if (isMIME && base64[0] == -1)
463                     return 0;
464                 throw new IllegalArgumentException(
465                         "Input byte[] should at least have 2 bytes for base64 bytes");
466             }
467             if (isMIME) {
468                 // scan all bytes to fill out all non-alphabet. a performance
469                 // trade-off of pre-scan or Arrays.copyOf
470                 int n = 0;
471                 while (sp < sl) {
472                     int b = src[sp++] & 0xff;
473                     if (b == '=') {
474                         len -= (sl - sp + 1);
475                         break;
476                     }
477                     if ((b = base64[b]) == -1)
478                         n++;
479                 }
480                 len -= n;
481             } else {
482                 if (src[sl - 1] == '=') {
483                     paddings++;
484                     if (src[sl - 2] == '=')
485                         paddings++;
486                 }
487             }
488             if (paddings == 0 && (len & 0x3) !=  0)
489                 paddings = 4 - (len & 0x3);
490             return 3 * ((len + 3) / 4) - paddings;
491         }
492
493         private int decode0(byte[] src, int sp, int sl, byte[] dst) {
494             int[] base64 = isURL ? fromBase64URL : fromBase64;
495             int dp = 0;
496             int bits = 0;
497             int shiftto = 18;       // pos of first byte of 4-byte atom
498             while (sp < sl) {
499                 int b = src[sp++] & 0xff;
500                 if ((b = base64[b]) < 0) {
501                     if (b == -2) {         // padding byte '='
502                         // =     shiftto==18 unnecessary padding
503                         // x=    shiftto==12 a dangling single x
504                         // x     to be handled together with non-padding case
505                         // xx=   shiftto==6&&sp==sl missing last =
506                         // xx=y  shiftto==6 last is not =
507                         if (shiftto == 6 && (sp == sl || src[sp++] != '=') ||
508                                 shiftto == 18) {
509                             throw new IllegalArgumentException(
510                                     "Input byte array has wrong 4-byte ending unit");
511                         }
512                         break;
513                     }
514                     if (isMIME)    // skip if for rfc2045
515                         continue;
516                     else
517                         throw new IllegalArgumentException(
518                                 "Illegal base64 character " +
519                                         Integer.toString(src[sp - 1], 16));
520                 }
521                 bits |= (b << shiftto);
522                 shiftto -= 6;
523                 if (shiftto < 0) {
524                     dst[dp++] = (byte)(bits >> 16);
525                     dst[dp++] = (byte)(bits >>  8);
526                     dst[dp++] = (byte)(bits);
527                     shiftto = 18;
528                     bits = 0;
529                 }
530             }
531             // reached end of byte array or hit padding '=' characters.
532             if (shiftto == 6) {
533                 dst[dp++] = (byte)(bits >> 16);
534             } else if (shiftto == 0) {
535                 dst[dp++] = (byte)(bits >> 16);
536                 dst[dp++] = (byte)(bits >>  8);
537             } else if (shiftto == 12) {
538                 // dangling single "x", incorrectly encoded.
539                 throw new IllegalArgumentException(
540                         "Last unit does not have enough valid bits");
541             }
542             // anything left is invalid, if is not MIME.
543             // if MIME, ignore all non-base64 character
544             while (sp < sl) {
545                 if (isMIME && base64[src[sp++]] < 0)
546                     continue;
547                 throw new IllegalArgumentException(
548                         "Input byte array has incorrect ending byte at " + sp);
549             }
550             return dp;
551         }
552     }
553 }