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

com.nimbusds.jose.util.Base64Codec Maven / Gradle / Ivy

Go to download

Java library for Javascript Object Signing and Encryption (JOSE) and JSON Web Tokens (JWT)

There is a newer version: 9.48
Show newest version
/*
 * nimbus-jose-jwt
 *
 * Copyright 2012-2016, Connect2id Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the
 * License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.nimbusds.jose.util;


import java.util.Arrays;


final class Base64Codec {


	/**
	 * The base 64 characters.
	 */
	private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();


	/**
	 * The base 64 URL-safe characters.
	 */
	private static final char[] CA_URL_SAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray();


	/**
	 * Maps base 64 characters to their respective byte values.
	 */
	private static final int[] IA = new int[256];


	/**
	 * Maps base 64 URL-safe characters to their respective byte values.
	 */
	private static final int[] IA_URL_SAFE = new int[256];


	static {
		// Regular map
		Arrays.fill(IA, -1);
		for (int i = 0, iS = CA.length; i < iS; i++) {
			IA[CA[i]] = i;
		}
		IA['='] = 0;

		// URL-safe map
		Arrays.fill(IA_URL_SAFE, -1);
		for (int i = 0, iS = CA_URL_SAFE.length; i < iS; i++) {
			IA_URL_SAFE[CA_URL_SAFE[i]] = i;
		}
		IA_URL_SAFE['='] = 0;
	}


	/**
	 * Computes the base 64 encoded character length for the specified
	 * input byte length.
	 *
	 * @param inputLength The input byte length.
	 * @param urlSafe     {@code true} for URL-safe encoding.
	 *
	 * @return The base 64 encoded character length.
	 */
	public static int computeEncodedLength(final int inputLength, final boolean urlSafe) {

		if (inputLength == 0) {
			return 0;
		}

		if (urlSafe) {

			// Compute the number of complete quads (4-char blocks)
			int fullQuadLength = (inputLength / 3) << 2;

			// Compute the remaining bytes at the end
			int remainder = inputLength % 3;

			// Compute the total
			return remainder == 0 ? fullQuadLength : fullQuadLength + remainder + 1;
		} else {
			// Original Mig code
			return ((inputLength - 1) / 3 + 1) << 2;
		}
	}


	/**
	 * Normalises a base 64 encoded string by ensuring any URL-safe
	 * characters are replaced with their regular base64 representation and
	 * any truncated '=' padding is restored.
	 *
	 * @param b64String The base 64 or base 64 URL-safe encoded string.
	 *                  Must not be {@code null}.
	 *
	 * @return The normalised base 64 encoded string.
	 */
	public static String normalizeEncodedString(final String b64String) {

		final int inputLen = b64String.length();

		// Compute missing padding, taking illegal chars into account
		final int legalLen = inputLen - countIllegalChars(b64String);
		final int padLength = legalLen % 4 == 0 ? 0 : 4 - (legalLen % 4);

		// Create output array
		char[] chars = new char[inputLen + padLength];

		// Copy chars into output array
		b64String.getChars(0, inputLen, chars, 0);

		// Append padding chars if required
		for (int i = 0; i < padLength; i++) {
			chars[inputLen + i] = '=';
		}

		// Replace URL-safe chars
		for (int i = 0; i < inputLen; i++) {
			if (chars[i] == '_') {
				chars[i] = '/';
			} else if (chars[i] == '-') {
				chars[i] = '+';
			}
		}

		return new String(chars);
	}


	/**
	 * Counts the illegal / separator characters in the specified base 64
	 * or base 64 URL-safe encoded string.
	 *
	 * @param b64String The base 64 or base 64 URL-safe encoded string.
	 *                  Must not be {@code null}.
	 *
	 * @return The illegal character count, zero if none.
	 */
	public static int countIllegalChars(final String b64String) {

		// Number of separator and illegal characters
		int illegalCharCount = 0;

		for (int i = 0; i < b64String.length(); i++) {

			final char c = b64String.charAt(i);

			if (IA[c] == -1 && IA_URL_SAFE[c] == -1) {
				illegalCharCount++;
			}
		}

		return illegalCharCount;
	}


	/**
	 * Encodes a byte array into a base 64 encoded character array.
	 *
	 * @param byteArray The bytes to convert. If {@code null} or length 0
	 *                  an empty array will be returned.
	 * @param urlSafe   If {@code true} to apply URL-safe encoding (padding
	 *                  still included and not to spec).
	 *
	 * @return The base 64 encoded character array. Never {@code null}.
	 */
	public static char[] encodeToChar(final byte[] byteArray, final boolean urlSafe) {

		// Check special case
		int sLen = byteArray != null ? byteArray.length : 0;

		if (sLen == 0) {
			return new char[0];
		}

		int eLen = (sLen / 3) * 3;                      // Length of even 24-bits.
		int dLen = computeEncodedLength(sLen, urlSafe); // Returned character count
		char[] out = new char[dLen];

		// Encode even 24-bits
		for (int s = 0, d = 0; s < eLen; ) {

			// Copy next three bytes into lower 24 bits of int, paying attention to sign
			int i = (byteArray[s++] & 0xff) << 16 | (byteArray[s++] & 0xff) << 8 | (byteArray[s++] & 0xff);

			// Encode the int into four chars
			if (urlSafe) {
				out[d++] = CA_URL_SAFE[(i >>> 18) & 0x3f];
				out[d++] = CA_URL_SAFE[(i >>> 12) & 0x3f];
				out[d++] = CA_URL_SAFE[(i >>> 6) & 0x3f];
				out[d++] = CA_URL_SAFE[i & 0x3f];
			} else {
				out[d++] = CA[(i >>> 18) & 0x3f];
				out[d++] = CA[(i >>> 12) & 0x3f];
				out[d++] = CA[(i >>> 6) & 0x3f];
				out[d++] = CA[i & 0x3f];
			}
		}

		// Pad and encode last bits if source isn't even 24 bits
		// according to URL-safe switch
		int left = sLen - eLen; // 0 - 2.
		if (left > 0) {
			// Prepare the int
			int i = ((byteArray[eLen] & 0xff) << 10) | (left == 2 ? ((byteArray[sLen - 1] & 0xff) << 2) : 0);

			// Set last four chars
			if (urlSafe) {

				if (left == 2) {
					out[dLen - 3] = CA_URL_SAFE[i >> 12];
					out[dLen - 2] = CA_URL_SAFE[(i >>> 6) & 0x3f];
					out[dLen - 1] = CA_URL_SAFE[i & 0x3f];
				} else {
					out[dLen - 2] = CA_URL_SAFE[i >> 12];
					out[dLen - 1] = CA_URL_SAFE[(i >>> 6) & 0x3f];
				}
			} else {
				// Original Mig code with padding
				out[dLen - 4] = CA[i >> 12];
				out[dLen - 3] = CA[(i >>> 6) & 0x3f];
				out[dLen - 2] = left == 2 ? CA[i & 0x3f] : '=';
				out[dLen - 1] = '=';
			}
		}

		return out;
	}


	/**
	 * Encodes a byte array into a base 64 encoded string.
	 *
	 * @param byteArray The bytes to convert. If {@code null} or length 0
	 *                  an empty array will be returned.
	 * @param urlSafe   If {@code true} to apply URL-safe encoding (padding
	 *                  still included and not to spec).
	 *
	 * @return The base 64 encoded string. Never {@code null}.
	 */
	public static String encodeToString(byte[] byteArray, final boolean urlSafe) {

		// Reuse char[] since we can't create a String incrementally
		// and StringBuffer/Builder would be slower
		return new String(encodeToChar(byteArray, urlSafe));
	}


	/**
	 * Decodes a base 64 or base 64 URL-safe encoded string. May contain
	 * line separators. Any illegal characters are ignored.
	 *
	 * @param b64String The base 64 or base 64 URL-safe encoded string. May
	 *                  be empty or {@code null}.
	 *
	 * @return The decoded byte array, empty if the input base 64 encoded
	 *         string is empty, {@code null} or corrupted.
	 */
	public static byte[] decode(final String b64String) {

		// Check special case
		if (b64String == null || b64String.isEmpty()) {
			return new byte[0];
		}

		final String nStr = normalizeEncodedString(b64String);

		final int sLen = nStr.length();

		// Count illegal characters (including '\r', '\n') to determine
		// the size of the byte array to return
		final int sepCnt = countIllegalChars(nStr);

		// Ensure the legal chars (including '=' padding) divide by 4 (RFC 2045)
		if ((sLen - sepCnt) % 4 != 0) {
			// The string is corrupted
			return new byte[0];
		}

		// Count '=' at end
		int pad = 0;

		for (int i = sLen; i > 1 && IA[nStr.charAt(--i)] <= 0; ) {
			if (nStr.charAt(i) == '=') {
				pad++;
			}
		}

		int len = ((sLen - sepCnt) * 6 >> 3) - pad;

		// Preallocate byte[] of final length
		byte[] dArr = new byte[len];

		for (int s = 0, d = 0; d < len; ) {
			// Assemble three bytes into an int from four base 64
			// characters
			int i = 0;

			for (int j = 0; j < 4; j++) {
				// j only increased if a valid char was found
				int c = IA[nStr.charAt(s++)];
				if (c >= 0) {
					i |= c << (18 - j * 6);
				} else {
					j--;
				}
			}
			// Add the bytes
			dArr[d++] = (byte) (i >> 16);
			if (d < len) {
				dArr[d++] = (byte) (i >> 8);
				if (d < len) {
					dArr[d++] = (byte) i;
				}
			}
		}

		return dArr;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy