src/share/classes/java/util/Base64.java

Print this page

        

@@ -62,11 +62,12 @@
  * <li><b>MIME</b>
  * <p> Uses the "The Base64 Alphabet" as specified in Table 1 of
  *     RFC 2045 for encoding and decoding operation. The encoded output
  *     must be represented in lines of no more than 76 characters each
  *     and uses a carriage return {@code '\r'} followed immediately by
- *     a linefeed {@code '\n'} as the line separator. All line separators
+ *     a linefeed {@code '\n'} as the line separator. No line separator
+ *     is added to the end of the encoded output. All line separators
  *     or other characters not found in the base64 alphabet table are
  *     ignored in decoding operation.</p></li>
  * </ul>
  *
  * <p> Unless otherwise noted, passing a {@code null} argument to a

@@ -612,10 +613,17 @@
 
     /**
      * This class implements a decoder for decoding byte data using the
      * Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
      *
+     * <p> The Base64 padding character {@code '='} is accepted and
+     * interpreted as the end of the encoded byte data, but is not
+     * required. So if the final unit of the encoded byte data only has
+     * two or three Base64 characters (without the corresponding padding
+     * character(s) padded), they are decoded as if followed by padding
+     * character(s).
+     *
      * <p> Instances of {@link Decoder} class are safe for use by
      * multiple concurrent threads.
      *
      * <p> Unless otherwise noted, passing a {@code null} argument to
      * a method of this class will cause a

@@ -855,10 +863,13 @@
         }
 
         /**
          * Returns an input stream for decoding {@link Base64} encoded byte stream.
          *
+         * <p> The {@code read}  methods of the returned {@code InputStream} will
+         * throw {@code IOException} when reading bytes that cannot be decoded.
+         *
          * <p> Closing the returned input stream will close the underlying
          * input stream.
          *
          * @param   is
          *          the input stream

@@ -881,17 +892,20 @@
             byte[] da = dst.array();
             int    dp = dst.arrayOffset() + dst.position();
             int    dl = dst.arrayOffset() + dst.limit();
             int    dp0 = dp;
             int    mark = sp;
-            boolean padding = false;
             try {
                 while (sp < sl) {
                     int b = sa[sp++] & 0xff;
                     if ((b = base64[b]) < 0) {
                         if (b == -2) {   // padding byte
-                            padding = true;
+                            if (shiftto == 6 && (sp == sl || sa[sp++] != '=') ||
+                                shiftto == 18) {
+                                throw new IllegalArgumentException(
+                                     "Input byte array has wrong 4-byte ending unit");
+                            }
                             break;
                         }
                         if (isMIME)     // skip if for rfc2045
                             continue;
                         else

@@ -913,28 +927,27 @@
                     }
                 }
                 if (shiftto == 6) {
                     if (dl - dp < 1)
                         return dp - dp0;
-                    if (padding && (sp + 1 != sl || sa[sp++] != '='))
-                        throw new IllegalArgumentException(
-                            "Input buffer has wrong 4-byte ending unit");
                     da[dp++] = (byte)(bits >> 16);
-                    mark = sp;
                 } else if (shiftto == 0) {
                     if (dl - dp < 2)
                         return dp - dp0;
-                    if (padding && sp != sl)
-                        throw new IllegalArgumentException(
-                            "Input buffer has wrong 4-byte ending unit");
                     da[dp++] = (byte)(bits >> 16);
                     da[dp++] = (byte)(bits >>  8);
-                    mark = sp;
-                } else if (padding || shiftto != 18) {
+                } else if (shiftto == 12) {
                     throw new IllegalArgumentException(
                         "Last unit does not have enough valid bits");
                 }
+                while (sp < sl) {
+                    if (isMIME && base64[sa[sp++]] < 0)
+                        continue;
+                    throw new IllegalArgumentException(
+                        "Input byte array has incorrect ending byte at " + sp);
+                }
+                mark = sp;
                 return dp - dp0;
             } finally {
                 src.position(mark);
                 dst.position(dp);
             }

@@ -948,18 +961,20 @@
             int    sl = src.limit();
             int    dp = dst.position();
             int    dl = dst.limit();
             int    dp0 = dp;
             int    mark = sp;
-            boolean padding = false;
-
             try {
                 while (sp < sl) {
                     int b = src.get(sp++) & 0xff;
                     if ((b = base64[b]) < 0) {
                         if (b == -2) {  // padding byte
-                            padding = true;
+                            if (shiftto == 6 && (sp == sl || src.get(sp++) != '=') ||
+                                shiftto == 18) {
+                                throw new IllegalArgumentException(
+                                     "Input byte array has wrong 4-byte ending unit");
+                            }
                             break;
                         }
                         if (isMIME)     // skip if for rfc2045
                             continue;
                         else

@@ -981,28 +996,27 @@
                     }
                 }
                 if (shiftto == 6) {
                     if (dl - dp < 1)
                         return dp - dp0;
-                    if (padding && (sp + 1 != sl || src.get(sp++) != '='))
-                        throw new IllegalArgumentException(
-                            "Input buffer has wrong 4-byte ending unit");
                      dst.put(dp++, (byte)(bits >> 16));
-                     mark = sp;
                 } else if (shiftto == 0) {
                     if (dl - dp < 2)
                         return dp - dp0;
-                    if (padding && sp != sl)
-                        throw new IllegalArgumentException(
-                            "Input buffer has wrong 4-byte ending unit");
                     dst.put(dp++, (byte)(bits >> 16));
                     dst.put(dp++, (byte)(bits >>  8));
-                    mark = sp;
-                } else if (padding || shiftto != 18) {
+                } else if (shiftto == 12) {
                     throw new IllegalArgumentException(
                         "Last unit does not have enough valid bits");
                 }
+                while (sp < sl) {
+                    if (isMIME && base64[src.get(sp++)] < 0)
+                        continue;
+                    throw new IllegalArgumentException(
+                        "Input byte array has incorrect ending byte at " + sp);
+                }
+                mark = sp;
                 return dp - dp0;
             } finally {
                 src.position(mark);
                 dst.position(dp);
             }

@@ -1046,16 +1060,24 @@
         private int decode0(byte[] src, int sp, int sl, byte[] dst) {
             int[] base64 = isURL ? fromBase64URL : fromBase64;
             int dp = 0;
             int bits = 0;
             int shiftto = 18;       // pos of first byte of 4-byte atom
-            boolean padding = false;
             while (sp < sl) {
                 int b = src[sp++] & 0xff;
                 if ((b = base64[b]) < 0) {
-                    if (b == -2) {     // padding byte
-                        padding = true;
+                    if (b == -2) {     // padding byte '='
+                        // xx=   shiftto==6&&sp==sl missing last =
+                        // xx=y  shiftto==6 last is not =
+                        // =     shiftto==18 unnecessary padding
+                        // x=    shiftto==12 be taken care later
+                        //       together with single x, invalid anyway
+                        if (shiftto == 6 && (sp == sl || src[sp++] != '=') ||
+                            shiftto == 18) {
+                            throw new IllegalArgumentException(
+                                "Input byte array has wrong 4-byte ending unit");
+                        }
                         break;
                     }
                     if (isMIME)    // skip if for rfc2045
                         continue;
                     else

@@ -1071,26 +1093,27 @@
                     dst[dp++] = (byte)(bits);
                     shiftto = 18;
                     bits = 0;
                 }
             }
-            // reach end of byte arry or hit padding '=' characters.
-            // if '=' presents, they must be the last one or two.
-            if (shiftto == 6) {           // xx==
-                if (padding && (sp + 1 != sl || src[sp] != '='))
-                    throw new IllegalArgumentException(
-                        "Input byte array has wrong 4-byte ending unit");
+            // reached end of byte array or hit padding '=' characters.
+            if (shiftto == 6) {
                 dst[dp++] = (byte)(bits >> 16);
-            } else if (shiftto == 0) {    // xxx=
-                if (padding && sp != sl)
-                    throw new IllegalArgumentException(
-                        "Input byte array has wrong 4-byte ending unit");
+            } else if (shiftto == 0) {
                 dst[dp++] = (byte)(bits >> 16);
                 dst[dp++] = (byte)(bits >>  8);
-            } else if (padding || shiftto != 18) {
+            } else if (shiftto == 12) {
+                throw new IllegalArgumentException(
+                    "Last unit does not have enough valid bits");
+            }
+            // anything left is invalid, if is not MIME.
+            // if MIME, ignore all non-base64 character
+            while (sp < sl) {
+                if (isMIME && base64[src[sp++]] < 0)
+                    continue;
                     throw new IllegalArgumentException(
-                        "last unit does not have enough bytes");
+                    "Input byte array has incorrect ending byte at " + sp);
             }
             return dp;
         }
     }
 

@@ -1250,12 +1273,26 @@
             }
             while (len > 0) {
                 int v = is.read();
                 if (v == -1) {
                     eof = true;
-                    if (nextin != 18)
-                        throw new IOException("Base64 stream has un-decoded dangling byte(s).");
+                    if (nextin != 18) {
+                        if (nextin == 12)
+                            throw new IOException("Base64 stream has one un-decoded dangling byte.");
+                        // treat ending xx/xxx without padding character legal.
+                        // same logic as v == 'v' below
+                        b[off++] = (byte)(bits >> (16));
+                        len--;
+                        if (nextin == 0) {           // only one padding byte
+                            if (len == 0) {          // no enough output space
+                                bits >>= 8;          // shift to lowest byte
+                                nextout = 0;
+                            } else {
+                                b[off++] = (byte) (bits >>  8);
+                            }
+                        }
+                    }
                     if (off == oldOff)
                         return -1;
                     else
                         return off - oldOff;
                 }