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

com.microsoft.azure.sdk.iot.deps.util.Base64 Maven / Gradle / Ivy

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package com.microsoft.azure.sdk.iot.deps.util;

public final class Base64
{
    private static final byte BYTE_START_UPPERCASE = 'A';
    private static final byte BYTE_END_UPPERCASE = 'Z';
    private static final byte BYTE_START_LOWERCASE = 'a';
    private static final byte BYTE_END_LOWERCASE = 'z';
    private static final byte BYTE_START_NUMBER = '0';
    private static final byte BYTE_END_NUMBER = '9';
    private static final byte BYTE_PLUS = '+';
    private static final byte BYTE_SLASH = '/';

    private static final int BASE64_END_UPPERCASE = 26;
    private static final int BASE64_END_LOWERCASE = 52;
    private static final int BASE64_END_NUMBER = 62;
    private static final int BASE64_PLUS = 62;
    private static final int BASE64_SLASH = 63;
    private static final byte BASE64_PAD = '=';

    private static final int HALF_NIBBLE = 2;
    private static final int ONE_NIBBLE = 4;
    private static final int ONE_AND_HALF_NIBBLE = 6;
    private static final int ONE_BYTE = 8;
    private static final int TWO_BYTES = 16;
    private static final int THREE_BYTES = 24;

    private static final int ISOLATE_BYTE = 0xFF;
    private static final int ISOLATE_BASE64 = 0x3F;
    private static final int ISOLATE_LSB_BASE64 = 0x0F;
    private static final int ISOLATE_MSB_BASE64 = 0x03;

    private static final int BYTE_GROUP_SIZE = 3;
    private static final int BASE64_GROUP_SIZE = 4;


    private static final int[] BASE64D16_CONVERSION_TABLE =
        {
            ((int)'A' + ((int)'E'<> (bytePosition << 3)) & ISOLATE_BYTE);
    }

    private static byte base64ToByte(final byte base64Value)
    {
        if(base64Value < BASE64_END_UPPERCASE)
        {
            return (byte)(BYTE_START_UPPERCASE + base64Value);
        }

        if(base64Value < BASE64_END_LOWERCASE)
        {
            return (byte)(BYTE_START_LOWERCASE + (base64Value - BASE64_END_UPPERCASE));
        }

        if(base64Value < BASE64_END_NUMBER)
        {
            return (byte)(BYTE_START_NUMBER + (base64Value - BASE64_END_LOWERCASE));
        }

        if(base64Value == BASE64_END_NUMBER)
        {
            return BYTE_PLUS;
        }

        return BYTE_SLASH;
    }

    private static byte base64d16ToByte(final byte base64d16Value)
    {
        return extractBase64FromInteger(BASE64D16_CONVERSION_TABLE[base64d16Value >> HALF_NIBBLE],
            (base64d16Value & ISOLATE_MSB_BASE64));
    }

    private static byte base64d8ToByte(final byte base64d8Value)
    {
        return extractBase64FromInteger(BASE64D8_CONVERSION_TABLE, base64d8Value);
    }

    private static byte byteToBase64(final byte byteValue) throws IllegalArgumentException
    {
        if((byteValue >= BYTE_START_UPPERCASE) && (byteValue <= BYTE_END_UPPERCASE))
        {
            return (byte)(byteValue - BYTE_START_UPPERCASE);
        }

        if((byteValue >= BYTE_START_LOWERCASE) && (byteValue <= BYTE_END_LOWERCASE))
        {
            return (byte)((BYTE_END_UPPERCASE - BYTE_START_UPPERCASE) + 1 +
                (byteValue - BYTE_START_LOWERCASE));
        }

        if((byteValue >= BYTE_START_NUMBER) && (byteValue <= BYTE_END_NUMBER))
        {
            return (byte)((BYTE_END_UPPERCASE - BYTE_START_UPPERCASE) + 1 +
                (BYTE_END_LOWERCASE - BYTE_START_LOWERCASE) + 1 +
                (byteValue - BYTE_START_NUMBER));
        }

        if(byteValue == BYTE_PLUS)
        {
            return BASE64_PLUS;
        }

        if(byteValue == BYTE_SLASH)
        {
            return BASE64_SLASH;
        }

        throw new IllegalArgumentException("provided byte value out of base64 range");
    }

    private static int numberOfValidBase64BytesWithoutPad(final byte[] bytesToEncode) throws IllegalArgumentException
    {
        int validLength = bytesToEncode.length;

        if(bytesToEncode[validLength-1] == BASE64_PAD)
        {
            validLength--;
        }

        if(bytesToEncode[validLength-1] == BASE64_PAD)
        {
            validLength--;
        }

        return validLength;
    }

    private static int base64EstimatedLength(final byte[] base64sToDecode)
    {
        int estimatedLength;

        if(base64sToDecode.length == 0)
        {
            return 0;
        }

        estimatedLength = base64sToDecode.length / BASE64_GROUP_SIZE * BYTE_GROUP_SIZE;
        if(base64sToDecode[base64sToDecode.length - 1] == BASE64_PAD)
        {
            if(base64sToDecode[base64sToDecode.length - 2] == BASE64_PAD)
            {
                estimatedLength --;
            }
            estimatedLength --;
        }

        return estimatedLength;
    }

    /**
     * Convert a array of base64 encoded byte in a array of bytes, returning the bytes
     * original values.
     * RFC 2045.
     *
     * Base64 only uses 6 bits, so fits each set of 4 base64 in 3 bytes
     *     Base64  |     c1    |     c2    |     c3    |     c4    |
     *             |7 6 5 4 3 2 1 0:7 6 5 4 3 2 1 0:7 6 5 4 3 2 1 0|
     *     Byte    |       b1      |       b2      |       b3      |
     *
     * @param base64Values is an array of base64 encoded values
     * @return an array of bytes with the original values
     * @throws IllegalArgumentException if the provided base64 values are null, or do not fits the required length
     */
    public static byte[] decodeBase64Local(final byte[] base64Values) throws IllegalArgumentException
    {
        /* Codes_SRS_BASE64_21_002: [If the `base64Values` is null, the decodeBase64Local shall throw IllegalArgumentException.] */
        if(base64Values == null)
        {
            throw new IllegalArgumentException("null or empty base64Values");
        }

        /* Codes_SRS_BASE64_21_003: [If the `base64Values` is empty, the decodeBase64Local shall return a empty byte array.] */
        if(base64Values.length == 0)
        {
            return new byte[0];
        }

        /* Codes_SRS_BASE64_21_004: [If the `base64Values` length is not multiple of 4, the decodeBase64Local shall throw IllegalArgumentException.] */
        if((base64Values.length % BASE64_GROUP_SIZE) != 0)
        {
            throw new IllegalArgumentException("invalid base64Values length");
        }

        /* Codes_SRS_BASE64_21_001: [The decodeBase64Local shall decode the provided `base64Values` in a byte array using the Base64 format define in the RFC2045.] */
        int numberOfEncodedBytes = numberOfValidBase64BytesWithoutPad(base64Values);
        int indexOfFirstEncodedByte = 0;
        int decodedIndex = 0;
        byte[] decodedResult = new byte[base64EstimatedLength(base64Values)];

        while(numberOfEncodedBytes >= BASE64_GROUP_SIZE)
        {
            byte c1 = byteToBase64(base64Values[indexOfFirstEncodedByte++]);
            byte c2 = byteToBase64(base64Values[indexOfFirstEncodedByte++]);
            byte c3 = byteToBase64(base64Values[indexOfFirstEncodedByte++]);
            byte c4 = byteToBase64(base64Values[indexOfFirstEncodedByte++]);
            decodedResult[decodedIndex++] = (byte)((c1 << HALF_NIBBLE) | (c2 >> ONE_NIBBLE));
            decodedResult[decodedIndex++] = (byte)((c2 << ONE_NIBBLE) | (c3 >> HALF_NIBBLE));
            decodedResult[decodedIndex++] = (byte)((c3 << ONE_AND_HALF_NIBBLE) | c4);
            numberOfEncodedBytes -= BASE64_GROUP_SIZE;
        }

        if(numberOfEncodedBytes == 3)
        {
            byte c1 = byteToBase64(base64Values[indexOfFirstEncodedByte++]);
            byte c2 = byteToBase64(base64Values[indexOfFirstEncodedByte++]);
            byte c3 = byteToBase64(base64Values[indexOfFirstEncodedByte]);
            decodedResult[decodedIndex++] = (byte)((c1 << HALF_NIBBLE) | (c2 >> ONE_NIBBLE));
            decodedResult[decodedIndex] = (byte)((c2 << ONE_NIBBLE) | (c3 >> HALF_NIBBLE));
        }

        if(numberOfEncodedBytes == 2)
        {
            byte c1 = byteToBase64(base64Values[indexOfFirstEncodedByte++]);
            byte c2 = byteToBase64(base64Values[indexOfFirstEncodedByte]);
            decodedResult[decodedIndex] = (byte)((c1 << HALF_NIBBLE) | (c2 >> ONE_NIBBLE));
        }

        return decodedResult;
    }

    /**
     * Convert a array of bytes in a array of MIME   Base64 values.
     * RFC 2045.
     *
     * @param dataValues is an array of bytes with the original values
     * @return an array of base64 encoded values
     * @throws IllegalArgumentException if the provided base64 values are null, or do not fits the required length
     */
    public static byte[] encodeBase64Local(byte[] dataValues) throws IllegalArgumentException
    {
        /* Codes_SRS_BASE64_21_006: [If the `dataValues` is null, the encodeBase64Local shall throw IllegalArgumentException.] */
        if(dataValues == null)
        {
            throw new IllegalArgumentException("null or empty dataValues");
        }

        /* Codes_SRS_BASE64_21_007: [If the `dataValues` is empty, the encodeBase64Local shall return a empty byte array.] */
        if(dataValues.length == 0)
        {
            return new byte[0];
        }

        /* Codes_SRS_BASE64_21_005: [The encodeBase64Local shall encoded the provided `dataValues` in a byte array using the Base64 format define in the RFC2045.] */
        return encodeBase64Internal(dataValues);
    }

    /**
     * Convert a array of bytes in a array of MIME Base64 values.
     * RFC 2045.
     *
     * @param dataValues is an array of bytes with the original values
     * @return a string with the base64 encoded values
     * @throws IllegalArgumentException if the provided base64 values are null, or do not fits the required length
     */
    public static String encodeBase64StringLocal(byte[] dataValues) throws IllegalArgumentException
    {
        /* Codes_SRS_BASE64_21_009: [If the `dataValues` is null, the encodeBase64StringLocal shall throw IllegalArgumentException.] */
        if(dataValues == null)
        {
            throw new IllegalArgumentException("null or empty dataValues");
        }

        /* Codes_SRS_BASE64_21_010: [If the `dataValues` is empty, the encodeBase64StringLocal shall return a empty string.] */
        if(dataValues.length == 0)
        {
            return new String();
        }

        /* Codes_SRS_BASE64_21_008: [The encodeBase64StringLocal shall encoded the provided `dataValues` in a string using the Base64 format define in the RFC2045.] */
        return new String(encodeBase64Internal(dataValues));
    }

    private static byte[] encodeBase64Internal(byte[] dataValues) throws IllegalArgumentException
    {
        int encodedLength = (((dataValues.length - 1) / BYTE_GROUP_SIZE) + 1) * BASE64_GROUP_SIZE;
        int destinationPosition = 0;
        int currentPosition = 0;

        byte[] encodedResult = new byte[encodedLength];

        while((dataValues.length - currentPosition) >= BYTE_GROUP_SIZE)
        {
            encodedResult[destinationPosition++] = base64ToByte((byte)((dataValues[currentPosition] >> HALF_NIBBLE) & ISOLATE_BASE64));
            encodedResult[destinationPosition++] = base64ToByte((byte)(((dataValues[currentPosition] << ONE_NIBBLE) & ISOLATE_BASE64) |
                ((dataValues[currentPosition + 1] >> ONE_NIBBLE) & ISOLATE_LSB_BASE64)));
            encodedResult[destinationPosition++] = base64ToByte((byte)(((dataValues[currentPosition + 1] << HALF_NIBBLE) & ISOLATE_BASE64) |
                ((dataValues[currentPosition + 2] >> ONE_AND_HALF_NIBBLE) & ISOLATE_MSB_BASE64)));
            encodedResult[destinationPosition++] = base64ToByte((byte)(dataValues[currentPosition + 2] & ISOLATE_BASE64));
            currentPosition += BYTE_GROUP_SIZE;
        }

        if((dataValues.length - currentPosition) == 2)
        {
            encodedResult[destinationPosition++] = base64ToByte((byte)((dataValues[currentPosition] >> HALF_NIBBLE) & ISOLATE_BASE64));
            encodedResult[destinationPosition++] = base64ToByte((byte)(((dataValues[currentPosition] << ONE_NIBBLE) & ISOLATE_BASE64) |
                ((dataValues[currentPosition + 1] >> ONE_NIBBLE) & ISOLATE_LSB_BASE64)));
            encodedResult[destinationPosition++] = base64d16ToByte((byte)(dataValues[currentPosition + 1] & ISOLATE_LSB_BASE64));
            encodedResult[destinationPosition] = BASE64_PAD;
        }

        if((dataValues.length - currentPosition) == 1)
        {
            encodedResult[destinationPosition++] = base64ToByte((byte)((dataValues[currentPosition] >> HALF_NIBBLE) & ISOLATE_BASE64));
            encodedResult[destinationPosition++] = base64d8ToByte((byte)(dataValues[currentPosition] & ISOLATE_MSB_BASE64));
            encodedResult[destinationPosition++] = BASE64_PAD;
            encodedResult[destinationPosition] = BASE64_PAD;
        }

        return encodedResult;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy