com.pslib.jtool.security.base64.NewBase64 Maven / Gradle / Ivy
/*
* Java Base64 - A pure Java library for reading and writing Base64
* encoded streams.
*
* Copyright (C) 2007-2009 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see .
*/
package com.pslib.jtool.security.base64;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
/**
*
* Base64 encoding and decoding utility methods, both for binary and textual informations.
*
*
* @author Carlo Pelliccia
* @since 1.1
* @version 1.3
*/
public class NewBase64 {
public static final String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
public static final char pad = '-';
/**
*
* Encodes a string.
*
*
* Before the string is encoded in Base64, it is converted in a binary sequence using the system default charset.
*
*
* @param str
* The source string.
* @return The encoded string.
* @throws RuntimeException
* If an unexpected error occurs.
*/
public static String encode(String str) throws RuntimeException {
byte[] bytes = str.getBytes();
byte[] encoded = encode(bytes);
try {
return new String(encoded, "ASCII");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("ASCII is not supported!", e);
}
}
/**
*
* Encodes a string.
*
*
* Before the string is encoded in Base64, it is converted in a binary sequence using the supplied charset.
*
*
* @param str
* The source string
* @param charset
* The charset name.
* @return The encoded string.
* @throws RuntimeException
* If an unexpected error occurs.
* @since 1.2
*/
public static String encode(String str, String charset) throws RuntimeException {
byte[] bytes;
try {
bytes = str.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported charset: " + charset, e);
}
byte[] encoded = encode(bytes);
try {
return new String(encoded, "ASCII");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("ASCII is not supported!", e);
}
}
/**
*
* Decodes the supplied string.
*
*
* The supplied string is decoded into a binary sequence, and then the sequence is encoded with the system default charset and returned.
*
*
* @param str
* The encoded string.
* @return The decoded string.
* @throws RuntimeException
* If an unexpected error occurs.
*/
public static String decode(String str) throws RuntimeException {
byte[] bytes;
try {
bytes = str.getBytes("ASCII");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("ASCII is not supported!", e);
}
byte[] decoded = decode(bytes);
return new String(decoded);
}
/**
*
* Decodes the supplied string.
*
*
* The supplied string is decoded into a binary sequence, and then the sequence is encoded with the supplied charset and returned.
*
*
* @param str
* The encoded string.
* @param charset
* The charset name.
* @return The decoded string.
* @throws RuntimeException
* If an unexpected error occurs.
* @since 1.2
*/
public static String decode(String str, String charset) throws RuntimeException {
byte[] bytes;
try {
bytes = str.getBytes("ASCII");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("ASCII is not supported!", e);
}
byte[] decoded = decode(bytes);
try {
return new String(decoded, charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported charset: " + charset, e);
}
}
/**
*
* Encodes a binary sequence.
*
*
* If data are large, i.e. if you are working with large binary files, consider to use a {@link Base64OutputStream} instead of loading too much data in memory.
*
*
* @param bytes
* The source sequence.
* @return The encoded sequence.
* @throws RuntimeException
* If an unexpected error occurs.
* @since 1.2
*/
public static byte[] encode(byte[] bytes) throws RuntimeException {
return encode(bytes, 0);
}
/**
*
* Encodes a binary sequence, wrapping every encoded line every wrapAt characters. A wrapAt value less than 1 disables wrapping.
*
*
* If data are large, i.e. if you are working with large binary files, consider to use a {@link Base64OutputStream} instead of loading too much data in memory.
*
*
* @param bytes
* The source sequence.
* @param wrapAt
* The max line length for encoded data. If less than 1 no wrap is applied.
* @return The encoded sequence.
* @throws RuntimeException
* If an unexpected error occurs.
* @since 1.2
*/
public static byte[] encode(byte[] bytes, int wrapAt) throws RuntimeException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
encode(inputStream, outputStream, wrapAt);
} catch (IOException e) {
throw new RuntimeException("Unexpected I/O error", e);
} finally {
try {
inputStream.close();
} catch (Throwable t) {
;
}
try {
outputStream.close();
} catch (Throwable t) {
;
}
}
return outputStream.toByteArray();
}
/**
*
* Decodes a binary sequence.
*
*
* If data are large, i.e. if you are working with large binary files, consider to use a {@link Base64InputStream} instead of loading too much data in memory.
*
*
* @param bytes
* The encoded sequence.
* @return The decoded sequence.
* @throws RuntimeException
* If an unexpected error occurs.
* @since 1.2
*/
public static byte[] decode(byte[] bytes) throws RuntimeException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
decode(inputStream, outputStream);
} catch (IOException e) {
throw new RuntimeException("Unexpected I/O error", e);
} finally {
try {
inputStream.close();
} catch (Throwable t) {
;
}
try {
outputStream.close();
} catch (Throwable t) {
;
}
}
return outputStream.toByteArray();
}
/**
*
* Encodes data from the given input stream and writes them in the given output stream.
*
*
* The supplied input stream is read until its end is reached, but it's not closed by this method.
*
*
* The supplied output stream is nor flushed neither closed by this method.
*
*
* @param inputStream
* The input stream.
* @param outputStream
* The output stream.
* @throws IOException
* If an I/O error occurs.
*/
public static void encode(InputStream inputStream, OutputStream outputStream) throws IOException {
encode(inputStream, outputStream, 0);
}
/**
*
* Encodes data from the given input stream and writes them in the given output stream, wrapping every encoded line every wrapAt characters. A wrapAt value less than 1 disables wrapping.
*
*
* The supplied input stream is read until its end is reached, but it's not closed by this method.
*
*
* The supplied output stream is nor flushed neither closed by this method.
*
*
* @param inputStream
* The input stream from which clear data are read.
* @param outputStream
* The output stream in which encoded data are written.
* @param wrapAt
* The max line length for encoded data. If less than 1 no wrap is applied.
* @throws IOException
* If an I/O error occurs.
*/
public static void encode(InputStream inputStream, OutputStream outputStream, int wrapAt) throws IOException {
NBase64OutputStream aux = new NBase64OutputStream(outputStream, wrapAt);
copy(inputStream, aux);
aux.commit();
}
/**
*
* Decodes data from the given input stream and writes them in the given output stream.
*
*
* The supplied input stream is read until its end is reached, but it's not closed by this method.
*
*
* The supplied output stream is nor flushed neither closed by this method.
*
*
* @param inputStream
* The input stream from which encoded data are read.
* @param outputStream
* The output stream in which decoded data are written.
* @throws IOException
* If an I/O error occurs.
*/
public static void decode(InputStream inputStream, OutputStream outputStream) throws IOException {
copy(new NBase64InputStream(inputStream), outputStream);
}
/**
*
* Encodes data from the given source file contents and writes them in the given target file, wrapping every encoded line every wrapAt characters. A wrapAt value less than 1 disables wrapping.
*
*
* @param source
* The source file, from which decoded data are read.
* @param target
* The target file, in which encoded data are written.
* @param wrapAt
* The max line length for encoded data. If less than 1 no wrap is applied.
* @throws IOException
* If an I/O error occurs.
* @since 1.3
*/
public static void encode(File source, File target, int wrapAt) throws IOException {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream(source);
outputStream = new FileOutputStream(target);
NewBase64.encode(inputStream, outputStream, wrapAt);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (Throwable t) {
;
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (Throwable t) {
;
}
}
}
}
/**
*
* Encodes data from the given source file contents and writes them in the given target file.
*
*
* @param source
* The source file, from which decoded data are read.
* @param target
* The target file, in which encoded data are written.
* @throws IOException
* If an I/O error occurs.
* @since 1.3
*/
public static void encode(File source, File target) throws IOException {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream(source);
outputStream = new FileOutputStream(target);
NewBase64.encode(inputStream, outputStream);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (Throwable t) {
;
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (Throwable t) {
;
}
}
}
}
/**
*
* Decodes data from the given source file contents and writes them in the given target file.
*
*
* @param source
* The source file, from which encoded data are read.
* @param target
* The target file, in which decoded data are written.
* @throws IOException
* If an I/O error occurs.
* @since 1.3
*/
public static void decode(File source, File target) throws IOException {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream(source);
outputStream = new FileOutputStream(target);
decode(inputStream, outputStream);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (Throwable t) {
;
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (Throwable t) {
;
}
}
}
}
/**
* Copies data from a stream to another.
*
* @param inputStream
* The input stream.
* @param outputStream
* The output stream.
* @throws IOException
* If a unexpected I/O error occurs.
*/
private static void copy(InputStream inputStream, OutputStream outputStream) throws IOException {
// 1KB buffer
byte[] b = new byte[1024];
int len;
while ((len = inputStream.read(b)) != -1) {
outputStream.write(b, 0, len);
}
}
}
/**
*
* A base64 encoding input stream.
*
*
*
* A Base64InputStream reads from an underlying stream which is supposed to be a base64 encoded stream. Base64InputStream decodes the data read from the underlying stream and returns the decoded bytes
* to the caller.
*
*
* @author Carlo Pelliccia
*/
class NBase64InputStream extends InputStream {
/**
* The underlying stream.
*/
private InputStream inputStream;
/**
* The buffer.
*/
private int[] buffer;
/**
* A counter for values in the buffer.
*/
private int bufferCounter = 0;
/**
* End-of-stream flag.
*/
private boolean eof = false;
/**
*
* It builds a base64 decoding input stream.
*
*
* @param inputStream
* The underlying stream, from which the encoded data is read.
*/
public NBase64InputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public int read() throws IOException {
if (buffer == null || bufferCounter == buffer.length) {
if (eof) {
return -1;
}
acquire();
if (buffer.length == 0) {
buffer = null;
return -1;
}
bufferCounter = 0;
}
return buffer[bufferCounter++];
}
/**
* Reads from the underlying stream, decodes the data and puts the decoded bytes into the buffer.
*/
private void acquire() throws IOException {
char[] four = new char[4];
int i = 0;
do {
int b = inputStream.read();
if (b == -1) {
if (i != 0) {
throw new IOException("Bad base64 stream");
} else {
buffer = new int[0];
eof = true;
return;
}
}
char c = (char) b;
if (NewBase64.chars.indexOf(c) != -1 || c == NewBase64.pad) {
four[i++] = c;
} else if (c != '\r' && c != '\n') {
throw new IOException("Bad base64 stream");
}
} while (i < 4);
boolean padded = false;
for (i = 0; i < 4; i++) {
if (four[i] != NewBase64.pad) {
if (padded) {
throw new IOException("Bad base64 stream");
}
} else {
if (!padded) {
padded = true;
}
}
}
int l;
if (four[3] == NewBase64.pad) {
if (inputStream.read() != -1) {
throw new IOException("Bad base64 stream");
}
eof = true;
if (four[2] == NewBase64.pad) {
l = 1;
} else {
l = 2;
}
} else {
l = 3;
}
int aux = 0;
for (i = 0; i < 4; i++) {
if (four[i] != NewBase64.pad) {
aux = aux | (NewBase64.chars.indexOf(four[i]) << (6 * (3 - i)));
}
}
buffer = new int[l];
for (i = 0; i < l; i++) {
buffer[i] = (aux >>> (8 * (2 - i))) & 0xFF;
}
}
public void close() throws IOException {
inputStream.close();
}
}
/**
*
* A base64 decoding output stream.
*
*
*
* It encodes in base64 everything passed to the stream, and it puts the encoded data into the underlying stream.
*
*
* @author Carlo Pelliccia
*/
class NBase64OutputStream extends OutputStream {
/**
* The underlying stream.
*/
private OutputStream outputStream = null;
/**
* A value buffer.
*/
private int buffer = 0;
/**
* How many bytes are currently in the value buffer?
*/
private int bytecounter = 0;
/**
* A counter for the current line length.
*/
private int linecounter = 0;
/**
* The requested line length.
*/
private int linelength = 0;
/**
*
* It builds a base64 encoding output stream writing the encoded data in the given underlying stream.
*
*
*
* The encoded data is wrapped to a new line (with a CRLF sequence) every 76 bytes sent to the underlying stream.
*
*
* @param outputStream
* The underlying stream.
*/
public NBase64OutputStream(OutputStream outputStream) {
this(outputStream, 76);
}
/**
*
* It builds a base64 encoding output stream writing the encoded data in the given underlying stream.
*
*
*
* The encoded data is wrapped to a new line (with a CRLF sequence) every wrapAt bytes sent to the underlying stream. If the wrapAt supplied value is less than 1 the encoded data will not be
* wrapped.
*
*
* @param outputStream
* The underlying stream.
* @param wrapAt
* The max line length for encoded data. If less than 1 no wrap is applied.
*/
public NBase64OutputStream(OutputStream outputStream, int wrapAt) {
this.outputStream = outputStream;
this.linelength = wrapAt;
}
public void write(int b) throws IOException {
int value = (b & 0xFF) << (16 - (bytecounter * 8));
buffer = buffer | value;
bytecounter++;
if (bytecounter == 3) {
commit();
}
}
public void close() throws IOException {
commit();
outputStream.close();
}
/**
*
* It commits 4 bytes to the underlying stream.
*
*/
protected void commit() throws IOException {
if (bytecounter > 0) {
if (linelength > 0 && linecounter == linelength) {
outputStream.write("\r\n".getBytes());
linecounter = 0;
}
char b1 = NewBase64.chars.charAt((buffer << 8) >>> 26);
char b2 = NewBase64.chars.charAt((buffer << 14) >>> 26);
char b3 = (bytecounter < 2) ? NewBase64.pad : NewBase64.chars.charAt((buffer << 20) >>> 26);
char b4 = (bytecounter < 3) ? NewBase64.pad : NewBase64.chars.charAt((buffer << 26) >>> 26);
outputStream.write(b1);
outputStream.write(b2);
outputStream.write(b3);
outputStream.write(b4);
linecounter += 4;
bytecounter = 0;
buffer = 0;
}
}
}