All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.freeutils.util.Base64 Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright © 2003-2024 Amichai Rothman
 *
 *  This file is part of JElementary - the Java Elementary Utilities package.
 *
 *  JElementary is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  JElementary 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 General Public License
 *  along with JElementary.  If not, see .
 *
 *  For additional info see https://www.freeutils.net/source/jelementary/
 */

package net.freeutils.util;

import java.io.UnsupportedEncodingException;

/**
 * The {@code Base64} class contains RFC 4648 compliant base64 and base64url
 * encoding and decoding methods.
 */
public class Base64 {

    protected static final byte[] ENCODE, DECODE, ENCODE_URL, DECODE_URL;
    static {
        byte[][] tables = createLookupTables("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
        ENCODE = tables[0];
        DECODE = tables[1];
        tables = createLookupTables("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
        ENCODE_URL = tables[0];
        DECODE_URL = tables[1];
    }

    /**
     * Creates an encode/decode lookup table pair using the given encoding characters.
     *
     * @param table a string containing the ordered characters used as the encoding lookup table
     * @return an array containing the two lookup arrays, for encoding and decoding (respectively)
     */
    public static byte[][] createLookupTables(String table) {
        byte[] enc = new byte[table.length()];
        byte[] dec = new byte[256];
        for (int i = 0; i < dec.length; i++)
            dec[i] = -1;
        for (int i = 0; i < enc.length; i++) {
            enc[i] = (byte)table.charAt(i);
            dec[enc[i]] = (byte)i;
        }
        return new byte[][] { enc, dec };
    }

    /**
     * Encodes the given bytes using the given encoding lookup table.
     *
     * @param b the bytes to encode
     * @param lookup the encoding lookup table
     * @return the encoded bytes
     */
    public static byte[] encode(byte[] b, byte[] lookup) {
        int full = b.length / 3 * 3;
        int rem = b.length - full;
        byte[] out = new byte[(b.length + 2) / 3 * 4];
        int i = 0;
        int j = 0;
        // encode full byte triplets
        while (i < full) {
            int t = (b[i++] & 0xFF) << 16 | (b[i++] & 0xFF) << 8 | (b[i++] & 0xFF);
            out[j++] = lookup[t >> 18];
            out[j++] = lookup[(t >> 12) & 0x3F];
            out[j++] = lookup[(t >> 6) & 0x3F];
            out[j++] = lookup[t & 0x3F];
        }
        // encode remainder (0, 1 or 2 last bytes)
        if (rem > 0) {
            int t = (b[i++] & 0xFF) << 16 | (rem == 1 ? 0 : (b[i] & 0xFF) << 8);
            out[j++] = lookup[t >> 18];
            out[j++] = lookup[(t >> 12) & 0x3F];
            out[j++] = rem == 1 ? (byte)'=' : lookup[(t >> 6) & 0x3F];
            out[j] = '=';
        }
        return out;
    }

    /**
     * Decodes the given bytes using the given decoding lookup table.
     *
     * @param b the bytes to decode
     * @param lookup the decoding lookup table
     * @return the decoded bytes
     */
    public static byte[] decode(byte[] b, byte[] lookup) {
        int pad = 0;
        for (int i = b.length - 1; i >= 0 && b[i] == '='; i--)
            pad++;
        int full = (b.length - pad) / 4 * 4;
        byte[] out = new byte[b.length / 4 * 3 - pad];
        int i = 0;
        int j = 0;
        // decode full byte quads
        while (i < full) {
            int t = lookup[b[i++]] << 18 | lookup[b[i++]] << 12 | lookup[b[i++]] << 6 | lookup[b[i++]];
            out[j++] = (byte)(t >> 16);
            out[j++] = (byte)(t >> 8);
            out[j++] = (byte)t;
        }
        // decode remainder (0, 1 or 2 last bytes)
        if (pad > 0) {
            int t = lookup[b[i++]] << 18 | lookup[b[i++]] << 12 | (pad == 1 ? lookup[b[i]] << 6 : 0);
            out[j++] = (byte)(t >> 16);
            if (pad == 1)
                out[j] = (byte)(t >> 8);
        }
        return out;
    }

    /**
     * Checks whether the given bytes are validly encoded.
     *
     * @param b the bytes to check
     * @param lookup the decoding lookup table
     * @return true if the bytes are validly encoded, false otherwise
     */
    public static boolean isValid(byte[] b, byte[] lookup) {
        int len = b.length;
        if (len % 4 != 0)
            return false;
        for (int i = 0; i < len; i++)
            if (lookup[b[i]] < 0 && (i < len - 2 || b[i] != '=' || b[len - 1] != '='))
                return false;
        return true;
    }

    /**
     * Encodes the given bytes using standard base64 encoding.
     *
     * @param b the bytes to encode
     * @return the encoded bytes
     */
    public static byte[] encode(byte[] b) {
        return encode(b, ENCODE);
    }

    /**
     * Encodes the given bytes using standard base64url encoding.
     *
     * @param b the bytes to encode
     * @return the encoded bytes
     */
    public static byte[] encodeUrl(byte[] b) {
        return encode(b, ENCODE_URL);
    }

    /**
     * Decodes the given bytes using standard base64 decoding.
     * The input it assumed to be valid, as this method does not
     * validate that it is indeed legal base64-encoded data.
     *
     * @param b the bytes to decode
     * @return the decoded bytes
     */
    public static byte[] decode(byte[] b) {
        return decode(b, DECODE);
    }

    /**
     * Decodes the given bytes using standard base64url decoding.
     * The input it assumed to be valid, as this method does not
     * validate that it is indeed legal base64-encoded data.
     *
     * @param b the bytes to decode
     * @return the decoded bytes
     */
    public static byte[] decodeUrl(byte[] b) {
        return decode(b, DECODE_URL);
    }

    /**
     * Checks whether the given bytes are validly encoded in standard base64 encoding.
     *
     * @param b the bytes to check
     * @return true if the bytes are validly encoded, false otherwise
     */
    public static boolean isValid(byte[] b) {
        return isValid(b, DECODE);
    }

    /**
     * Checks whether the given bytes are validly encoded in standard base64url encoding.
     *
     * @param b the bytes to check
     * @return true if the bytes are validly encoded, false otherwise
     */
    public static boolean isValidUrl(byte[] b) {
        return isValid(b, DECODE_URL);
    }

    /**
     * Decodes the given string using standard base64 decoding.
     *
     * @param s the string to decode
     * @param validate if true, the bytes are validated to be legal base64 bytes
     * @return the decoded bytes
     * @throws IllegalArgumentException if the given string contains non US-ASCII characters,
     *         or if validate is true and the given bytes are not valid base64 bytes
     */
    public static byte[] decode(String s, boolean validate) {
        return decode(Strings.getASCIIBytes(s), validate);
    }

    /**
     * Decodes the given string using standard base64url decoding.
     *
     * @param s the string to decode
     * @param validate if true, the bytes are validated to be legal base64url bytes
     * @return the decoded bytes
     * @throws IllegalArgumentException if the given string contains non US-ASCII characters,
     *         or if validate is true and the given bytes are not valid base64url bytes
     */
    public static byte[] decodeUrl(String s, boolean validate) {
        return decodeUrl(Strings.getASCIIBytes(s), validate);
    }

    /**
     * Decodes the given bytes using standard base64 decoding.
     *
     * @param b the bytes to decode
     * @param validate if true, the bytes are validated to be legal base64 bytes
     * @return the decoded bytes
     * @throws IllegalArgumentException if validate is true and the given
     *         bytes are not valid base64 bytes
     */
    public static byte[] decode(byte[] b, boolean validate) {
        if (validate && !isValid(b))
            throw new IllegalArgumentException("invalid base64 bytes");
        return decode(b);
    }

    /**
     * Decodes the given bytes using standard base64url decoding.
     *
     * @param b the bytes to decode
     * @param validate if true, the bytes are validated to be legal base64url bytes
     * @return the decoded bytes
     * @throws IllegalArgumentException if validate is true and the given
     *         bytes are not valid base64url bytes
     */
    public static byte[] decodeUrl(byte[] b, boolean validate) {
        if (validate && !isValidUrl(b))
            throw new IllegalArgumentException("invalid base64url bytes");
        return decodeUrl(b);
    }

    /**
     * Encodes the given bytes using standard base64 encoding.
     *
     * @param b the bytes to encode
     * @return the encoded bytes as an ASCII hex string
     */
    public static String encodeString(byte[] b) {
        try {
            return new String(encode(b, ENCODE), "US-ASCII");
        } catch (UnsupportedEncodingException uee) {
            return null; // can't happen
        }
    }

    /**
     * Encodes the given bytes using standard base64url encoding.
     *
     * @param b the bytes to encode
     * @return the encoded bytes as an ASCII hex string
     */
    public static String encodeStringUrl(byte[] b) {
        try {
            return new String(encode(b, ENCODE_URL), "US-ASCII");
        } catch (UnsupportedEncodingException uee) {
            return null; // can't happen
        }
    }

    /**
     * Encodes the given string as UTF-8 bytes using standard base64 encoding.
     *
     * @param s the string to encode
     * @return the encoded bytes as an ASCII hex string
     */
    public static String encodeUTFString(String s) {
        try {
            return new String(encode(s.getBytes("UTF-8"), ENCODE), "US-ASCII");
        } catch (UnsupportedEncodingException uee) {
            return null; // can't happen
        }
    }

    /**
     * Encodes the given string as UTF-8 bytes using standard base64url encoding.
     *
     * @param s the string to encode
     * @return the encoded bytes as an ASCII hex string
     */
    public static String encodeUTFStringUrl(String s) {
        try {
            return new String(encode(s.getBytes("UTF-8"), ENCODE_URL), "US-ASCII");
        } catch (UnsupportedEncodingException uee) {
            return null; // can't happen
        }
    }

    /**
     * Decodes the given string using standard base64 decoding.
     *
     * @param s the string to decode
     * @param validate if true, the bytes are validated to be legal base64 bytes
     * @param charset the charset to interpret the decoded bytes in
     * @return the decoded bytes as string in the given charset
     * @throws IllegalArgumentException if the given string contains non US-ASCII characters,
     *         or if validate is true and the given bytes are not valid base64 bytes,
     *         or if the decoded bytes are invalid in the given charset,
     *         or if the given charset does not exist
     */
    public static String decodeString(String s, boolean validate, String charset) {
        try {
            return new String(decode(Strings.getASCIIBytes(s), validate), charset);
        } catch (UnsupportedEncodingException uee) {
            throw new IllegalArgumentException(uee);
        }
    }

    /**
     * Decodes the given string using standard base64url decoding.
     *
     * @param s the string to decode
     * @param validate if true, the bytes are validated to be legal base64url bytes
     * @param charset the charset to interpret the decoded bytes in
     * @return the decoded bytes as string in the given charset
     * @throws IllegalArgumentException if the given string contains non US-ASCII characters,
     *         or if validate is true and the given bytes are not valid base64url bytes,
     *         or if the decoded bytes are invalid in the given charset,
     *         or if the given charset does not exist
     */
    public static String decodeStringUrl(String s, boolean validate, String charset) {
        try {
            return new String(decodeUrl(Strings.getASCIIBytes(s), validate), charset);
        } catch (UnsupportedEncodingException uee) {
            throw new IllegalArgumentException(uee);
        }
    }

    /**
     * Decodes the given string using standard base64 decoding.
     *
     * @param s the string to decode
     * @param validate if true, the bytes are validated to be legal base64 bytes
     * @return the decoded bytes as string in the given charset
     * @throws IllegalArgumentException if the given string contains non US-ASCII characters,
     *         or if validate is true and the given bytes are not valid base64 bytes,
     *         or if the decoded bytes are invalid in the UTF-8 charset
     */
    public static String decodeUTFString(String s, boolean validate) {
        return decodeString(s, validate, "UTF-8");
    }

    /**
     * Decodes the given string using standard base64url decoding.
     *
     * @param s the string to decode
     * @param validate if true, the bytes are validated to be legal base64url bytes
     * @return the decoded bytes as string in the given charset
     * @throws IllegalArgumentException if the given string contains non US-ASCII characters,
     *         or if validate is true and the given bytes are not valid base64url bytes,
     *         or if the decoded bytes are invalid in the UTF-8 charset
     */
    public static String decodeUTFStringUrl(String s, boolean validate) {
        return decodeStringUrl(s, validate, "UTF-8");
    }

    /**
     * Decodes the given string using standard base64 decoding.
     *
     * @param s the string to decode
     * @param validate if true, the bytes are validated to be legal base64 bytes
     * @return the decoded bytes as string in the ISO-8869-1 charset
     * @throws IllegalArgumentException if the given string contains non US-ASCII characters,
     *         or if validate is true and the given bytes are not valid base64 bytes
     */
    public static String decodeString(String s, boolean validate) {
        return decodeString(s, validate, "ISO8859_1");
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy