// // ======================================================================== // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; /** Fast B64 Encoder/Decoder as described in RFC 1421. * <p>Does not insert or interpret whitespace as described in RFC * 1521. If you require this you must pre/post process your data. * <p> Note that in a web context the usual case is to not want * linebreaks or other white space in the encoded output. * */ public class B64Code { private static final char __pad='='; private static final char[] __rfc1421alphabet= { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' }; private static final byte[] __rfc1421nibbles; static { __rfc1421nibbles=new byte[256]; for (int i=0;i<256;i++) __rfc1421nibbles[i]=-1; for (byte b=0;b<64;b++) __rfc1421nibbles[(byte)__rfc1421alphabet[b]]=b; __rfc1421nibbles[(byte)__pad]=0; } private static final char[] __rfc4648urlAlphabet= { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','-','_' }; private static final byte[] __rfc4648urlNibbles; static { __rfc4648urlNibbles=new byte[256]; for (int i=0;i<256;i++) __rfc4648urlNibbles[i]=-1; for (byte b=0;b<64;b++) __rfc4648urlNibbles[(byte)__rfc4648urlAlphabet[b]]=b; __rfc4648urlNibbles[(byte)__pad]=0; } private B64Code() { } /** * Base 64 encode as described in RFC 1421. * <p>Does not insert whitespace as described in RFC 1521. * @param s String to encode. * @return String containing the encoded form of the input. */ public static String encode(String s) { return encode(s, (Charset)null); } /** * Base 64 encode as described in RFC 1421. * <p>Does not insert whitespace as described in RFC 1521. * @param s String to encode. * @param charEncoding String representing the name of * the character encoding of the provided input String. * @return String containing the encoded form of the input. */ public static String encode(String s,String charEncoding) { byte[] bytes; if (charEncoding==null) bytes=s.getBytes(StandardCharsets.ISO_8859_1); else bytes=s.getBytes(Charset.forName(charEncoding)); return new String(encode(bytes)); } /** * Base 64 encode as described in RFC 1421. * <p>Does not insert whitespace as described in RFC 1521. * @param s String to encode. * @param charEncoding The character encoding of the provided input String. * @return String containing the encoded form of the input. */ public static String encode(String s, Charset charEncoding) { byte[] bytes=s.getBytes(charEncoding==null ? StandardCharsets.ISO_8859_1 : charEncoding); return new String(encode(bytes)); } /** * Fast Base 64 encode as described in RFC 1421. * <p>Does not insert whitespace as described in RFC 1521. * <p> Avoids creating extra copies of the input/output. * @param b byte array to encode. * @return char array containing the encoded form of the input. */ public static char[] encode(byte[] b) { if (b==null) return null; int bLen=b.length; int cLen=((bLen+2)/3)*4; char c[]=new char[cLen]; int ci=0; int bi=0; byte b0, b1, b2; int stop=(bLen/3)*3; while (bi<stop) { b0=b[bi++]; b1=b[bi++]; b2=b[bi++]; c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f]; c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f]; c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03]; c[ci++]=__rfc1421alphabet[b2&0x3f]; } if (bLen!=bi) { switch (bLen%3) { case 2: b0=b[bi++]; b1=b[bi++]; c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f]; c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f]; c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f]; c[ci++]=__pad; break; case 1: b0=b[bi++]; c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f]; c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f]; c[ci++]=__pad; c[ci++]=__pad; break; default: break; } } return c; } /** * Fast Base 64 encode as described in RFC 1421 and RFC2045 * <p>Does not insert whitespace as described in RFC 1521, unless rfc2045 is passed as true. * <p> Avoids creating extra copies of the input/output. * @param b byte array to encode. * @param rfc2045 If true, break lines at 76 characters with CRLF * @return char array containing the encoded form of the input. */ public static char[] encode(byte[] b, boolean rfc2045) { if (b==null) return null; if (!rfc2045) return encode(b); int bLen=b.length; int cLen=((bLen+2)/3)*4; cLen+=2+2*(cLen/76); char c[]=new char[cLen]; int ci=0; int bi=0; byte b0, b1, b2; int stop=(bLen/3)*3; int l=0; while (bi<stop) { b0=b[bi++]; b1=b[bi++]; b2=b[bi++]; c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f]; c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f]; c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03]; c[ci++]=__rfc1421alphabet[b2&0x3f]; l+=4; if (l%76==0) { c[ci++]=13; c[ci++]=10; } } if (bLen!=bi) { switch (bLen%3) { case 2: b0=b[bi++]; b1=b[bi++]; c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f]; c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f]; c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f]; c[ci++]=__pad; break; case 1: b0=b[bi++]; c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f]; c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f]; c[ci++]=__pad; c[ci++]=__pad; break; default: break; } } c[ci++]=13; c[ci++]=10; return c; } /** * Base 64 decode as described in RFC 2045. * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored. * @param encoded String to decode. * @param charEncoding String representing the character encoding * used to map the decoded bytes into a String. * @return String decoded byte array. * @throws UnsupportedCharsetException if the encoding is not supported * @throws IllegalArgumentException if the input is not a valid * B64 encoding. */ public static String decode(String encoded,String charEncoding) { byte[] decoded=decode(encoded); if (charEncoding==null) return new String(decoded); return new String(decoded,Charset.forName(charEncoding)); } /** * Base 64 decode as described in RFC 2045. * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored. * @param encoded String to decode. * @param charEncoding Character encoding * used to map the decoded bytes into a String. * @return String decoded byte array. * @throws IllegalArgumentException if the input is not a valid * B64 encoding. */ public static String decode(String encoded, Charset charEncoding) { byte[] decoded=decode(encoded); if (charEncoding==null) return new String(decoded); return new String(decoded, charEncoding); } /** * Fast Base 64 decode as described in RFC 1421. * * <p>Unlike other decode methods, this does not attempt to * cope with extra whitespace as described in RFC 1521/2045. * <p> Avoids creating extra copies of the input/output. * <p> Note this code has been flattened for performance. * @param b char array to decode. * @return byte array containing the decoded form of the input. * @throws IllegalArgumentException if the input is not a valid * B64 encoding. */ public static byte[] decode(char[] b) { if (b==null) return null; int bLen=b.length; if (bLen%4!=0) throw new IllegalArgumentException("Input block size is not 4"); int li=bLen-1; while (li>=0 && b[li]==(byte)__pad) li--; if (li<0) return new byte[0]; // Create result array of exact required size. int rLen=((li+1)*3)/4; byte r[]=new byte[rLen]; int ri=0; int bi=0; int stop=(rLen/3)*3; byte b0,b1,b2,b3; try { while (ri<stop) { b0=__rfc1421nibbles[b[bi++]]; b1=__rfc1421nibbles[b[bi++]]; b2=__rfc1421nibbles[b[bi++]]; b3=__rfc1421nibbles[b[bi++]]; if (b0<0 || b1<0 || b2<0 || b3<0) throw new IllegalArgumentException("Not B64 encoded"); r[ri++]=(byte)(b0<<2|b1>>>4); r[ri++]=(byte)(b1<<4|b2>>>2); r[ri++]=(byte)(b2<<6|b3); } if (rLen!=ri) { switch (rLen%3) { case 2: b0=__rfc1421nibbles[b[bi++]]; b1=__rfc1421nibbles[b[bi++]]; b2=__rfc1421nibbles[b[bi++]]; if (b0<0 || b1<0 || b2<0) throw new IllegalArgumentException("Not B64 encoded"); r[ri++]=(byte)(b0<<2|b1>>>4); r[ri++]=(byte)(b1<<4|b2>>>2); break; case 1: b0=__rfc1421nibbles[b[bi++]]; b1=__rfc1421nibbles[b[bi++]]; if (b0<0 || b1<0) throw new IllegalArgumentException("Not B64 encoded"); r[ri++]=(byte)(b0<<2|b1>>>4); break; default: break; } } } catch (IndexOutOfBoundsException e) { throw new IllegalArgumentException("char "+bi +" was not B64 encoded"); } return r; } /** * Base 64 decode as described in RFC 2045. * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored. * @param encoded String to decode. * @return byte array containing the decoded form of the input. * @throws IllegalArgumentException if the input is not a valid * B64 encoding. */ public static byte[] decode(String encoded) { if (encoded==null) return null; ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3); decode(encoded, bout); return bout.toByteArray(); } /* ------------------------------------------------------------ */ /** * Base 64 decode as described in RFC 2045. * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored. * @param encoded String to decode. * @param bout stream for decoded bytes * @throws IllegalArgumentException if the input is not a valid * B64 encoding. */ static public void decode (String encoded, ByteArrayOutputStream bout) { if (encoded==null) return; if (bout == null) throw new IllegalArgumentException("No outputstream for decoded bytes"); int ci=0; byte nibbles[] = new byte[4]; int s=0; while (ci<encoded.length()) { char c=encoded.charAt(ci++); if (c==__pad) break; if (Character.isWhitespace(c)) continue; byte nibble=__rfc1421nibbles[c]; if (nibble<0) throw new IllegalArgumentException("Not B64 encoded"); nibbles[s++]=__rfc1421nibbles[c]; switch(s) { case 1: break; case 2: bout.write(nibbles[0]<<2|nibbles[1]>>>4); break; case 3: bout.write(nibbles[1]<<4|nibbles[2]>>>2); break; case 4: bout.write(nibbles[2]<<6|nibbles[3]); s=0; break; } } return; } /* ------------------------------------------------------------ */ public static byte[] decodeRFC4648URL(String encoded) { if (encoded==null) return null; ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3); decodeRFC4648URL(encoded, bout); return bout.toByteArray(); } /* ------------------------------------------------------------ */ /** * Base 64 decode as described in RFC 4648 URL. * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored. * @param encoded String to decode. * @param bout stream for decoded bytes * @throws IllegalArgumentException if the input is not a valid * B64 encoding. */ static public void decodeRFC4648URL (String encoded, ByteArrayOutputStream bout) { if (encoded==null) return; if (bout == null) throw new IllegalArgumentException("No outputstream for decoded bytes"); int ci=0; byte nibbles[] = new byte[4]; int s=0; while (ci<encoded.length()) { char c=encoded.charAt(ci++); if (c==__pad) break; if (Character.isWhitespace(c)) continue; byte nibble=__rfc4648urlNibbles[c]; if (nibble<0) throw new IllegalArgumentException("Not B64 encoded"); nibbles[s++]=__rfc4648urlNibbles[c]; switch(s) { case 1: break; case 2: bout.write(nibbles[0]<<2|nibbles[1]>>>4); break; case 3: bout.write(nibbles[1]<<4|nibbles[2]>>>2); break; case 4: bout.write(nibbles[2]<<6|nibbles[3]); s=0; break; } } return; } public static void encode(int value,Appendable buf) throws IOException { buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]); buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]); buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]); buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]); buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]); buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4)]); buf.append('='); } public static void encode(long lvalue,Appendable buf) throws IOException { int value=(int)(0xFFFFFFFC&(lvalue>>32)); buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]); buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]); buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]); buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]); buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]); buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4) + (0xf&(int)(lvalue>>28))]); value=0x0FFFFFFF&(int)lvalue; buf.append(__rfc1421alphabet[0x3f&((0x0FC00000&value)>>22)]); buf.append(__rfc1421alphabet[0x3f&((0x003F0000&value)>>16)]); buf.append(__rfc1421alphabet[0x3f&((0x0000FC00&value)>>10)]); buf.append(__rfc1421alphabet[0x3f&((0x000003F0&value)>>4)]); buf.append(__rfc1421alphabet[0x3f&((0x0000000F&value)<<2)]); } }