org.eclipse.jetty.util.B64Code Maven / Gradle / Ivy
//
// ========================================================================
// Copyright (c) 1995-2018 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.
* Does not insert or interpret whitespace as described in RFC
* 1521. If you require this you must pre/post process your data.
*
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.
*
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.
*
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.
*
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.
*
Does not insert whitespace as described in RFC 1521.
*
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>>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
* Does not insert whitespace as described in RFC 1521, unless rfc2045 is passed as true.
*
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>>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.
* 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. If null
* the platforms default charset is used.
* @return String decoded byte array.
* @throws UnsupportedCharsetException if the encoding is not supported
* @throws IllegalArgumentException if the input is not a valid
* B64 encoding.
*/
@SuppressWarnings("DefaultCharset")
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.
*
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. If null
* the platforms default charset is used.
* @return String decoded byte array.
* @throws IllegalArgumentException if the input is not a valid
* B64 encoding.
*/
@SuppressWarnings("DefaultCharset")
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.
*
*
Unlike other decode methods, this does not attempt to
* cope with extra whitespace as described in RFC 1521/2045.
*
Avoids creating extra copies of the input/output.
*
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>>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.
* 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.
*
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>>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.
* 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>>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)]);
}
}